# Documentation RoadWave


---


## Table des matières


- [RoadWave](#roadwave)
- [RoadWave - Architecture Technique](#roadwave---architecture-technique)
- [ADR-001 : Langage Backend](#adr-001--langage-backend)
- [ADR-002 : Protocole de Streaming](#adr-002--protocole-de-streaming)
- [ADR-003 : Codec Audio](#adr-003--codec-audio)
- [ADR-004 : CDN](#adr-004--cdn)
- [ADR-005 : Base de Données](#adr-005--base-de-données)
- [ADR-006 : Chiffrement](#adr-006--chiffrement)
- [ADR-007 : Tests et Spécifications Exécutables](#adr-007--tests-et-spécifications-exécutables)
- [ADR-008 : Authentification et Gestion d'Identité](#adr-008--authentification-et-gestion-didentité)
- [ADR-009 : Solution de Paiement et Gestion des Abonnements](#adr-009--solution-de-paiement-et-gestion-des-abonnements)
- [ADR-010 : Commandes au volant et likes](#adr-010--commandes-au-volant-et-likes)
- [ADR-011 : Conformité App Stores et Plateformes Auto](#adr-011--conformité-app-stores-et-plateformes-auto)
- [ADR-012 : Architecture Backend](#adr-012--architecture-backend)
- [ADR-013 : ORM et Accès Données](#adr-013--orm-et-accès-données)
- [ADR-014 : Frontend Mobile](#adr-014--frontend-mobile)
- [ADR-015 : Stratégie Tests](#adr-015--stratégie-tests)
- [Règles métier RoadWave](#règles-métier-roadwave)
- [Annexe : Fonctionnalités reportées Post-MVP](#annexe--fonctionnalités-reportées-post-mvp)
- [Audio-guides multi-séquences pour piétons](#audio-guides-multi-séquences-pour-piétons)
- [Impact des abonnements sur l'algorithme](#impact-des-abonnements-sur-lalgorithme)
- [Limites d'abonnements et désabonnement](#limites-dabonnements-et-désabonnement)
- [Notifications contextuelles selon le mode de déplacement](#notifications-contextuelles-selon-le-mode-de-déplacement)
- [Création d'audio-guide multi-séquences](#création-daudio-guide-multi-séquences)
- [Intégration audio-guides avec autres fonctionnalités](#intégration-audio-guides-avec-autres-fonctionnalités)
- [Audio-guide mode piéton (navigation manuelle)](#audio-guide-mode-piéton-navigation-manuelle)
- [Audio-guide mode voiture (GPS automatique)](#audio-guide-mode-voiture-gps-automatique)
- [Audio-guides modes vélo et transport](#audio-guides-modes-vélo-et-transport)
- [Audio-guides Premium et monétisation](#audio-guides-premium-et-monétisation)
- [Sauvegarde et reprise de progression audio-guide](#sauvegarde-et-reprise-de-progression-audio-guide)
- [Classification des contenus par âge](#classification-des-contenus-par-âge)
- [Connexion utilisateur](#connexion-utilisateur)
- [Inscription utilisateur](#inscription-utilisateur)
- [Récupération de compte](#récupération-de-compte)
- [Gestion des sessions et tokens](#gestion-des-sessions-et-tokens)
- [Authentification à deux facteurs (2FA)](#authentification-à-deux-facteurs-2fa)
- [Vérification d'email](#vérification-demail)
- [Métadonnées et publication de contenu](#métadonnées-et-publication-de-contenu)
- [Modification et suppression de contenu](#modification-et-suppression-de-contenu)
- [Upload et encodage de contenu audio](#upload-et-encodage-de-contenu-audio)
- [Validation des 3 premiers contenus](#validation-des-3-premiers-contenus)
- [Élargissement automatique de zone quand aucun contenu n'est disponible](#élargissement-automatique-de-zone-quand-aucun-contenu-nest-disponible)
- [Gestion d'un contenu supprimé pendant l'écoute](#gestion-dun-contenu-supprimé-pendant-lécoute)
- [Mode dégradé sans géolocalisation](#mode-dégradé-sans-géolocalisation)
- [Gestion de la perte de réseau et buffering adaptatif](#gestion-de-la-perte-de-réseau-et-buffering-adaptatif)
- [Tests BDD - Documentation des fonctionnalités](#tests-bdd---documentation-des-fonctionnalités)
- [Pas de dégradation temporelle des jauges](#pas-de-dégradation-temporelle-des-jauges)
- [Évolution des jauges d'intérêt](#évolution-des-jauges-dintérêt)
- [Jauge initiale et cold start](#jauge-initiale-et-cold-start)
- [Synchronisation actions offline](#synchronisation-actions-offline)
- [Téléchargement de contenus offline](#téléchargement-de-contenus-offline)
- [Validité et renouvellement contenus offline](#validité-et-renouvellement-contenus-offline)
- [Modération préventive](#modération-préventive)
- [Sanctions et notifications de modération](#sanctions-et-notifications-de-modération)
- [Signalement de contenu inapproprié](#signalement-de-contenu-inapproprié)
- [Traitement des signalements par l'IA et les modérateurs](#traitement-des-signalements-par-lia-et-les-modérateurs)
- [Conditions d'activation de la monétisation](#conditions-dactivation-de-la-monétisation)
- [Contenus Premium exclusifs](#contenus-premium-exclusifs)
- [Désactivation et suspension monétisation](#désactivation-et-suspension-monétisation)
- [KYC et inscription à la monétisation](#kyc-et-inscription-à-la-monétisation)
- [Obligations fiscales](#obligations-fiscales)
- [Paiement des créateurs](#paiement-des-créateurs)
- [Sources de revenus créateurs](#sources-de-revenus-créateurs)
- [Actions complémentaires à l'arrêt](#actions-complémentaires-à-larrêt)
- [Commande "Précédent"](#commande-précédent)
- [Commandes vocales CarPlay et Android Auto](#commandes-vocales-carplay-et-android-auto)
- [Commandes au volant et interactions simplifiées](#commandes-au-volant-et-interactions-simplifiées)
- [File d'attente et commande "Suivant"](#file-dattente-et-commande-suivant)
- [Lecture en boucle et enchaînement automatique](#lecture-en-boucle-et-enchaînement-automatique)
- [Partage de contenu](#partage-de-contenu)
- [Avantages Premium](#avantages-premium)
- [Gestion abonnement Premium](#gestion-abonnement-premium)
- [Multi-devices et détection simultanée](#multi-devices-et-détection-simultanée)
- [Offre et tarification Premium](#offre-et-tarification-premium)
- [Profil créateur](#profil-créateur)
- [Création de campagnes publicitaires](#création-de-campagnes-publicitaires)
- [Caractéristiques et facturation des publicités](#caractéristiques-et-facturation-des-publicités)
- [Gestion du budget et alertes publicitaires](#gestion-du-budget-et-alertes-publicitaires)
- [Insertion et fréquence des publicités](#insertion-et-fréquence-des-publicités)
- [Métriques d'engagement et dashboard publicitaire](#métriques-dengagement-et-dashboard-publicitaire)
- [Validation et modération des publicités](#validation-et-modération-des-publicités)
- [Architecture technique radio live](#architecture-technique-radio-live)
- [Arrêt du live](#arrêt-du-live)
- [Comportement auditeur pendant un live](#comportement-auditeur-pendant-un-live)
- [Démarrage d'un live](#démarrage-dun-live)
- [Recherche de contenu](#recherche-de-contenu)
- [Classification de géo-pertinence des contenus](#classification-de-géo-pertinence-des-contenus)
- [Gestion du contenu politique (MVP simplifié)](#gestion-du-contenu-politique-mvp-simplifié)
- [Contenus géolocalisés en mode voiture](#contenus-géolocalisés-en-mode-voiture)
- [Gestion de l'historique et reproposition](#gestion-de-lhistorique-et-reproposition)
- [Médias traditionnels sur RoadWave](#médias-traditionnels-sur-roadwave)
- [Mode Kids pour utilisateurs 13-15 ans](#mode-kids-pour-utilisateurs-13-15-ans)
- [Paramétrabilité admin et A/B testing](#paramétrabilité-admin-et-ab-testing)
- [Paramétrabilité utilisateur et profils](#paramétrabilité-utilisateur-et-profils)
- [Formule de scoring et recommandation](#formule-de-scoring-et-recommandation)
- [Anonymisation des données GPS après 24h](#anonymisation-des-données-gps-après-24h)
- [Conformité administrative RGPD (Registre, Breach, DPO)](#conformité-administrative-rgpd-registre-breach-dpo)
- [Gestion du consentement RGPD](#gestion-du-consentement-rgpd)
- [Durée de conservation des données et purge automatique](#durée-de-conservation-des-données-et-purge-automatique)
- [Cookies et analytics avec Matomo self-hosted](#cookies-et-analytics-avec-matomo-self-hosted)
- [Mode dégradé avec GeoIP (sans GPS précis)](#mode-dégradé-avec-geoip-sans-gps-précis)
- [Portabilité des données (Article 20 RGPD)](#portabilité-des-données-article-20-rgpd)
- [Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)](#suppression-du-compte-utilisateur-article-17-rgpd---droit-à-leffacement)


---


<div style="page-break-after: always;"></div>


# 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](#docs/adr/010-commandes-volant) 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




<div style="page-break-after: always;"></div>


# RoadWave - Architecture Technique

> Les décisions techniques sont documentées dans [docs/adr/](#docs/adr/)

## Stack Technologique

| Composant | Technologie | ADR |
|-----------|-------------|-----|
| **Backend** | Go + Fiber | [ADR-001](#docs/adr/001-langage-backend) |
| **Architecture Backend** | Monolithe Modulaire | [ADR-012](#docs/adr/012-architecture-backend) |
| **Authentification** | Zitadel | [ADR-008](#docs/adr/008-authentification) |
| **Streaming** | HLS | [ADR-002](#docs/adr/002-protocole-streaming) |
| **Codec** | Opus | [ADR-003](#docs/adr/003-codec-audio) |
| **CDN** | Bunny CDN | [ADR-004](#docs/adr/004-cdn) |
| **Base de données** | PostgreSQL + PostGIS | [ADR-005](#docs/adr/005-base-de-donnees) |
| **ORM/Accès données** | sqlc | [ADR-013](#docs/adr/013-orm-acces-donnees) |
| **Cache** | Redis Cluster | [ADR-005](#docs/adr/005-base-de-donnees) |
| **Chiffrement** | TLS 1.3 | [ADR-006](#docs/adr/006-chiffrement) |
| **Live** | WebRTC | [ADR-002](#docs/adr/002-protocole-streaming) |
| **Frontend Mobile** | Flutter | [ADR-014](#docs/adr/014-frontend-mobile) |
| **Tests** | Testify + Godog (Gherkin) | [ADR-015](#docs/adr/015-strategie-tests), [ADR-007](#docs/adr/007-tests-bdd) |
| **Paiements** | Mangopay | [ADR-009](#docs/adr/009-solution-paiement) |
| **Commandes volant** | Like automatique | [ADR-010](#docs/adr/010-commandes-volant) |
| **Conformité stores** | CarPlay, Android Auto, App/Play Store | [ADR-011](#docs/adr/011-conformite-stores-carplay-android-auto) |

---

## 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

```sql
-- 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.




<div style="page-break-after: always;"></div>


# 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




<div style="page-break-after: always;"></div>


# 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




<div style="page-break-after: always;"></div>


# 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




<div style="page-break-after: always;"></div>


# 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




<div style="page-break-after: always;"></div>


# 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

```sql
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




<div style="page-break-after: always;"></div>


# 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




<div style="page-break-after: always;"></div>


# 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

```gherkin
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




<div style="page-break-after: always;"></div>


# 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

```go
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




<div style="page-break-after: always;"></div>


# 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

```go
// 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)




<div style="page-break-after: always;"></div>


# 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




<div style="page-break-after: always;"></div>


# 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](#../regles-metier/05-interactions-navigation.md#512-mode-piéton-audio-guides)

### 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](https://developer.android.com/training/cars/media)
- [CarPlay Developer Guide](https://developer.apple.com/carplay)
- [Google Play UGC Policy](https://support.google.com/googleplay/android-developer/answer/9876937)
- [App Store Guidelines](https://developer.apple.com/app-store/review/guidelines/)
- [Apple DMA Update EU](https://www.revenuecat.com/blog/growth/apple-eu-dma-update-june-2025/)
- [Google Background Location 2026](https://support.google.com/googleplay/android-developer/answer/9799150)




<div style="page-break-after: always;"></div>


# 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)




<div style="page-break-after: always;"></div>


# 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

```sql
-- 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;
```

```bash
sqlc generate
```

```go
// 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




<div style="page-break-after: always;"></div>


# 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

```yaml
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




<div style="page-break-after: always;"></div>


# 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)

```go
// 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](#007-tests-bdd) pour contexte complet.

```gherkin
# 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](#../../README) traduits en `.feature`.

## Tests intégration (Testcontainers)

```go
// 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)

```dart
// 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)

```javascript
// 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

```yaml
# .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




<div style="page-break-after: always;"></div>


# 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](#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](#02-algorithme-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](#03-centres-interet-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](#04-creation-publication-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](#05-interactions-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](#06-publicites)

**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](#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](#08-abonnements-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](#09-monetisation-createurs)

**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](#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](#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](#12-gestion-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](#13-conformite-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](#14-moderation-flows)

**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](#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](#16-audio-guides-multi-sequences)

**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




<div style="page-break-after: always;"></div>


## 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




<div style="page-break-after: always;"></div>


## 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](#ANNEXE-POST-MVP) 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




<div style="page-break-after: always;"></div>


## 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](#../adr/010-commandes-volant))
- **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




<div style="page-break-after: always;"></div>


## 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 :
   - Écoute 30 secondes (ou 15s à 2x)
   - Vérifie métadonnées
   - Valide ou rejette avec raison
4. Si accepté : contenu publié + notification créateur
5. Si refusé : notification avec raison détaillée + lien vers règles
6. 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 | Suppression BDD + CDN sous 5 min |
| **Réversibilité** | Non | Suppression définitive |
| **Historique auditeurs** | Marqué "Contenu supprimé par créateur" | Conserve écoute dans historique |
| **Analytics plateforme** | Anonymisé et conservé | Métriques globales (RGPD compliant) |
| **Fichiers CDN** | Supprimés sous 24h | Purge cache Bunny CDN |

**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




<div style="page-break-after: always;"></div>


## 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`) :
```xml
<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`) :
```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](#../adr/010-commandes-volant) 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) :

```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) :

```swift
// 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




<div style="page-break-after: always;"></div>


## 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




<div style="page-break-after: always;"></div>


## 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** :
   - Connexion ≥1 Mbps upload (warning si insuffisant)
   - Micro autorisé
   - Zone diffusion déjà définie (ville, département, région, national)
3. **Buffer initial 15 secondes** avant diffusion publique
   - Créateur parle pendant 15s → accumulation buffer serveur
   - Message créateur : "Live démarre dans 15s... Testez votre micro"
   - Permet vérifier qualité audio avant diffusion
4. 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é
   - Message audio : "Ce live se termine dans 5... 4... 3... 2... 1"
   - Permet au créateur de faire un outro propre
   - Annulable pendant décompte (bouton "Annuler")
3. Timer atteint 0 → arrêt diffusion
4. **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




<div style="page-break-after: always;"></div>


## 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** :
```json
{
  "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](#../adr/010-commandes-volant))
- 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




<div style="page-break-after: always;"></div>


## 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** :

```sql
-- 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




<div style="page-break-after: always;"></div>


## 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** :

```sql
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




<div style="page-break-after: always;"></div>


## 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 |

---




<div style="page-break-after: always;"></div>


## 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




<div style="page-break-after: always;"></div>


## 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** :
```sql
-- 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)




<div style="page-break-after: always;"></div>


## 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)




<div style="page-break-after: always;"></div>


## 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** :

```html
┌───────────────────────────────────────┐
│ 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)** :

```html
<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** :
   - Fade out audio (2 secondes)
   - 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]             │
└─────────────────────────────────┘
```

5. Utilisateur peut :
   - S'abonner Premium (redirection web Mangopay)
   - Télécharger l'app (redirection stores)
   - 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** :

```sql
-- 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 :

```sql
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** :

```sql
-- 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)




<div style="page-break-after: always;"></div>


## 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](#05-interactions-navigation.md#511-file-dattente-et-commande-suivant))
- 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** :

```javascript
// 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** :

```javascript
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




<div style="page-break-after: always;"></div>


# 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](##1-classification-politique-et-équilibre-éditorial)
2. [Système de pourboires créateurs](##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




<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant qu'auditeur
> <span style="color: #9E9E9E">**Et**</span> que je suis en mode piéton (vitesse <5 km/h)
## 1. Détection d'audio-guide à proximité

<span style="color: #2196F3">**Étant donné**</span> que je me trouve à 80 mètres du Musée du Louvre
<span style="color: #9E9E9E">**Et**</span> que 3 audio-guides sont disponibles pour ce lieu

<span style="color: #FF9800">**Quand**</span> le système détecte ma position

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push:

---

## 2. Rayon de détection de 100m

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide est centré aux coordonnées GPS du Louvre

<span style="color: #FF9800">**Quand**</span> je suis à exactement 100m du centre

<span style="color: #4CAF50">**Alors**</span> la notification est déclenchée
<span style="color: #9E9E9E">**Et**</span> quand je suis à 101m, aucune notification n'est envoyée

---

## 3. Page de sélection des audio-guides

<span style="color: #2196F3">**Étant donné**</span> que j'ai tapé sur la notification audio-guide

<span style="color: #FF9800">**Quand**</span> la page de sélection s'affiche

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis sur la page de sélection

<span style="color: #FF9800">**Quand**</span> je tape sur "Visite complète (45 min)"

<span style="color: #4CAF50">**Alors**</span> l'interface de lecture d'audio-guide s'ouvre
<span style="color: #9E9E9E">**Et**</span> la séquence 1 commence automatiquement
<span style="color: #9E9E9E">**Et**</span> je vois la liste complète des 12 séquences

---

## 5. Interface de lecture audio-guide

<span style="color: #2196F3">**Étant donné**</span> que j'ai sélectionné un audio-guide de 12 séquences

<span style="color: #FF9800">**Quand**</span> l'interface s'affiche

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la séquence 2

<span style="color: #FF9800">**Quand**</span> je tape sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> la séquence 3 commence immédiatement
<span style="color: #9E9E9E">**Et**</span> le titre de la séquence s'affiche: "Vénus de Milo"
<span style="color: #9E9E9E">**Et**</span> la barre de progression se réinitialise

---

## 7. Navigation vers séquence précédente

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la séquence 5

<span style="color: #FF9800">**Quand**</span> je tape sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> la séquence 4 recommence depuis le début
<span style="color: #9E9E9E">**Et**</span> je peux réécouter cette séquence

---

## 8. Saut direct à une séquence spécifique

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la séquence 2
<span style="color: #9E9E9E">**Et**</span> que la liste des séquences est affichée

<span style="color: #FF9800">**Quand**</span> je tape sur "7. Peintures Renaissance"

<span style="color: #4CAF50">**Alors**</span> la séquence 7 démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> je passe directement de la séquence 2 à la 7

---

## 9. Commande vocale "Suivant"

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la séquence 3

<span style="color: #FF9800">**Quand**</span> je dis "Suivant" via la commande vocale

<span style="color: #4CAF50">**Alors**</span> la séquence 4 démarre
<span style="color: #9E9E9E">**Et**</span> la commande vocale fonctionne même si l'écran est verrouillé

---

## 10. Commande vocale "Précédent"

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la séquence 6

<span style="color: #FF9800">**Quand**</span> je dis "Précédent" via la commande vocale

<span style="color: #4CAF50">**Alors**</span> la séquence 5 démarre depuis le début

---

## 11. Pause et reprise à la position exacte

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la séquence 4 à la position 2:30

<span style="color: #FF9800">**Quand**</span> je mets en pause
<span style="color: #9E9E9E">**Et**</span> que j'attends 5 minutes
<span style="color: #9E9E9E">**Et**</span> que je reprends la lecture

<span style="color: #4CAF50">**Alors**</span> la séquence reprend exactement à 2:30
<span style="color: #9E9E9E">**Et**</span> aucune donnée n'est perdue

---

## 12. Guidage vocal automatique entre séquences

<span style="color: #2196F3">**Étant donné**</span> que la séquence 2 se termine

<span style="color: #FF9800">**Quand**</span> la transition vers la séquence 3 se produit

<span style="color: #4CAF50">**Alors**</span> j'entends un message vocal:
<span style="color: #9E9E9E">**Et**</span> la séquence 3 ne démarre pas automatiquement (navigation manuelle)

---

## 13. Avertissement si éloignement du point d'intérêt

<span style="color: #2196F3">**Étant donné**</span> que je suis dans le guide du Louvre
<span style="color: #9E9E9E">**Et**</span> que je devrais être devant la Vénus de Milo (séquence 3)

<span style="color: #FF9800">**Quand**</span> je m'éloigne de plus de 50m de ce point

<span style="color: #4CAF50">**Alors**</span> j'entends un message vocal:
<span style="color: #9E9E9E">**Et**</span> un bouton "Voir le plan" apparaît dans l'interface

---

## 14. Sauvegarde automatique de la progression

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la séquence 5 à la position 1:45

<span style="color: #FF9800">**Quand**</span> je ferme l'application brutalement
<span style="color: #9E9E9E">**Et**</span> que je la rouvre 10 minutes plus tard

<span style="color: #4CAF50">**Alors**</span> je vois une popup "Reprendre la visite du Musée du Louvre ?"
<span style="color: #9E9E9E">**Et**</span> si je choisis "Reprendre", je retourne à la séquence 5 à 1:45

---

## 15. Option de recommencer depuis le début

<span style="color: #2196F3">**Étant donné**</span> que j'ai une progression sauvegardée à la séquence 7

<span style="color: #FF9800">**Quand**</span> je rouvre le guide

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai une progression sauvegardée depuis 30 jours

<span style="color: #FF9800">**Quand**</span> j'essaie de reprendre le guide

<span style="color: #4CAF50">**Alors**</span> la sauvegarde est considérée comme expirée
<span style="color: #9E9E9E">**Et**</span> je recommence depuis la séquence 1
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un guide sur mon iPhone à la séquence 4

<span style="color: #FF9800">**Quand**</span> je ferme l'app et ouvre sur mon iPad

<span style="color: #4CAF50">**Alors**</span> je vois la progression synchronisée
<span style="color: #9E9E9E">**Et**</span> je peux reprendre à la séquence 4 sur l'iPad

---

## 18. Marquage "Terminé" après toutes les séquences

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la dernière séquence (12/12)

<span style="color: #FF9800">**Quand**</span> cette séquence se termine

<span style="color: #4CAF50">**Alors**</span> le guide est marqué "✅ Terminé" dans mon historique
<span style="color: #9E9E9E">**Et**</span> je vois un message de félicitation:
<span style="color: #9E9E9E">**Et**</span> le créateur gagne les statistiques d'écoute complète

---

## 19. Création d'audio-guide par un créateur

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur

<span style="color: #FF9800">**Quand**</span> je crée un nouvel audio-guide

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> la durée totale est calculée automatiquement

---

## 20. Structure JSON de stockage audio-guide

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur publie un audio-guide du Louvre

<span style="color: #FF9800">**Quand**</span> les métadonnées sont stockées en base

<span style="color: #4CAF50">**Alors**</span> le format JSON contient:

---

## 21. Limitation du nombre de séquences

<span style="color: #2196F3">**Étant donné**</span> que je crée un audio-guide

<span style="color: #FF9800">**Quand**</span> j'essaie d'ajouter plus de 50 séquences

<span style="color: #4CAF50">**Alors**</span> je vois le message "Maximum 50 séquences par audio-guide"
<span style="color: #9E9E9E">**Et**</span> je dois structurer mon contenu différemment ou créer plusieurs guides

---

## 22. Quitter le guide et sauvegarder

<span style="color: #2196F3">**Étant donné**</span> que j'écoute la séquence 6

<span style="color: #FF9800">**Quand**</span> je tape sur le bouton "×" (fermer)

<span style="color: #4CAF50">**Alors**</span> je vois une confirmation:
<span style="color: #9E9E9E">**Et**</span> si je confirme, la progression est enregistrée
<span style="color: #9E9E9E">**Et**</span> je retourne à l'écran principal

---

## 23. Statistiques créateur pour audio-guides

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur d'un audio-guide

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur peut publier plusieurs versions linguistiques

<span style="color: #FF9800">**Quand**</span> un touriste anglophone visite le Louvre

<span style="color: #4CAF50">**Alors**</span> il voit les guides disponibles en anglais
<span style="color: #9E9E9E">**Et**</span> peut choisir parmi les guides traduits
<span style="color: #F44336">**Mais**</span> cette fonctionnalité n'est pas disponible en MVP

---

## 25. Publicité entre séquences d'audio-guide

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit
<span style="color: #9E9E9E">**Et**</span> que j'écoute un audio-guide

<span style="color: #FF9800">**Quand**</span> je passe de la séquence 5 à la séquence 6

<span style="color: #4CAF50">**Alors**</span> une publicité peut être insérée (1 pub toutes les 5 séquences)
<span style="color: #9E9E9E">**Et**</span> la publicité est skippable après 5 secondes
<span style="color: #9E9E9E">**Et**</span> les utilisateurs Premium ne voient pas de publicité

---

## 26. Audio-guide en mode offline

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé un audio-guide complet

<span style="color: #FF9800">**Quand**</span> je visite le lieu sans connexion internet

<span style="color: #4CAF50">**Alors**</span> toutes les séquences sont disponibles hors ligne
<span style="color: #9E9E9E">**Et**</span> la navigation fonctionne normalement
<span style="color: #9E9E9E">**Et**</span> seule la sauvegarde cloud est différée jusqu'à reconnexion

---

## 27. Notation d'un audio-guide après écoute

<span style="color: #2196F3">**Étant donné**</span> que j'ai terminé un audio-guide

<span style="color: #FF9800">**Quand**</span> je ferme l'interface

<span style="color: #4CAF50">**Alors**</span> je vois une popup "Notez cette visite"
<span style="color: #9E9E9E">**Et**</span> je peux donner une note de 1 à 5 étoiles
<span style="color: #9E9E9E">**Et**</span> cette note contribue à la note globale visible par les autres utilisateurs

---

## 28. Filtrage par langue dans la page de sélection

<span style="color: #2196F3">**Étant donné**</span> que plusieurs audio-guides sont disponibles en différentes langues

<span style="color: #FF9800">**Quand**</span> j'accède à la page de sélection

<span style="color: #4CAF50">**Alors**</span> je peux filtrer par langue
<span style="color: #9E9E9E">**Et**</span> par défaut, les guides dans ma langue système sont affichés en premier

---

## 29. Réutilisation de l'infrastructure existante

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide est techniquement un contenu structuré

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> aucune infrastructure dédiée n'est nécessaire

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant qu'auditeur
> <span style="color: #9E9E9E">**Et**</span> que je suis abonné au créateur "JeanDupont"
## 1. Boost de +30% appliqué au score final

<span style="color: #2196F3">**Étant donné**</span> un contenu du créateur "JeanDupont" avec:

    | score_geo | 0.5 |
    |---|---|
    | score_interet | 0.6 |
    | score_engage | 0.5 |


<span style="color: #FF9800">**Quand**</span> le score final est calculé

<span style="color: #4CAF50">**Alors**</span> le score de base est 0.53
<span style="color: #9E9E9E">**Et**</span> le boost abonnement de +30% est appliqué
<span style="color: #9E9E9E">**Et**</span> le score final avec boost est 0.69

---

## 2. Contenu non-suivi peut battre contenu suivi

<span style="color: #2196F3">**Étant donné**</span> que je suis à Paris
<span style="color: #9E9E9E">**Et**</span> 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 |


<span style="color: #FF9800">**Quand**</span> l'algorithme sélectionne le prochain contenu

<span style="color: #4CAF50">**Alors**</span> le Contenu A est proposé en premier

---

## 3. Contenu suivi remporte grâce au boost

<span style="color: #2196F3">**Étant donné**</span> que je suis à Paris
<span style="color: #9E9E9E">**Et**</span> 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 |


<span style="color: #FF9800">**Quand**</span> l'algorithme sélectionne le prochain contenu

<span style="color: #4CAF50">**Alors**</span> le Contenu B est proposé en premier

---

## 4. Contenu suivi avec faible engagement ne domine pas

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "CreateurMoyen"
<span style="color: #9E9E9E">**Et**</span> qu'il publie un contenu avec très faible engagement (score 0.30)
<span style="color: #9E9E9E">**Et**</span> qu'un contenu viral d'un créateur non-suivi a un score de 0.85

<span style="color: #FF9800">**Quand**</span> l'algorithme sélectionne le prochain contenu

<span style="color: #4CAF50">**Alors**</span> le contenu viral est proposé en premier (0.85)

---

## 5. Pas de file dédiée aux abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 50 créateurs

<span style="color: #FF9800">**Quand**</span> l'algorithme génère ma file d'attente de 5 contenus

<span style="color: #4CAF50">**Alors**</span> les contenus suivis et non-suivis sont mélangés
<span style="color: #9E9E9E">**Et**</span> tous entrent en compétition selon leurs scores (avec boost si abonnement)
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de section séparée "Contenus de vos abonnements"

---

## 6. Vérification du calcul du boost

<span style="color: #2196F3">**Étant donné**</span> un contenu d'un créateur suivi
<span style="color: #9E9E9E">**Et**</span> que le score final de base est calculé à 0.65

<span style="color: #FF9800">**Quand**</span> le boost abonnement est appliqué

<span style="color: #4CAF50">**Alors**</span> le multiplicateur utilisé est exactement 1.3
<span style="color: #9E9E9E">**Et**</span> le score final avec boost est 0.845 (0.65 × 1.3)
<span style="color: #9E9E9E">**Et**</span> le résultat est arrondi à 2 décimales: 0.85

---

## 7. Boost appliqué à tous les contenus du créateur suivi

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "JeanDupont"
<span style="color: #9E9E9E">**Et**</span> qu'il a publié 10 contenus différents

<span style="color: #FF9800">**Quand**</span> l'algorithme évalue chacun de ces contenus

<span style="color: #4CAF50">**Alors**</span> le boost de +30% est appliqué à tous les 10 contenus
<span style="color: #9E9E9E">**Et**</span> chaque contenu bénéficie du même multiplicateur 1.3

---

## 8. Plusieurs créateurs suivis en compétition

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à "Créateur A" et "Créateur B"
<span style="color: #9E9E9E">**Et**</span> 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 |


<span style="color: #FF9800">**Quand**</span> l'algorithme sélectionne le prochain contenu

<span style="color: #4CAF50">**Alors**</span> le contenu du Créateur A est proposé en premier (0.91 > 0.85)
<span style="color: #9E9E9E">**Et**</span> les 2 bénéficient du boost, mais le meilleur score gagne

---

## 9. Contenu national d'un créateur suivi

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à "MediaNational"
<span style="color: #9E9E9E">**Et**</span> qu'il publie un contenu de type "National" (score_geo 0.2)

<span style="color: #FF9800">**Quand**</span> le score est calculé avec:

    | score_geo | score_interet | score_engage |
    |---|---|---|
    | 0.2 | 0.7 | 0.6 |


<span style="color: #4CAF50">**Alors**</span> le score de base est environ 0.50
<span style="color: #9E9E9E">**Et**</span> avec le boost abonnement, le score devient 0.65
<span style="color: #9E9E9E">**Et**</span> le contenu peut être proposé malgré son score géo faible

---

## 10. Transparence du boost dans les paramètres

<span style="color: #FF9800">**Quand**</span> j'accède aux paramètres de l'algorithme de recommandation

<span style="color: #4CAF50">**Alors**</span> je vois l'information: "Les contenus de vos créateurs suivis bénéficient d'un boost de +30%"
<span style="color: #9E9E9E">**Et**</span> je comprends que ce n'est pas une priorité absolue
<span style="color: #9E9E9E">**Et**</span> que la découverte de nouveaux contenus reste possible

---

## 11. Boost désactivé si désabonnement

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "JeanDupont"
<span style="color: #9E9E9E">**Et**</span> qu'un de ses contenus bénéficiait du boost +30%

<span style="color: #FF9800">**Quand**</span> je me désabonne de "JeanDupont"

<span style="color: #4CAF50">**Alors**</span> ses contenus n'ont plus le boost
<span style="color: #9E9E9E">**Et**</span> leur score revient au score de base sans multiplicateur

---

## 12. Contenu d'un créateur nouvellement suivi

<span style="color: #2196F3">**Étant donné**</span> que je viens de m'abonner à "NouveauCreateur"
<span style="color: #9E9E9E">**Et**</span> qu'il a publié un contenu il y a 2 jours

<span style="color: #FF9800">**Quand**</span> l'algorithme recalcule les scores

<span style="color: #4CAF50">**Alors**</span> le boost de +30% est immédiatement appliqué à ce contenu
<span style="color: #9E9E9E">**Et**</span> il peut apparaître dans ma prochaine file d'attente

---

## 13. Impact sur le taux de contenu suivi dans le feed

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 30 créateurs
<span style="color: #9E9E9E">**Et**</span> que j'écoute 100 contenus sur une semaine

<span style="color: #FF9800">**Quand**</span> j'analyse la répartition

<span style="color: #4CAF50">**Alors**</span> environ 40-50% des contenus proviennent de créateurs suivis
<span style="color: #9E9E9E">**Et**</span> 50-60% proviennent de créateurs non-suivis (découverte)

---

## 14. Contenu suivi hors zone géographique

<span style="color: #2196F3">**Étant donné**</span> que je suis à Paris
<span style="color: #9E9E9E">**Et**</span> que je suis abonné à un créateur de Marseille
<span style="color: #9E9E9E">**Et**</span> qu'il publie un contenu ancré à Marseille (hors de portée)

<span style="color: #FF9800">**Quand**</span> l'algorithme évalue ce contenu

<span style="color: #4CAF50">**Alors**</span> le score géo est quasi nul (0.05)
<span style="color: #9E9E9E">**Et**</span> même avec boost +30%, le score reste très faible
<span style="color: #9E9E9E">**Et**</span> le contenu n'est probablement pas proposé

---

## 15. Performance de calcul du boost

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 100 créateurs
<span style="color: #9E9E9E">**Et**</span> que l'algorithme évalue 1000 contenus potentiels

<span style="color: #FF9800">**Quand**</span> le calcul des scores avec boost est effectué

<span style="color: #4CAF50">**Alors**</span> le temps de calcul reste inférieur à 50ms
<span style="color: #9E9E9E">**Et**</span> la requête SQL utilise un JOIN sur la table abonnements

---

## 16. Boost combiné avec d'autres facteurs

<span style="color: #2196F3">**Étant donné**</span> un contenu d'un créateur suivi
<span style="color: #9E9E9E">**Et**</span> que le contenu bénéficie aussi de:

    | facteur | impact |
    |---|---|
    | Score d'engagement élevé | +20% |
    | Contenu récent (<24h) | +10% |
    | Boost abonnement | +30% |


<span style="color: #FF9800">**Quand**</span> le score final est calculé

<span style="color: #4CAF50">**Alors**</span> le boost abonnement s'applique au score final (après tous les autres calculs)
<span style="color: #9E9E9E">**Et**</span> les boosts ne s'additionnent pas, le boost abonnement est un multiplicateur final

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant qu'auditeur
## 1. Limite maximale de 200 abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 199 créateurs

<span style="color: #FF9800">**Quand**</span> j'essaie de m'abonner à un 200ème créateur

<span style="color: #4CAF50">**Alors**</span> l'abonnement réussit
<span style="color: #9E9E9E">**Et**</span> je suis maintenant abonné à 200 créateurs

---

## 2. Impossible de dépasser 200 abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suis déjà abonné à 200 créateurs

<span style="color: #FF9800">**Quand**</span> j'essaie de m'abonner à un nouveau créateur

<span style="color: #4CAF50">**Alors**</span> l'action échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message:

---

## 3. Suggestion de désabonnement de créateurs inactifs

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 200 créateurs
<span style="color: #9E9E9E">**Et**</span> que j'essaie de m'abonner à un nouveau créateur

<span style="color: #FF9800">**Quand**</span> je vois le message de limite atteinte

<span style="color: #4CAF50">**Alors**</span> je vois aussi une suggestion:
<span style="color: #9E9E9E">**Et**</span> un bouton "Désabonner" est proposé pour ce créateur

---

## 4. Liste triable des abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 150 créateurs

<span style="color: #FF9800">**Quand**</span> j'accède à ma liste d'abonnements

<span style="color: #4CAF50">**Alors**</span> 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%

<span style="color: #2196F3">**Étant donné**</span> que mes jauges d'intérêt sont:

    | catégorie | valeur initiale |
    |---|---|
    | Automobile | 60% |
    | Voyage | 55% |

<span style="color: #9E9E9E">**Et**</span> qu'un créateur tague ses contenus "Automobile" et "Voyage"

<span style="color: #FF9800">**Quand**</span> je m'abonne à ce créateur

<span style="color: #4CAF50">**Alors**</span> mes jauges évoluent:

    | catégorie | nouvelle valeur |
    |---|---|
    | Automobile | 65% (+5%) |
    | Voyage | 60% (+5%) |


---

## 6. Abonnement avec créateur ayant 3 tags

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur tague ses contenus:

    | tags |
    |---|
    | Automobile, Voyage, Technologie |

<span style="color: #9E9E9E">**Et**</span> que mes jauges sont toutes à 50%

<span style="color: #FF9800">**Quand**</span> je m'abonne à ce créateur

<span style="color: #4CAF50">**Alors**</span> les 3 jauges augmentent de +5%:

    | catégorie | nouvelle valeur |
    |---|---|
    | Automobile | 55% |
    | Voyage | 55% |
    | Technologie | 55% |


---

## 7. Désabonnement diminue les jauges de -5%

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à un créateur avec tags "Politique" et "Économie"
<span style="color: #9E9E9E">**Et**</span> que mes jauges sont:

    | catégorie | valeur actuelle |
    |---|---|
    | Politique | 70% |
    | Économie | 65% |


<span style="color: #FF9800">**Quand**</span> je me désabonne de ce créateur

<span style="color: #4CAF50">**Alors**</span> mes jauges évoluent:

    | catégorie | nouvelle valeur |
    |---|---|
    | Politique | 65% (-5%) |
    | Économie | 60% (-5%) |


---

## 8. Désabonnement sans confirmation

<span style="color: #2196F3">**Étant donné**</span> que je consulte le profil d'un créateur suivi

<span style="color: #FF9800">**Quand**</span> je clique sur "Se désabonner"

<span style="color: #4CAF50">**Alors**</span> le désabonnement est immédiat
<span style="color: #9E9E9E">**Et**</span> aucune popup de confirmation n'apparaît

---

## 9. Réabonnement possible immédiatement

<span style="color: #2196F3">**Étant donné**</span> que je viens de me désabonner d'un créateur

<span style="color: #FF9800">**Quand**</span> je consulte à nouveau son profil

<span style="color: #4CAF50">**Alors**</span> le bouton "S'abonner" est affiché
<span style="color: #9E9E9E">**Et**</span> je peux me réabonner immédiatement
<span style="color: #9E9E9E">**Et**</span> mes jauges augmentent à nouveau de +5%

---

## 10. Effet symétrique abonnement/désabonnement

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur a les tags "Musique" et "Culture"
<span style="color: #9E9E9E">**Et**</span> que ma jauge Musique est à 50%

<span style="color: #FF9800">**Quand**</span> je m'abonne puis me désabonne immédiatement

<span style="color: #4CAF50">**Alors**</span> ma jauge revient exactement à 50%
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de perte ou gain net

---

## 11. Abonnement ne dépasse pas 100% de jauge

<span style="color: #2196F3">**Étant donné**</span> que ma jauge Automobile est à 97%
<span style="color: #9E9E9E">**Et**</span> qu'un créateur tague ses contenus "Automobile"

<span style="color: #FF9800">**Quand**</span> je m'abonne à ce créateur

<span style="color: #4CAF50">**Alors**</span> ma jauge Automobile passe à 100% (limite max)
<span style="color: #9E9E9E">**Et**</span> l'augmentation effective est de +3% seulement

---

## 12. Désabonnement ne descend pas sous 0%

<span style="color: #2196F3">**Étant donné**</span> que ma jauge Politique est à 3%
<span style="color: #9E9E9E">**Et**</span> que je suis abonné à un créateur avec tag "Politique"

<span style="color: #FF9800">**Quand**</span> je me désabonne de ce créateur

<span style="color: #4CAF50">**Alors**</span> ma jauge Politique passe à 0% (limite min)
<span style="color: #9E9E9E">**Et**</span> la diminution effective est de -3% seulement

---

## 13. Créateur ne voit pas qui est abonné (privacy)

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "JeanDupont"

<span style="color: #FF9800">**Quand**</span> "JeanDupont" consulte ses statistiques

<span style="color: #4CAF50">**Alors**</span> il voit le nombre total d'abonnés (ex: "1,247 abonnés")
<span style="color: #F44336">**Mais**</span> il ne voit pas la liste des utilisateurs abonnés
<span style="color: #9E9E9E">**Et**</span> mon identité reste privée

---

## 14. Créateur voit uniquement le nombre total d'abonnés

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur
<span style="color: #9E9E9E">**Et**</span> que j'ai 523 abonnés

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> je vois "523 abonnés"
<span style="color: #F44336">**Mais**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "Alice"
<span style="color: #9E9E9E">**Et**</span> qu'"Alice" est abonnée à mon compte créateur

<span style="color: #FF9800">**Quand**</span> je consulte le profil d'"Alice"

<span style="color: #4CAF50">**Alors**</span> je ne vois pas d'indication qu'elle est abonnée à moi
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de badge "Abonné mutuellement"

---

## 16. Performance avec 200 abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 200 créateurs

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule ma recommandation

<span style="color: #4CAF50">**Alors**</span> la requête SQL utilise un JOIN sur la table abonnements
<span style="color: #9E9E9E">**Et**</span> la table est indexée sur user_id et creator_id
<span style="color: #9E9E9E">**Et**</span> le temps de calcul reste inférieur à 50ms

---

## 17. Impact sur la recommandation avec beaucoup d'abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 150 créateurs très actifs
<span style="color: #9E9E9E">**Et**</span> qu'ils publient collectivement 100 contenus par jour

<span style="color: #FF9800">**Quand**</span> l'algorithme génère ma file de 5 contenus

<span style="color: #4CAF50">**Alors**</span> environ 60-70% des contenus proviennent de créateurs suivis (grâce au boost +30%)
<span style="color: #F44336">**Mais**</span> 30-40% proviennent de nouveaux créateurs (découverte)

---

## 18. Notification de désabonnement au créateur (non implémenté)

<span style="color: #2196F3">**Étant donné**</span> que je me désabonne d'un créateur

<span style="color: #4CAF50">**Alors**</span> le créateur ne reçoit aucune notification
<span style="color: #9E9E9E">**Et**</span> il ne peut pas savoir qui s'est désabonné

---

## 19. Statistiques d'abonnements pour l'utilisateur

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 87 créateurs

<span style="color: #FF9800">**Quand**</span> j'accède à mes statistiques d'abonnements

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 120 créateurs

<span style="color: #FF9800">**Quand**</span> j'accède à ma liste d'abonnements

<span style="color: #4CAF50">**Alors**</span> je peux chercher par nom de créateur
<span style="color: #9E9E9E">**Et**</span> les résultats sont filtrés en temps réel
<span style="color: #9E9E9E">**Et**</span> je trouve rapidement un créateur spécifique

---

## 21. Export de la liste d'abonnements (RGPD)

<span style="color: #2196F3">**Étant donné**</span> que je demande l'export de mes données

<span style="color: #FF9800">**Quand**</span> l'export est généré

<span style="color: #4CAF50">**Alors**</span> la liste de mes abonnements est incluse:

---

## 22. Suppression compte utilisateur et impact sur abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 50 créateurs

<span style="color: #FF9800">**Quand**</span> je supprime définitivement mon compte

<span style="color: #4CAF50">**Alors**</span> tous mes abonnements sont supprimés
<span style="color: #9E9E9E">**Et**</span> le compteur d'abonnés de chaque créateur est décrémenté de -1
<span style="color: #9E9E9E">**Et**</span> les jauges n'existent plus (données supprimées)

---

## 23. Suppression compte créateur et impact sur abonnés

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "Bob"

<span style="color: #FF9800">**Quand**</span> "Bob" supprime son compte créateur

<span style="color: #4CAF50">**Alors**</span> je suis automatiquement désabonné
<span style="color: #9E9E9E">**Et**</span> mes jauges diminuent de -5% pour les tags de "Bob"
<span style="color: #9E9E9E">**Et**</span> je ne vois plus "Bob" dans ma liste d'abonnements

---

## 24. Limite 200 justifiée par usage réaliste

<span style="color: #2196F3">**Étant donné**</span> que la moyenne d'abonnements sur YouTube est de ~50-100 chaînes
<span style="color: #9E9E9E">**Et**</span> que Twitter limite à 5000 follows (mais moyenne ~150)

<span style="color: #FF9800">**Quand**</span> RoadWave fixe la limite à 200

<span style="color: #4CAF50">**Alors**</span> cela couvre largement 99% des utilisateurs
<span style="color: #9E9E9E">**Et**</span> évite les abus (comptes spam suivant tout le monde)

---

## 25. Table PostgreSQL optimisée pour abonnements

<span style="color: #2196F3">**Étant donné**</span> la structure de table subscriptions:

<span style="color: #4CAF50">**Alors**</span> les requêtes d'abonnements sont O(1) avec index
<span style="color: #9E9E9E">**Et**</span> le count d'abonnés par créateur est rapide
<span style="color: #9E9E9E">**Et**</span> la vérification "est abonné ?" est instantanée

---

## 26. Détection d'abonnements abusifs

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur s'abonne à 200 créateurs en moins de 5 minutes

<span style="color: #FF9800">**Quand**</span> le système détecte cette activité suspecte

<span style="color: #4CAF50">**Alors**</span> un rate limiting est appliqué (max 10 abonnements/minute)
<span style="color: #9E9E9E">**Et**</span> l'utilisateur voit "Trop d'actions rapides. Veuillez réessayer dans 1 minute"
<span style="color: #9E9E9E">**Et**</span> cela prévient les bots de spam

---

## 27. Badge créateur vérifié visible dans abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 3 créateurs dont 1 vérifié

<span style="color: #FF9800">**Quand**</span> je consulte ma liste d'abonnements

<span style="color: #4CAF50">**Alors**</span> le créateur vérifié a un badge ✓ bleu
<span style="color: #9E9E9E">**Et**</span> les créateurs non vérifiés n'ont pas de badge

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant qu'auditeur
> <span style="color: #9E9E9E">**Et**</span> que j'ai activé les notifications
## 1. Détection automatique du contexte en voiture

<span style="color: #2196F3">**Étant donné**</span> que ma vitesse GPS est de 50 km/h

<span style="color: #FF9800">**Quand**</span> le système détecte mon contexte

<span style="color: #4CAF50">**Alors**</span> je suis identifié comme "En voiture"
<span style="color: #9E9E9E">**Et**</span> les notifications push sont désactivées
<span style="color: #9E9E9E">**Et**</span> seules les notifications in-app sont actives

---

## 2. Détection automatique du contexte à pied

<span style="color: #2196F3">**Étant donné**</span> que ma vitesse GPS est de 3 km/h

<span style="color: #FF9800">**Quand**</span> le système détecte mon contexte

<span style="color: #4CAF50">**Alors**</span> je suis identifié comme "À pied"
<span style="color: #9E9E9E">**Et**</span> les notifications push sont activées
<span style="color: #9E9E9E">**Et**</span> l'interface tactile et vocale sont disponibles

---

## 3. Zone de transition 5-10 km/h

<span style="color: #2196F3">**Étant donné**</span> que ma vitesse GPS varie entre 5 et 10 km/h

<span style="color: #FF9800">**Quand**</span> le système détecte mon contexte

<span style="color: #4CAF50">**Alors**</span> un algorithme de lissage est appliqué sur 30 secondes
<span style="color: #9E9E9E">**Et**</span> le mode est déterminé selon la vitesse moyenne
<span style="color: #9E9E9E">**Et**</span> les changements de mode ne sont pas trop fréquents

---

## 4. Nouveau contenu créateur suivi - Mode voiture

<span style="color: #2196F3">**Étant donné**</span> que je suis en voiture (vitesse >10 km/h)
<span style="color: #9E9E9E">**Et**</span> que je suis abonné au créateur "JeanDupont"

<span style="color: #FF9800">**Quand**</span> "JeanDupont" publie un nouveau contenu dans ma zone

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de notification push
<span style="color: #F44336">**Mais**</span> je vois un badge compteur in-app
<span style="color: #9E9E9E">**Et**</span> le contenu apparaît dans ma file avec boost +30%

---

## 5. Nouveau contenu créateur suivi - Mode piéton

<span style="color: #2196F3">**Étant donné**</span> que je suis à pied (vitesse <5 km/h)
<span style="color: #9E9E9E">**Et**</span> que je suis abonné au créateur "JeanDupont"
<span style="color: #9E9E9E">**Et**</span> que je suis situé en Île-de-France

<span style="color: #FF9800">**Quand**</span> "JeanDupont" publie un contenu géolocalisé en Île-de-France

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push:

---

## 6. Live créateur suivi - Mode voiture

<span style="color: #2196F3">**Étant donné**</span> que je suis en voiture
<span style="color: #9E9E9E">**Et**</span> que je suis abonné au créateur "RadioLive"

<span style="color: #FF9800">**Quand**</span> "RadioLive" démarre un live dans ma zone

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de notification push
<span style="color: #F44336">**Mais**</span> je vois un badge compteur in-app
<span style="color: #9E9E9E">**Et**</span> le live peut apparaître dans ma recommandation automatiquement

---

## 7. Live créateur suivi - Mode piéton

<span style="color: #2196F3">**Étant donné**</span> que je suis à pied
<span style="color: #9E9E9E">**Et**</span> que je suis abonné au créateur "RadioLive"
<span style="color: #9E9E9E">**Et**</span> que je suis situé dans la zone du live

<span style="color: #FF9800">**Quand**</span> "RadioLive" démarre un live

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push:

---

## 8. Audio-guide disponible à proximité - Mode piéton

<span style="color: #2196F3">**Étant donné**</span> que je suis à pied

<span style="color: #FF9800">**Quand**</span> je passe à moins de 100m d'un lieu avec audio-guides

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push:

---

## 9. Audio-guide disponible à proximité - Mode voiture

<span style="color: #2196F3">**Étant donné**</span> que je suis en voiture

<span style="color: #FF9800">**Quand**</span> je passe à moins de 100m d'un lieu avec audio-guides

<span style="color: #4CAF50">**Alors**</span> je reçois une notification audio (bip)
<span style="color: #9E9E9E">**Et**</span> une annonce vocale: "Audio-guide disponible"
<span style="color: #F44336">**Mais**</span> pas de notification push (sécurité)

---

## 10. Filtrage géographique des notifications

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "CreateurMarseille"
<span style="color: #9E9E9E">**Et**</span> que je suis situé à Paris

<span style="color: #FF9800">**Quand**</span> "CreateurMarseille" publie un contenu ancré à Marseille

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de notification
<span style="color: #9E9E9E">**Et**</span> cela évite la frustration de contenus non écoutables

---

## 11. Contenu national notifie tous les abonnés

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "MediaNational"
<span style="color: #9E9E9E">**Et**</span> que je suis situé n'importe où en France

<span style="color: #FF9800">**Quand**</span> "MediaNational" publie un contenu de type "National"

<span style="color: #4CAF50">**Alors**</span> je reçois une notification (si mode piéton)

---

## 12. Limite de 10 notifications push par jour

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 50 créateurs actifs
<span style="color: #9E9E9E">**Et**</span> que j'ai déjà reçu 10 notifications push aujourd'hui

<span style="color: #FF9800">**Quand**</span> un 11ème contenu est publié

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de notification push individuelle
<span style="color: #F44336">**Mais**</span> une notification groupée: "🎧 3 nouveaux contenus de créateurs suivis"

---

## 13. Paramétrage de la limite quotidienne

<span style="color: #2196F3">**Étant donné**</span> que la limite par défaut est de 10 notifications/jour

<span style="color: #FF9800">**Quand**</span> j'accède aux paramètres de notifications

<span style="color: #4CAF50">**Alors**</span> je peux modifier la limite entre 5 et 20
<span style="color: #9E9E9E">**Et**</span> si je choisis 15, je recevrai jusqu'à 15 notifications/jour

---

## 14. Mode silencieux nocturne par défaut

<span style="color: #2196F3">**Étant donné**</span> que le mode silencieux est activé de 22h à 8h par défaut
<span style="color: #9E9E9E">**Et**</span> qu'il est 23h30

<span style="color: #FF9800">**Quand**</span> un créateur suivi publie un contenu

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de notification push
<span style="color: #F44336">**Mais**</span> les notifications sont empilées
<span style="color: #9E9E9E">**Et**</span> je les vois le lendemain matin à 8h01

---

## 15. Exception du mode silencieux pour les lives

<span style="color: #2196F3">**Étant donné**</span> que le mode silencieux est activé (22h-8h)
<span style="color: #9E9E9E">**Et**</span> qu'il est 23h00
<span style="color: #9E9E9E">**Et**</span> que j'ai activé "Notifications importantes uniquement" (lives uniquement)

<span style="color: #FF9800">**Quand**</span> un créateur suivi démarre un live

<span style="color: #4CAF50">**Alors**</span> je reçois quand même la notification push du live

---

## 16. Désactivation complète des notifications

<span style="color: #2196F3">**Étant donné**</span> que j'accède aux paramètres de notifications

<span style="color: #FF9800">**Quand**</span> je désactive toutes les notifications

<span style="color: #4CAF50">**Alors**</span> je ne reçois plus aucune notification push
<span style="color: #9E9E9E">**Et**</span> les badges in-app sont également désactivés
<span style="color: #9E9E9E">**Et**</span> seule la recommandation algorithmique reste active

---

## 17. Notification "Nouveaux contenus" activée par défaut

<span style="color: #2196F3">**Étant donné**</span> que je crée un nouveau compte
<span style="color: #9E9E9E">**Et**</span> que je m'abonne à mon premier créateur

<span style="color: #FF9800">**Quand**</span> je consulte les préférences de notifications

<span style="color: #4CAF50">**Alors**</span> "Nouveaux contenus" est activé par défaut
<span style="color: #9E9E9E">**Et**</span> "Lives" est activé par défaut
<span style="color: #9E9E9E">**Et**</span> "Audio-guides proximité" est activé par défaut

---

## 18. Désactivation sélective par type de notification

<span style="color: #2196F3">**Étant donné**</span> que j'ai activé toutes les notifications

<span style="color: #FF9800">**Quand**</span> je désactive uniquement "Nouveaux contenus"

<span style="color: #4CAF50">**Alors**</span> je ne reçois plus de notifications pour nouveaux contenus
<span style="color: #F44336">**Mais**</span> je reçois toujours les notifications de lives
<span style="color: #9E9E9E">**Et**</span> les notifications d'audio-guides restent actives

---

## 19. Notification groupée après limite dépassée

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu 10 notifications push aujourd'hui
<span style="color: #9E9E9E">**Et**</span> que 5 nouveaux contenus sont publiés dans l'heure suivante

<span style="color: #FF9800">**Quand**</span> la 11ème notification devrait être envoyée

<span style="color: #4CAF50">**Alors**</span> les 5 contenus sont regroupés en une seule notification:

---

## 20. Détail de la notification groupée

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu une notification groupée "3 nouveaux contenus"

<span style="color: #FF9800">**Quand**</span> je tape sur la notification

<span style="color: #4CAF50">**Alors**</span> l'app s'ouvre sur une liste des 3 contenus:

    | créateur | titre |
    |---|---|
    | JeanDupont | "Actualité du jour" |
    | MarieDurand | "Podcast économie" |
    | PaulMartin | "Anecdote historique" |

<span style="color: #9E9E9E">**Et**</span> je peux choisir lequel écouter en premier

---

## 21. Personnalisation des plages horaires du mode silencieux

<span style="color: #2196F3">**Étant donné**</span> que le mode silencieux est 22h-8h par défaut

<span style="color: #FF9800">**Quand**</span> j'accède aux paramètres

<span style="color: #4CAF50">**Alors**</span> je peux modifier les heures: par exemple 23h-7h
<span style="color: #9E9E9E">**Et**</span> le mode silencieux s'applique dans la nouvelle plage horaire

---

## 22. Format notification nouveau contenu complet

<span style="color: #2196F3">**Étant donné**</span> que je suis à pied
<span style="color: #9E9E9E">**Et**</span> qu'un créateur suivi publie un contenu

<span style="color: #FF9800">**Quand**</span> je reçois la notification push

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis à pied
<span style="color: #9E9E9E">**Et**</span> qu'un créateur suivi démarre un live

<span style="color: #FF9800">**Quand**</span> je reçois la notification push

<span style="color: #4CAF50">**Alors**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu une notification pour un contenu
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas encore tapé dessus

<span style="color: #FF9800">**Quand**</span> le créateur supprime le contenu

<span style="color: #4CAF50">**Alors**</span> la notification est automatiquement retirée de mon centre de notifications
<span style="color: #9E9E9E">**Et**</span> si je tape dessus par erreur, je vois "Contenu non disponible"

---

## 25. Badge compteur in-app en mode voiture

<span style="color: #2196F3">**Étant donné**</span> que je suis en voiture
<span style="color: #9E9E9E">**Et**</span> que 5 créateurs suivis publient des contenus

<span style="color: #FF9800">**Quand**</span> j'ouvre l'application

<span style="color: #4CAF50">**Alors**</span> je vois un badge "5" sur l'onglet "Nouveautés"
<span style="color: #9E9E9E">**Et**</span> en consultant l'onglet, je vois les 5 nouveaux contenus
<span style="color: #9E9E9E">**Et**</span> le badge disparaît après consultation

---

## 26. Coût des notifications push Firebase

<span style="color: #2196F3">**Étant donné**</span> que je reçois 10 notifications push par jour
<span style="color: #9E9E9E">**Et**</span> que je suis actif 365 jours par an

<span style="color: #FF9800">**Quand**</span> le système calcule le coût

<span style="color: #4CAF50">**Alors**</span> 3650 notifications/an sont envoyées
<span style="color: #9E9E9E">**Et**</span> Firebase Cloud Messaging est gratuit jusqu'à plusieurs millions de notifications
<span style="color: #9E9E9E">**Et**</span> le coût reste 0€ pour le volume MVP/Growth

---

## 27. Deep link depuis notification push

<span style="color: #2196F3">**Étant donné**</span> que je reçois une notification push pour un contenu

<span style="color: #FF9800">**Quand**</span> je tape sur la notification

<span style="color: #4CAF50">**Alors**</span> l'app s'ouvre directement sur le contenu
<span style="color: #9E9E9E">**Et**</span> la lecture démarre automatiquement (si j'étais à pied)

---

## 28. Notification refusée si permissions désactivées au niveau OS

<span style="color: #2196F3">**Étant donné**</span> que j'ai désactivé les notifications dans les paramètres iOS/Android

<span style="color: #FF9800">**Quand**</span> un créateur suivi publie un contenu

<span style="color: #4CAF50">**Alors**</span> aucune notification push n'est envoyée
<span style="color: #9E9E9E">**Et**</span> l'app propose de réactiver les permissions dans les paramètres
<span style="color: #F44336">**Mais**</span> les badges in-app continuent de fonctionner

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que le créateur "guide@example.com" est connecté
> <span style="color: #9E9E9E">**Et**</span> que son compte est vérifié
## 1. 📋 Plan: Détection automatique du mode selon la vitesse

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur se déplace à <vitesse> km/h

<span style="color: #FF9800">**Quand**</span> la vitesse est calculée sur 30 secondes

<span style="color: #4CAF50">**Alors**</span> le mode <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

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide "Safari du Paugre" est disponible
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur se déplace à 35 km/h

<span style="color: #FF9800">**Quand**</span> l'audio-guide démarre

<span style="color: #4CAF50">**Alors**</span> une popup s'affiche:

---

## 3. Changement manuel du mode détecté

<span style="color: #2196F3">**Étant donné**</span> que le mode "Voiture" est suggéré automatiquement

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "Changer"

<span style="color: #4CAF50">**Alors**</span> les 4 modes sont proposés:

    | mode | emoji |
    |---|---|
    | Piéton | 🚶 |
    | Voiture | 🚗 |
    | Vélo | 🚴 |
    | Transport | 🚌 |


---

## 4. 📋 Plan: Caractéristiques par mode de déplacement

<span style="color: #2196F3">**Étant donné**</span> un audio-guide configuré en mode <mode>

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le créateur est sur son dashboard

<span style="color: #FF9800">**Quand**</span> il clique sur "Créer un audio-guide"

<span style="color: #4CAF50">**Alors**</span> le formulaire de création s'affiche
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le créateur est sur l'étape 1 du wizard

<span style="color: #FF9800">**Quand**</span> il complète le formulaire

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le créateur crée un audio-guide

<span style="color: #FF9800">**Quand**</span> il sélectionne le mode "🚗 Voiture (GPS auto + manuel)"

<span style="color: #4CAF50">**Alors**</span> le champ "Vitesse recommandée" s'affiche
<span style="color: #9E9E9E">**Et**</span> la plage suggérée est "30-50 km/h"

---

## 8. Validation du titre

<span style="color: #2196F3">**Étant donné**</span> que le créateur entre un titre

<span style="color: #FF9800">**Quand**</span> le titre contient moins de 5 caractères

<span style="color: #4CAF50">**Alors**</span> un message d'erreur "Minimum 5 caractères" s'affiche
<span style="color: #9E9E9E">**Et**</span> le bouton "Suivant" est désactivé

---

## 9. Validation de la description

<span style="color: #2196F3">**Étant donné**</span> que le créateur entre une description

<span style="color: #FF9800">**Quand**</span> la description contient 520 caractères

<span style="color: #4CAF50">**Alors**</span> un message d'erreur "Maximum 500 caractères" s'affiche
<span style="color: #9E9E9E">**Et**</span> les 20 caractères en trop sont surlignés en rouge

---

## 10. Étape 2 - Ajout de la première séquence

<span style="color: #2196F3">**Étant donné**</span> que le créateur est sur l'étape 2 "Ajout séquences"

<span style="color: #FF9800">**Quand**</span> il clique sur "Ajouter séquence"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le créateur ajoute une séquence en mode "Voiture"

<span style="color: #FF9800">**Quand**</span> il clique sur "📍 Ajouter point GPS"

<span style="color: #4CAF50">**Alors**</span> une carte s'affiche
<span style="color: #9E9E9E">**Et**</span> il peut:

    | action |
    |---|
    | Cliquer sur la carte |
    | Entrer coordonnées manuelles |
    | Utiliser sa position actuelle |


---

## 12. Configuration du rayon de déclenchement avec preview

<span style="color: #2196F3">**Étant donné**</span> qu'un point GPS est défini à (43.1234, 2.5678)

<span style="color: #FF9800">**Quand**</span> le créateur ajuste le curseur de rayon

<span style="color: #4CAF50">**Alors**</span> le rayon varie de 10m à 200m
<span style="color: #9E9E9E">**Et**</span> un cercle visuel est affiché sur la carte
<span style="color: #9E9E9E">**Et**</span> la valeur actuelle s'affiche "30m"

---

## 13. 📋 Plan: Rayon par défaut selon le mode

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en mode <mode>

<span style="color: #FF9800">**Quand**</span> le créateur ajoute un point GPS

<span style="color: #4CAF50">**Alors**</span> le rayon par défaut est <rayon_defaut>

**📊 Exemples de données:**

| mode | rayon_defaut |
|---|---|
| Voiture | 30m |
| Vélo | 50m |
| Transport | 100m |

---

## 14. Suggestion intelligente du rayon

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en mode "Voiture" avec vitesse recommandée 30 km/h

<span style="color: #FF9800">**Quand**</span> le créateur ajoute un point GPS

<span style="color: #4CAF50">**Alors**</span> une suggestion s'affiche: "Recommandé : 30m pour voiture à 30 km/h"

---

## 15. Upload audio pour une séquence

<span style="color: #2196F3">**Étant donné**</span> que le créateur crée une séquence "Introduction"

<span style="color: #FF9800">**Quand**</span> il upload un fichier audio de 5 MB

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un audio-guide avec 5 séquences:

    | ordre | titre |
    |---|---|
    | 1 | Introduction |
    | 2 | Les lions |
    | 3 | Les girafes |
    | 4 | Les éléphants |
    | 5 | Conclusion |


<span style="color: #FF9800">**Quand**</span> le créateur glisse "Les éléphants" en position 2

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un audio-guide avec seulement 1 séquence

<span style="color: #FF9800">**Quand**</span> le créateur tente de passer à l'étape suivante

<span style="color: #4CAF50">**Alors**</span> un message d'erreur s'affiche: "Minimum 2 séquences requis"
<span style="color: #9E9E9E">**Et**</span> le bouton "Suivant" est désactivé

---

## 18. Nombre maximum de séquences

<span style="color: #2196F3">**Étant donné**</span> un audio-guide avec 50 séquences

<span style="color: #FF9800">**Quand**</span> le créateur tente d'ajouter une 51ème séquence

<span style="color: #4CAF50">**Alors**</span> un message d'erreur s'affiche: "Maximum 50 séquences par audio-guide"
<span style="color: #9E9E9E">**Et**</span> le bouton "+ Ajouter séquence" est désactivé

---

## 19. Étape 3 - Preview carte avec tracé et points

<span style="color: #2196F3">**Étant donné**</span> un audio-guide avec 5 séquences géolocalisées

<span style="color: #FF9800">**Quand**</span> le créateur accède à l'étape 3 "Preview carte"

<span style="color: #4CAF50">**Alors**</span> une carte Leaflet s'affiche
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> les statistiques sont calculées

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la preview carte est affichée

<span style="color: #FF9800">**Quand**</span> le créateur clique sur le marker "2"

<span style="color: #4CAF50">**Alors**</span> une popup s'affiche avec:

    | information |
    |---|
    | Titre: "Les lions" |
    | Durée: 3:42 |
    | Rayon: 30m |
    | [✏️ Modifier] |
    | [🗑️ Supprimer] |


---

## 22. Zone de diffusion géographique

<span style="color: #2196F3">**Étant donné**</span> un audio-guide avec des points dans Paris

<span style="color: #FF9800">**Quand**</span> le créateur définit la zone de diffusion

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un créateur qui publie ses 3 premiers audio-guides

<span style="color: #FF9800">**Quand**</span> il clique sur "✅ Publier audio-guide"

<span style="color: #4CAF50">**Alors**</span> un message s'affiche:

---

## 24. Publication directe pour créateurs expérimentés

<span style="color: #2196F3">**Étant donné**</span> un créateur ayant publié 5 audio-guides validés
<span style="color: #9E9E9E">**Et**</span> aucun strike actif

<span style="color: #FF9800">**Quand**</span> il publie un nouvel audio-guide

<span style="color: #4CAF50">**Alors**</span> l'audio-guide est publié immédiatement
<span style="color: #9E9E9E">**Et**</span> il devient visible pour les utilisateurs
<span style="color: #9E9E9E">**Et**</span> aucune validation manuelle n'est requise

---

## 25. Mode piéton sans points GPS obligatoires

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en mode "🚶 Piéton"

<span style="color: #FF9800">**Quand**</span> le créateur ajoute une séquence

<span style="color: #4CAF50">**Alors**</span> le champ "Point GPS" est optionnel
<span style="color: #9E9E9E">**Et**</span> le champ "Rayon déclenchement" est masqué
<span style="color: #9E9E9E">**Et**</span> un message info s'affiche: "Mode manuel : les séquences se déclenchent au clic utilisateur"

---

## 26. Sauvegarde brouillon automatique

<span style="color: #2196F3">**Étant donné**</span> que le créateur édite un audio-guide depuis 5 minutes

<span style="color: #FF9800">**Quand**</span> il ajoute une nouvelle séquence

<span style="color: #4CAF50">**Alors**</span> l'audio-guide est sauvegardé en brouillon automatiquement
<span style="color: #9E9E9E">**Et**</span> un toast "Brouillon sauvegardé" s'affiche brièvement

---

## 27. Reprise d'un brouillon

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en brouillon "Safari du Paugre"
<span style="color: #9E9E9E">**Et**</span> qu'il contient 3 séquences complètes

<span style="color: #FF9800">**Quand**</span> le créateur retourne sur son dashboard

<span style="color: #4CAF50">**Alors**</span> le brouillon est visible avec le statut "📝 Brouillon"
<span style="color: #9E9E9E">**Et**</span> un bouton "Continuer" est disponible
<span style="color: #9E9E9E">**Et**</span> la progression "3/5 séquences" est affichée

---

## 28. Suppression d'un brouillon

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en brouillon

<span style="color: #FF9800">**Quand**</span> le créateur clique sur "🗑️ Supprimer"

<span style="color: #4CAF50">**Alors**</span> une confirmation s'affiche:

---

## 29. Modification d'un audio-guide publié

<span style="color: #2196F3">**Étant donné**</span> un audio-guide publié "Safari du Paugre"

<span style="color: #FF9800">**Quand**</span> le créateur clique sur "✏️ Modifier"

<span style="color: #4CAF50">**Alors**</span> 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) |  |

<span style="color: #9E9E9E">**Et**</span> un avertissement s'affiche: "Les modifications structurelles nécessitent une nouvelle publication"

---

## 30. Duplication d'un audio-guide existant

<span style="color: #2196F3">**Étant donné**</span> un audio-guide publié "Visite Paris"

<span style="color: #FF9800">**Quand**</span> le créateur clique sur "📋 Dupliquer"

<span style="color: #4CAF50">**Alors**</span> une copie est créée avec le titre "Visite Paris (copie)"
<span style="color: #9E9E9E">**Et**</span> toutes les séquences sont copiées
<span style="color: #9E9E9E">**Et**</span> le statut est "📝 Brouillon"
<span style="color: #9E9E9E">**Et**</span> le créateur peut modifier avant publication

---

## 31. Upload audio échoue (format non supporté)

<span style="color: #2196F3">**Étant donné**</span> que le créateur upload un fichier "audio.wav"

<span style="color: #FF9800">**Quand**</span> le format est vérifié

<span style="color: #4CAF50">**Alors**</span> un message d'erreur s'affiche: "Format non supporté. Utilisez MP3, AAC ou M4A"
<span style="color: #9E9E9E">**Et**</span> le fichier est rejeté

---

## 32. Upload audio échoue (taille trop grande)

<span style="color: #2196F3">**Étant donné**</span> que le créateur upload un fichier de 250 MB

<span style="color: #FF9800">**Quand**</span> la taille est vérifiée

<span style="color: #4CAF50">**Alors**</span> un message d'erreur s'affiche: "Fichier trop volumineux. Maximum 200 MB"
<span style="color: #9E9E9E">**Et**</span> le fichier est rejeté

---

## 33. Points GPS trop éloignés (alerte cohérence)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en mode "Piéton"
<span style="color: #9E9E9E">**Et**</span> une séquence au Louvre (Paris)

<span style="color: #FF9800">**Quand**</span> le créateur ajoute une séquence à Lyon

<span style="color: #4CAF50">**Alors**</span> un avertissement s'affiche:

---

## 34. Pas de connexion lors de la sauvegarde

<span style="color: #2196F3">**Étant donné**</span> que le créateur édite un audio-guide
<span style="color: #9E9E9E">**Et**</span> que la connexion réseau est perdue

<span style="color: #FF9800">**Quand**</span> il tente de sauvegarder

<span style="color: #4CAF50">**Alors**</span> le brouillon est sauvegardé localement
<span style="color: #9E9E9E">**Et**</span> un message s'affiche: "Sauvegarde locale. Sera synchronisée à la reconnexion"
<span style="color: #9E9E9E">**Et**</span> une icône "☁️ Hors ligne" s'affiche

---

## 35. Reprise après perte de connexion

<span style="color: #2196F3">**Étant donné**</span> un brouillon sauvegardé localement

<span style="color: #FF9800">**Quand**</span> la connexion réseau est rétablie

<span style="color: #4CAF50">**Alors**</span> le brouillon est synchronisé automatiquement
<span style="color: #9E9E9E">**Et**</span> un toast "✅ Audio-guide synchronisé" s'affiche

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que l'utilisateur "jean@example.com" est connecté
## 1. Téléchargement complet d'un audio-guide

<span style="color: #2196F3">**Étant donné**</span> un audio-guide "Visite du Louvre" avec 12 séquences

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "⬇️ Télécharger pour écouter hors ligne"

<span style="color: #4CAF50">**Alors**</span> toutes les 12 séquences sont téléchargées
<span style="color: #9E9E9E">**Et**</span> les métadonnées (titres, descriptions, GPS) sont sauvegardées
<span style="color: #9E9E9E">**Et**</span> les images (cover, miniatures) sont mises en cache

---

## 2. Affichage de la progression du téléchargement

<span style="color: #2196F3">**Étant donné**</span> qu'un téléchargement d'audio-guide est en cours

<span style="color: #FF9800">**Quand**</span> l'utilisateur consulte l'état

<span style="color: #4CAF50">**Alors**</span> la progression s'affiche:

---

## 3. Téléchargement uniquement en WiFi (par défaut)

<span style="color: #2196F3">**Étant donné**</span> que l'option "Télécharger uniquement en WiFi" est activée

<span style="color: #FF9800">**Quand**</span> l'utilisateur lance un téléchargement sur réseau mobile

<span style="color: #4CAF50">**Alors**</span> un avertissement s'affiche:

---

## 4. Gestion de l'espace de stockage

<span style="color: #2196F3">**Étant donné**</span> que l'appareil a 500 MB d'espace libre
<span style="color: #9E9E9E">**Et**</span> qu'un audio-guide pèse 380 MB

<span style="color: #FF9800">**Quand**</span> l'utilisateur lance le téléchargement

<span style="color: #4CAF50">**Alors**</span> un avertissement s'affiche:

---

## 5. Liste des audio-guides téléchargés

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a téléchargé 3 audio-guides

<span style="color: #FF9800">**Quand**</span> il accède à "Bibliothèque > Téléchargés"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide est téléchargé
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur active le mode avion

<span style="color: #FF9800">**Quand**</span> il lance l'audio-guide

<span style="color: #4CAF50">**Alors**</span> toutes les séquences sont lisibles
<span style="color: #9E9E9E">**Et**</span> les métadonnées sont accessibles
<span style="color: #9E9E9E">**Et**</span> les images s'affichent normalement
<span style="color: #9E9E9E">**Et**</span> la progression est sauvegardée localement

---

## 7. GPS fonctionne en mode avion (mode voiture)

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide voiture est téléchargé
<span style="color: #9E9E9E">**Et**</span> que le mode avion est activé (avec GPS actif)

<span style="color: #FF9800">**Quand**</span> l'utilisateur se déplace

<span style="color: #4CAF50">**Alors**</span> les déclenchements GPS fonctionnent normalement
<span style="color: #9E9E9E">**Et**</span> la distance/ETA sont calculés

---

## 8. Suppression d'audio-guide téléchargé

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide téléchargé pèse 380 MB

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "🗑️ Supprimer téléchargement"

<span style="color: #4CAF50">**Alors**</span> une confirmation s'affiche
<span style="color: #9E9E9E">**Et**</span> si confirmé, les 380 MB sont libérés
<span style="color: #9E9E9E">**Et**</span> l'audio-guide reste accessible en streaming

---

## 9. Mise à jour automatique si nouvelle version

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide téléchargé a été mis à jour par le créateur

<span style="color: #FF9800">**Quand**</span> l'utilisateur se connecte en WiFi

<span style="color: #4CAF50">**Alors**</span> une notification s'affiche:

---

## 10. Ajout d'audio-guide à une playlist

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur consulte un audio-guide

<span style="color: #FF9800">**Quand**</span> il clique sur "➕ Ajouter à une playlist"

<span style="color: #4CAF50">**Alors**</span> ses playlists s'affichent:

    | playlist |
    |---|
    | 🗺️ Voyages en France |
    | 🏛️ Musées parisiens |
    | + Créer nouvelle playlist |


---

## 11. Comportement audio-guide dans une playlist

<span style="color: #2196F3">**Étant donné**</span> une playlist contenant 2 audio-guides et 1 podcast

<span style="color: #FF9800">**Quand**</span> la lecture atteint un audio-guide

<span style="color: #4CAF50">**Alors**</span> l'audio-guide démarre à la séquence 1 (ou progression sauvegardée)
<span style="color: #9E9E9E">**Et**</span> les séquences se jouent normalement

<span style="color: #FF9800">**Quand**</span> l'audio-guide se termine (dernière séquence)

<span style="color: #4CAF50">**Alors**</span> le contenu suivant de la playlist démarre

---

## 12. Audio-guide marqué comme "Favori"

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur aime un audio-guide

<span style="color: #FF9800">**Quand**</span> il clique sur "⭐ Ajouter aux favoris"

<span style="color: #4CAF50">**Alors**</span> l'audio-guide est ajouté à la section "Favoris"
<span style="color: #9E9E9E">**Et**</span> il est facilement accessible depuis le menu principal

---

## 13. Collections thématiques d'audio-guides

<span style="color: #2196F3">**Étant donné**</span> que RoadWave propose des collections éditoriales

<span style="color: #FF9800">**Quand**</span> l'utilisateur accède à "Collections"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur consulte un audio-guide

<span style="color: #FF9800">**Quand**</span> il clique sur "⬆️ Partager"

<span style="color: #4CAF50">**Alors**</span> le menu de partage natif s'ouvre
<span style="color: #9E9E9E">**Et**</span> le lien généré est "https://roadwave.fr/share/ag/louvre_123"

---

## 15. Page web de partage pour audio-guide

<span style="color: #2196F3">**Étant donné**</span> qu'un lien d'audio-guide partagé est ouvert sur le web

<span style="color: #FF9800">**Quand**</span> la page se charge

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que l'app est installée
<span style="color: #9E9E9E">**Et**</span> qu'un lien "https://roadwave.fr/share/ag/louvre_123" est cliqué

<span style="color: #FF9800">**Quand**</span> le système détecte l'app

<span style="color: #4CAF50">**Alors**</span> l'app s'ouvre directement sur l'audio-guide
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut démarrer immédiatement

---

## 17. Partage avec séquence spécifique

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur est sur la séquence 5 "La Joconde"

<span style="color: #FF9800">**Quand**</span> il partage l'audio-guide

<span style="color: #4CAF50">**Alors**</span> le lien généré est "https://roadwave.fr/share/ag/louvre_123?seq=5"
<span style="color: #9E9E9E">**Et**</span> le destinataire est dirigé vers la séquence 5 directement

---

## 18. Note globale de l'audio-guide

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur termine un audio-guide

<span style="color: #FF9800">**Quand**</span> la dernière séquence se termine

<span style="color: #4CAF50">**Alors**</span> une popup de notation s'affiche:

---

## 19. Note moyenne affichée sur la page

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide a reçu 150 notes
<span style="color: #9E9E9E">**Et**</span> que la moyenne est 4.3/5

<span style="color: #FF9800">**Quand**</span> la page est affichée

<span style="color: #4CAF50">**Alors**</span> la note "⭐ 4.3 (150 avis)" est visible

---

## 20. Commentaires triés par pertinence

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide a 50 commentaires

<span style="color: #FF9800">**Quand**</span> l'utilisateur consulte les avis

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur laisse un commentaire négatif

<span style="color: #FF9800">**Quand**</span> le créateur consulte son dashboard

<span style="color: #4CAF50">**Alors**</span> il peut répondre au commentaire
<span style="color: #9E9E9E">**Et**</span> sa réponse apparaît en dessous avec badge "Créateur"

---

## 22. Audio-guides similaires recommandés

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur termine "Visite du Louvre"

<span style="color: #FF9800">**Quand**</span> il consulte les recommandations

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur est à Paris (GPS détecté)

<span style="color: #FF9800">**Quand**</span> il ouvre l'onglet "Audio-guides"

<span style="color: #4CAF50">**Alors**</span> les audio-guides parisiens sont mis en avant
<span style="color: #9E9E9E">**Et**</span> un filtre "🗺️ Autour de moi" est pré-appliqué

---

## 24. Badge "Populaire dans votre région"

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide a >100 écoutes dans la région Île-de-France
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur est en Île-de-France

<span style="color: #FF9800">**Quand**</span> l'audio-guide est affiché

<span style="color: #4CAF50">**Alors**</span> un badge "🔥 Populaire près de chez vous" est visible

---

## 25. Préchargement de la séquence suivante

<span style="color: #2196F3">**Étant donné**</span> que la séquence 3 est en cours à 2:30/3:42

<span style="color: #FF9800">**Quand**</span> il reste 60 secondes de lecture

<span style="color: #4CAF50">**Alors**</span> la séquence 4 est préchargée en arrière-plan
<span style="color: #9E9E9E">**Et**</span> la transition est instantanée (0 latence)

---

## 26. Buffer adaptatif selon connexion

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur est sur réseau 4G

<span style="color: #FF9800">**Quand**</span> la séquence démarre

<span style="color: #4CAF50">**Alors**</span> 30 secondes d'audio sont bufferisées initialement
<span style="color: #9E9E9E">**Et**</span> le buffering continue en arrière-plan

---

## 27. 📋 Plan: Buffer selon qualité réseau

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur est sur réseau <reseau>

<span style="color: #FF9800">**Quand**</span> une séquence démarre

<span style="color: #4CAF50">**Alors**</span> <buffer_secondes> secondes sont bufferisées

**📊 Exemples de données:**

| reseau | buffer_secondes |
|---|---|
| WiFi | 60 |
| 5G | 45 |
| 4G | 30 |
| 3G | 20 |

---

## 28. Compression audio adaptative

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur est sur connexion lente (3G)

<span style="color: #FF9800">**Quand**</span> une séquence est streamée

<span style="color: #4CAF50">**Alors**</span> le CDN sert la version 64 kbps (au lieu de 128 kbps)
<span style="color: #9E9E9E">**Et**</span> la qualité reste acceptable pour la voix

---

## 29. Cache intelligent des séquences jouées

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté les séquences 1-5

<span style="color: #FF9800">**Quand**</span> il clique sur "Précédent" pour réécouter la séquence 4

<span style="color: #4CAF50">**Alors**</span> la séquence 4 est chargée depuis le cache local
<span style="color: #9E9E9E">**Et**</span> le chargement est instantané (pas de stream)

---

## 30. Nettoyage automatique du cache

<span style="color: #2196F3">**Étant donné**</span> que le cache audio occupe 500 MB
<span style="color: #9E9E9E">**Et**</span> que la limite configurée est 300 MB

<span style="color: #FF9800">**Quand**</span> le nettoyage automatique s'exécute

<span style="color: #4CAF50">**Alors**</span> les séquences les plus anciennes (non téléchargées) sont supprimées
<span style="color: #9E9E9E">**Et**</span> le cache revient à 280 MB

---

## 31. Tracking des événements clés

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur écoute un audio-guide

<span style="color: #FF9800">**Quand**</span> il interagit avec l'application

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide a été écouté 1000 fois

<span style="color: #FF9800">**Quand**</span> le créateur consulte la heatmap

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un audio-guide voiture avec 8 points GPS

<span style="color: #FF9800">**Quand**</span> les statistiques sont calculées

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide avec seulement 1 séquence

<span style="color: #FF9800">**Quand**</span> il est publié

<span style="color: #4CAF50">**Alors**</span> un avertissement s'affiche:

---

## 35. Séquence manquante ou corrompue

<span style="color: #2196F3">**Étant donné**</span> qu'une séquence 5 a un fichier audio corrompu

<span style="color: #FF9800">**Quand**</span> l'utilisateur tente de la lire

<span style="color: #4CAF50">**Alors**</span> un message d'erreur s'affiche
<span style="color: #9E9E9E">**Et**</span> un bouton "⏭️ Passer à la suivante" est disponible
<span style="color: #9E9E9E">**Et**</span> le créateur reçoit une notification de l'erreur

---

## 36. GPS désactivé puis réactivé en cours de route

<span style="color: #2196F3">**Étant donné**</span> un audio-guide voiture en cours
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur désactive le GPS

<span style="color: #FF9800">**Quand**</span> il le réactive 10 minutes plus tard

<span style="color: #4CAF50">**Alors**</span> le déclenchement automatique reprend
<span style="color: #9E9E9E">**Et**</span> les points GPS manqués entre-temps ne déclenchent pas de popup

---

## 37. Modification d'audio-guide avec utilisateurs en cours

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide a 50 utilisateurs en cours d'écoute

<span style="color: #FF9800">**Quand**</span> le créateur modifie une séquence

<span style="color: #4CAF50">**Alors**</span> les utilisateurs actuels conservent l'ancienne version
<span style="color: #9E9E9E">**Et**</span> les nouveaux utilisateurs obtiennent la nouvelle version
<span style="color: #9E9E9E">**Et**</span> un message informe les utilisateurs lors de la prochaine ouverture

---

## 38. Suppression d'audio-guide par le créateur

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide a 20 utilisateurs avec progression

<span style="color: #FF9800">**Quand**</span> le créateur supprime l'audio-guide

<span style="color: #4CAF50">**Alors**</span> une confirmation stricte est demandée
<span style="color: #9E9E9E">**Et**</span> si confirmé, les progressions utilisateurs sont archivées (30 jours)
<span style="color: #9E9E9E">**Et**</span> l'audio-guide devient inaccessible

---

## 39. Signalement d'audio-guide pour contenu inapproprié

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur signale un audio-guide

<span style="color: #FF9800">**Quand**</span> le signalement est modéré
<span style="color: #9E9E9E">**Et**</span> jugé valide

<span style="color: #4CAF50">**Alors**</span> l'audio-guide est dépublié temporairement
<span style="color: #9E9E9E">**Et**</span> le créateur reçoit une notification d'explication
<span style="color: #9E9E9E">**Et**</span> il peut corriger puis republier

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que l'utilisateur "jean@example.com" est connecté (gratuit)
> <span style="color: #9E9E9E">**Et**</span> qu'un audio-guide piéton "Visite du Louvre" est disponible avec 12 séquences
## 1. Fin de séquence normale avec pause automatique

<span style="color: #2196F3">**Étant donné**</span> que la séquence 1 "Introduction" est en cours de lecture

<span style="color: #FF9800">**Quand**</span> la séquence se termine à 2:15

<span style="color: #4CAF50">**Alors**</span> le player se met en pause automatiquement
<span style="color: #9E9E9E">**Et**</span> le message suivant s'affiche: "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt."
<span style="color: #9E9E9E">**Et**</span> la barre de progression indique "1/12 complétée"

---

## 2. Passage manuel à la séquence suivante

<span style="color: #2196F3">**Étant donné**</span> que la séquence 1 est terminée et le player en pause

<span style="color: #FF9800">**Quand**</span> l'utilisateur appuie sur le bouton [▶|] "Suivant"

<span style="color: #4CAF50">**Alors**</span> la séquence 2 "Pyramide du Louvre" démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> aucune latence n'est observée

---

## 3. Séquence avec publicité (1/5 séquences)

<span style="color: #2196F3">**Étant donné**</span> que la séquence 5 se termine
<span style="color: #9E9E9E">**Et**</span> que c'est la 5ème séquence (1 pub toutes les 5)

<span style="color: #FF9800">**Quand**</span> la séquence se termine

<span style="color: #4CAF50">**Alors**</span> la publicité s'enchaîne automatiquement (sans attente bouton)
<span style="color: #9E9E9E">**Et**</span> la publicité se lit normalement
<span style="color: #9E9E9E">**Et**</span> elle est skippable après 5 secondes

---

## 4. Fin de publicité avec pause automatique

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité est en cours de lecture

<span style="color: #FF9800">**Quand**</span> la publicité se termine

<span style="color: #4CAF50">**Alors**</span> le player se met en pause automatiquement
<span style="color: #9E9E9E">**Et**</span> le message suivant s'affiche: "Séquence 6 prête. Appuyez sur Suivant."
<span style="color: #9E9E9E">**Et**</span> l'utilisateur doit cliquer sur [▶|] pour continuer

---

## 5. Flux complet séquence → pub → séquence

<span style="color: #2196F3">**Étant donné**</span> que la séquence 5 démarre

<span style="color: #FF9800">**Quand**</span> la séquence 5 se termine

<span style="color: #4CAF50">**Alors**</span> la publicité démarre automatiquement

<span style="color: #FF9800">**Quand**</span> la publicité se termine

<span style="color: #4CAF50">**Alors**</span> le player se met en pause

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶|]

<span style="color: #4CAF50">**Alors**</span> la séquence 6 démarre

---

## 6. 📋 Plan: Fréquence de publicité configurable

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur gratuit écoute un audio-guide
<span style="color: #9E9E9E">**Et**</span> que la fréquence pub est configurée à <frequence>

<span style="color: #FF9800">**Quand**</span> il termine la séquence <numero_sequence>

<span style="color: #4CAF50">**Alors**</span> une publicité est insérée : <pub_inseree>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur "premium@example.com" est abonné Premium
<span style="color: #9E9E9E">**Et**</span> qu'il écoute un audio-guide piéton

<span style="color: #FF9800">**Quand**</span> il termine la séquence 5

<span style="color: #4CAF50">**Alors**</span> aucune publicité n'est insérée
<span style="color: #9E9E9E">**Et**</span> le player se met en pause immédiatement
<span style="color: #9E9E9E">**Et**</span> le message "Séquence 6 prête. Appuyez sur Suivant." s'affiche

---

## 8. Boutons de contrôle disponibles en mode piéton

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide piéton est en lecture

<span style="color: #FF9800">**Quand**</span> l'utilisateur consulte les contrôles

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la séquence 3 "La Joconde" est en cours à 1:42/3:42

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶|] "Suivant"

<span style="color: #4CAF50">**Alors**</span> la séquence 4 "Vénus de Milo" démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> la séquence 3 n'est pas marquée comme écoutée (car <80%)

---

## 10. Retour à la séquence précédente (saut direct)

<span style="color: #2196F3">**Étant donné**</span> que la séquence 5 est en cours de lecture

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [|◀] "Précédent"

<span style="color: #4CAF50">**Alors**</span> la séquence 4 démarre depuis le début (0:00)
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de logique "replay si >10s" (contrairement au contenu classique)

---

## 11. Pause et reprise pendant une séquence

<span style="color: #2196F3">**Étant donné**</span> que la séquence 2 est en cours à 1:15/1:48

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [⏸️] "Pause"

<span style="color: #4CAF50">**Alors**</span> la lecture se met en pause
<span style="color: #9E9E9E">**Et**</span> la position 1:15 est conservée

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶️] "Play"

<span style="color: #4CAF50">**Alors**</span> la lecture reprend exactement à 1:15

---

## 12. Interface liste des séquences

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide de 12 séquences est en cours

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [📋] "Liste séquences"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la séquence 3 est en cours à 1:22/3:42

<span style="color: #FF9800">**Quand**</span> la liste des séquences est affichée

<span style="color: #4CAF50">**Alors**</span> la séquence 3 affiche:

---

## 14. Navigation libre vers séquence non encore écoutée

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur est sur la séquence 3
<span style="color: #9E9E9E">**Et**</span> que les séquences 4 à 12 n'ont pas été écoutées

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "8. Les Appartements de Napoléon"

<span style="color: #4CAF50">**Alors**</span> la séquence 8 démarre immédiatement depuis 0:00
<span style="color: #9E9E9E">**Et**</span> les séquences 4 à 7 restent marquées ⭕ "À écouter"

---

## 15. Retour à une séquence déjà écoutée

<span style="color: #2196F3">**Étant donné**</span> que la séquence 2 "Pyramide du Louvre" a été écoutée à 100%
<span style="color: #9E9E9E">**Et**</span> qu'elle est marquée ✅ "Écouté"

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique dessus dans la liste

<span style="color: #4CAF50">**Alors**</span> la séquence 2 démarre depuis 0:00
<span style="color: #9E9E9E">**Et**</span> le statut ✅ est conservé

---

## 16. Checkmarks sur séquences écoutées >80%

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur écoute la séquence 2 de durée 1:48

<span style="color: #FF9800">**Quand**</span> il écoute jusqu'à 1:30 (83% de complétion)
<span style="color: #9E9E9E">**Et**</span> qu'il passe à la séquence suivante

<span style="color: #4CAF50">**Alors**</span> la séquence 2 est marquée ✅ "Écouté"
<span style="color: #9E9E9E">**Et**</span> la date d'écoute est enregistrée

---

## 17. Pas de checkmark si séquence écoutée <80%

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur écoute la séquence 3 de durée 3:42

<span style="color: #FF9800">**Quand**</span> il écoute jusqu'à 1:30 (40% de complétion)
<span style="color: #9E9E9E">**Et**</span> qu'il passe à la séquence suivante

<span style="color: #4CAF50">**Alors**</span> la séquence 3 reste marquée ⭕ "À écouter"

---

## 18. Bouton "Tout afficher" si plus de 6 séquences

<span style="color: #2196F3">**Étant donné**</span> un audio-guide avec 12 séquences

<span style="color: #FF9800">**Quand**</span> la liste est affichée

<span style="color: #4CAF50">**Alors**</span> seules les 6 premières séquences sont visibles initialement
<span style="color: #9E9E9E">**Et**</span> un bouton "Tout afficher ▼" est présent

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "Tout afficher ▼"

<span style="color: #4CAF50">**Alors**</span> les 6 séquences restantes sont affichées

---

## 19. Saut vers séquence spécifique depuis la barre de progression

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide est en cours

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "3/12" dans la barre de progression

<span style="color: #4CAF50">**Alors**</span> la liste des séquences s'ouvre
<span style="color: #9E9E9E">**Et**</span> la séquence en cours (3) est mise en surbrillance

---

## 20. Position exacte sauvegardée automatiquement

<span style="color: #2196F3">**Étant donné**</span> que la séquence 5 est en cours à 2:34/4:10

<span style="color: #FF9800">**Quand**</span> l'utilisateur quitte l'application

<span style="color: #4CAF50">**Alors**</span> la position 2:34 dans la séquence 5 est sauvegardée
<span style="color: #9E9E9E">**Et**</span> la sauvegarde est effectuée localement (SQLite)
<span style="color: #9E9E9E">**Et**</span> la sauvegarde est synchronisée sur le cloud (PostgreSQL)

---

## 21. Reprise après fermeture de l'application

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a quitté l'app à la séquence 5 position 2:34

<span style="color: #FF9800">**Quand**</span> il rouvre l'audio-guide

<span style="color: #4CAF50">**Alors**</span> une popup de reprise s'affiche

<span style="color: #FF9800">**Quand**</span> il clique sur "▶️ Reprendre"

<span style="color: #4CAF50">**Alors**</span> la lecture reprend à la séquence 5 position 2:34 exacte

---

## 22. Visiteur qui connaît déjà certaines œuvres

<span style="color: #2196F3">**Étant donné**</span> qu'un visiteur du Louvre démarre l'audio-guide
<span style="color: #9E9E9E">**Et**</span> qu'il connaît déjà "La Joconde" (séquence 3)

<span style="color: #FF9800">**Quand**</span> il arrive à la séquence 3
<span style="color: #9E9E9E">**Et**</span> qu'il clique sur [▶|] "Suivant" après 10 secondes

<span style="color: #4CAF50">**Alors**</span> la séquence 4 démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> la séquence 3 n'est pas marquée comme écoutée

---

## 23. Visiteur qui veut voir une œuvre éloignée

<span style="color: #2196F3">**Étant donné**</span> qu'un visiteur est à la séquence 2
<span style="color: #9E9E9E">**Et**</span> qu'il aperçoit "La Victoire de Samothrace" (séquence 8) physiquement

<span style="color: #FF9800">**Quand**</span> il ouvre la liste et clique sur la séquence 8

<span style="color: #4CAF50">**Alors**</span> la séquence 8 démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> qu'un visiteur écoute la séquence 6

<span style="color: #FF9800">**Quand**</span> il clique sur [⏸️] "Pause"
<span style="color: #9E9E9E">**Et**</span> qu'il ferme l'application pendant 30 minutes
<span style="color: #FF9800">**Quand**</span> il rouvre l'application

<span style="color: #4CAF50">**Alors**</span> la séquence 6 reprend à la position exacte où il s'était arrêté

---

## 25. Visiteur qui revient le lendemain

<span style="color: #2196F3">**Étant donné**</span> qu'un visiteur a écouté les séquences 1-5 hier
<span style="color: #9E9E9E">**Et**</span> qu'il revient au musée aujourd'hui

<span style="color: #FF9800">**Quand**</span> il ouvre l'audio-guide

<span style="color: #4CAF50">**Alors**</span> une popup propose "▶️ Reprendre" (séquence 6)
<span style="color: #9E9E9E">**Et**</span> les séquences 1-5 sont marquées ✅ "Écouté"

---

## 26. Séquence audio corrompue ou indisponible

<span style="color: #2196F3">**Étant donné**</span> que la séquence 7 a un fichier audio corrompu

<span style="color: #FF9800">**Quand**</span> l'utilisateur tente de la lire

<span style="color: #4CAF50">**Alors**</span> un message d'erreur s'affiche:

---

## 27. Perte de connexion pendant le chargement

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur lance la séquence 4
<span style="color: #9E9E9E">**Et**</span> que la connexion réseau est perdue

<span style="color: #FF9800">**Quand**</span> le chargement échoue

<span style="color: #4CAF50">**Alors**</span> un message s'affiche: "Connexion perdue. Vérifiez votre réseau."
<span style="color: #9E9E9E">**Et**</span> un bouton "🔄 Réessayer" est disponible

---

## 28. Batterie faible en cours de visite

<span style="color: #2196F3">**Étant donné**</span> que la batterie de l'appareil est à 5%

<span style="color: #FF9800">**Quand**</span> l'utilisateur écoute une séquence

<span style="color: #4CAF50">**Alors**</span> une notification système s'affiche: "Batterie faible. Progression sauvegardée."
<span style="color: #9E9E9E">**Et**</span> la position est sauvegardée localement toutes les 10 secondes

---

## 29. Mode piéton sans points GPS (pas d'alerte localisation)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en mode piéton
<span style="color: #9E9E9E">**Et**</span> que le GPS est désactivé

<span style="color: #FF9800">**Quand**</span> l'utilisateur démarre l'audio-guide

<span style="color: #4CAF50">**Alors**</span> aucune alerte GPS ne s'affiche
<span style="color: #9E9E9E">**Et**</span> l'audio-guide fonctionne normalement (navigation 100% manuelle)

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que l'utilisateur "jean@example.com" est connecté (gratuit)
> <span style="color: #9E9E9E">**Et**</span> qu'un audio-guide voiture "Safari du Paugre" est disponible avec 8 séquences
> <span style="color: #9E9E9E">**Et**</span> que le GPS est activé
## 1. Distinction audio-guides vs contenus géolocalisés simples

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur est en mode voiture

<span style="color: #FF9800">**Quand**</span> il écoute un contenu géolocalisé simple (1 séquence unique)

<span style="color: #4CAF50">**Alors**</span> une notification avec compteur 7→1 est affichée 7s avant le point
<span style="color: #9E9E9E">**Et**</span> il doit valider avec "Suivant" + décompte 5s
<span style="color: #9E9E9E">**Et**</span> ce contenu compte 1/6 dans le quota horaire

<span style="color: #FF9800">**Quand**</span> il démarre un audio-guide multi-séquences

<span style="color: #4CAF50">**Alors**</span> les séquences se déclenchent au point GPS exact (rayon 30m)
<span style="color: #9E9E9E">**Et**</span> aucun compteur 7s n'est affiché (juste notification "Ding" + toast 2s)
<span style="color: #9E9E9E">**Et**</span> l'audio-guide entier compte 1/6 dans le quota

---

## 2. Démarrage automatique au premier point GPS

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur démarre l'audio-guide "Safari du Paugre"
<span style="color: #9E9E9E">**Et**</span> que le point de départ est à (43.1234, 2.5678) avec rayon 30m

<span style="color: #FF9800">**Quand**</span> l'utilisateur entre dans le rayon de 30m

<span style="color: #4CAF50">**Alors**</span> la séquence 1 "Introduction - Point d'accueil" démarre automatiquement
<span style="color: #9E9E9E">**Et**</span> une notification sonore "Ding" est jouée (non intrusif)
<span style="color: #9E9E9E">**Et**</span> un toast s'affiche brièvement pendant 2s: "Introduction - Point d'accueil"
<span style="color: #9E9E9E">**Et**</span> aucun compteur 7→1 n'est affiché (contrairement aux contenus géolocalisés simples)

---

## 3. Déclenchement automatique séquence suivante

<span style="color: #2196F3">**Étant donné**</span> que la séquence 1 est terminée
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur se déplace vers le point GPS 2 (43.1245, 2.5690)

<span style="color: #FF9800">**Quand**</span> l'utilisateur entre dans le rayon de 30m du point 2

<span style="color: #4CAF50">**Alors**</span> la séquence 2 "Enclos des lions" démarre automatiquement
<span style="color: #9E9E9E">**Et**</span> une notification "Ding" + toast "Enclos des lions" s'affiche

---

## 4. Navigation manuelle conservée (bouton Suivant actif)

<span style="color: #2196F3">**Étant donné**</span> que la séquence 1 est en cours
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur est encore loin du point GPS 2 (distance 500m)

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶|] "Suivant"

<span style="color: #4CAF50">**Alors**</span> la séquence 2 démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> aucune vérification GPS n'est effectuée

---

## 5. Navigation manuelle conservée (bouton Précédent actif)

<span style="color: #2196F3">**Étant donné**</span> que la séquence 3 est en cours

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [|◀] "Précédent"

<span style="color: #4CAF50">**Alors**</span> la séquence 2 démarre depuis le début
<span style="color: #9E9E9E">**Et**</span> aucune vérification GPS n'est effectuée

---

## 6. Tous les boutons de contrôle restent actifs

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide voiture est en cours

<span style="color: #FF9800">**Quand**</span> l'utilisateur consulte les contrôles

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que la séquence 3 "Enclos des girafes" est terminée
<span style="color: #9E9E9E">**Et**</span> que le point GPS 4 est à 2 km de distance (embouteillage)

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique manuellement sur [▶|] "Suivant"

<span style="color: #4CAF50">**Alors**</span> la séquence 4 démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut continuer l'expérience sans attendre d'atteindre le point GPS

---

## 8. Use case - Route fermée (point GPS inaccessible)

<span style="color: #2196F3">**Étant donné**</span> que le point GPS 5 est sur une route fermée
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur ne peut pas s'en approcher

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶|] "Suivant"

<span style="color: #4CAF50">**Alors**</span> la séquence 5 démarre quand même
<span style="color: #9E9E9E">**Et**</span> l'audio-guide continue normalement

---

## 9. Use case - Passager manipule l'application

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur est passager (non conducteur)
<span style="color: #9E9E9E">**Et**</span> que la vitesse du véhicule est 45 km/h

<span style="color: #FF9800">**Quand**</span> le passager clique sur [▶|] "Suivant"

<span style="color: #4CAF50">**Alors**</span> la séquence suivante démarre
<span style="color: #9E9E9E">**Et**</span> un avertissement s'affiche pendant 3 secondes

---

## 10. Avertissement sécurité si vitesse >10 km/h

<span style="color: #2196F3">**Étant donné**</span> que la vitesse actuelle est 35 km/h

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur un bouton (Suivant ou Précédent)

<span style="color: #4CAF50">**Alors**</span> l'action est exécutée immédiatement (pas de blocage)
<span style="color: #9E9E9E">**Et**</span> un toast s'affiche pendant 3 secondes:

---

## 11. 📋 Plan: Avertissement selon la vitesse

<span style="color: #2196F3">**Étant donné**</span> que la vitesse actuelle est <vitesse> km/h

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur un bouton de navigation

<span style="color: #4CAF50">**Alors**</span> l'avertissement est affiché : <avertissement>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que la séquence 2 "Les lions" vient de se terminer
<span style="color: #9E9E9E">**Et**</span> que le prochain point GPS 3 "Enclos des girafes" est à 500m

<span style="color: #FF9800">**Quand**</span> l'interface bascule en mode "attente prochain point"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la distance initiale vers le prochain point était 500m
<span style="color: #9E9E9E">**Et**</span> que la séquence précédente est terminée

<span style="color: #FF9800">**Quand**</span> l'utilisateur se rapproche du prochain point
<span style="color: #9E9E9E">**Et**</span> que la distance actuelle est 175m

<span style="color: #4CAF50">**Alors**</span> la progress bar affiche "65%" remplie
<span style="color: #9E9E9E">**Et**</span> le calcul est: 100 - (175 / 500 * 100) = 65%
<span style="color: #9E9E9E">**Et**</span> la barre se met à jour chaque seconde

---

## 14. Bouton "Rejouer séq." pour réécouter

<span style="color: #2196F3">**Étant donné**</span> que la séquence 3 vient de se terminer
<span style="color: #9E9E9E">**Et**</span> que l'interface "attente prochain point" est affichée

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶️ Rejouer séq.]

<span style="color: #4CAF50">**Alors**</span> la séquence 3 redémarre depuis 0:00
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut la réécouter (utile si distraction)

---

## 15. Interface en conduite avec distance et ETA

<span style="color: #2196F3">**Étant donné**</span> que la séquence 2 est en cours
<span style="color: #9E9E9E">**Et**</span> que le prochain point GPS 3 "Enclos des girafes" est à 320m
<span style="color: #9E9E9E">**Et**</span> que la vitesse actuelle est 28 km/h

<span style="color: #FF9800">**Quand**</span> l'interface est affichée

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la distance au prochain point est 500m

<span style="color: #FF9800">**Quand**</span> 10 secondes s'écoulent et que l'utilisateur se rapproche

<span style="color: #4CAF50">**Alors**</span> la distance est mise à jour chaque seconde
<span style="color: #9E9E9E">**Et**</span> la nouvelle distance "450m" s'affiche

---

## 17. Mise à jour de l'ETA en temps réel

<span style="color: #2196F3">**Étant donné**</span> que l'ETA est "≈ 2 minutes"
<span style="color: #9E9E9E">**Et**</span> que la vitesse est constante à 30 km/h

<span style="color: #FF9800">**Quand**</span> l'utilisateur se rapproche du point

<span style="color: #4CAF50">**Alors**</span> l'ETA est recalculé chaque seconde
<span style="color: #9E9E9E">**Et**</span> il diminue progressivement: "≈ 1 minute 50", "≈ 1 minute 40", etc.

---

## 18. 📋 Plan: Format d'affichage de la distance

<span style="color: #2196F3">**Étant donné**</span> que la distance au prochain point est <distance_metres>

<span style="color: #FF9800">**Quand**</span> l'interface est mise à jour

<span style="color: #4CAF50">**Alors**</span> la distance affichée est "<affichage>"

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que l'ETA calculé est <secondes> secondes

<span style="color: #FF9800">**Quand**</span> l'interface est mise à jour

<span style="color: #4CAF50">**Alors**</span> l'ETA affiché est "<affichage>"

**📊 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)

<span style="color: #2196F3">**Étant donné**</span> que la position actuelle est (43.1234, 2.5678)
<span style="color: #9E9E9E">**Et**</span> que le prochain point est au nord-est (angle 45°)

<span style="color: #FF9800">**Quand**</span> la direction est calculée

<span style="color: #4CAF50">**Alors**</span> la flèche "↗" est affichée

---

## 21. 📋 Plan: Flèches de direction selon l'angle

<span style="color: #2196F3">**Étant donné**</span> que l'angle vers le prochain point est <angle>°

<span style="color: #FF9800">**Quand**</span> la direction est calculée

<span style="color: #4CAF50">**Alors**</span> la flèche "<fleche>" 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

<span style="color: #2196F3">**Étant donné**</span> que la direction actuelle est ↑ (nord)
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur tourne vers l'est

<span style="color: #FF9800">**Quand**</span> 5 secondes s'écoulent

<span style="color: #4CAF50">**Alors**</span> la direction est recalculée
<span style="color: #9E9E9E">**Et**</span> la nouvelle flèche ↗ (nord-est) s'affiche

---

## 23. Message "En attente de déplacement" si vitesse <5 km/h

<span style="color: #2196F3">**Étant donné**</span> que la vitesse actuelle est 2 km/h (arrêté)

<span style="color: #FF9800">**Quand**</span> l'ETA est calculé

<span style="color: #4CAF50">**Alors**</span> le message "En attente de déplacement" s'affiche
<span style="color: #9E9E9E">**Et**</span> l'ETA n'est pas calculé (car vitesse insuffisante)

---

## 24. Simplicité de l'interface (pas de carte miniature)

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide voiture est en cours

<span style="color: #FF9800">**Quand**</span> l'interface est affichée

<span style="color: #4CAF50">**Alors**</span> aucune carte miniature n'est présente
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un audio-guide voiture

<span style="color: #FF9800">**Quand**</span> un point GPS est défini

<span style="color: #4CAF50">**Alors**</span> le rayon de déclenchement est 30 mètres par défaut
<span style="color: #9E9E9E">**Et**</span> le rayon de tolérance "point manqué" est 100 mètres

---

## 26. Déclenchement dans le rayon (30m)

<span style="color: #2196F3">**Étant donné**</span> que le point GPS 3 est défini avec rayon 30m

<span style="color: #FF9800">**Quand**</span> l'utilisateur entre à 25m du point

<span style="color: #4CAF50">**Alors**</span> la séquence 3 se déclenche automatiquement

---

## 27. Pas de déclenchement hors rayon

<span style="color: #2196F3">**Étant donné**</span> que le point GPS 3 a un rayon de 30m

<span style="color: #FF9800">**Quand**</span> l'utilisateur passe à 45m du point

<span style="color: #4CAF50">**Alors**</span> la séquence 3 ne se déclenche pas automatiquement

---

## 28. Point manqué dans rayon de tolérance (100m)

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur passe à 60m du point GPS 4 (hors rayon 30m)
<span style="color: #9E9E9E">**Et**</span> que 60m < 100m (rayon tolérance)

<span style="color: #FF9800">**Quand**</span> le point est détecté comme manqué

<span style="color: #4CAF50">**Alors**</span> un toast s'affiche: "⚠️ Point manqué : Enclos des éléphants"
<span style="color: #9E9E9E">**Et**</span> une popup s'affiche pendant 5 secondes avec 3 options

---

## 29. Popup "Point manqué" avec 3 actions

<span style="color: #2196F3">**Étant donné**</span> qu'un point GPS a été manqué (distance 60m)

<span style="color: #FF9800">**Quand**</span> la popup s'affiche

<span style="color: #4CAF50">**Alors**</span> 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"

<span style="color: #2196F3">**Étant donné**</span> qu'un point GPS est manqué

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "🔊 Écouter quand même"

<span style="color: #4CAF50">**Alors**</span> la séquence correspondante démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut continuer sa route

---

## 31. Action "Passer au suivant"

<span style="color: #2196F3">**Étant donné**</span> qu'un point GPS 5 est manqué

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "⏭️ Passer au suivant"

<span style="color: #4CAF50">**Alors**</span> la séquence 5 est ignorée (non écoutée)
<span style="color: #9E9E9E">**Et**</span> l'application attend le point GPS 6
<span style="color: #9E9E9E">**Et**</span> la distance vers le point 6 s'affiche

---

## 32. Action "Faire demi-tour"

<span style="color: #2196F3">**Étant donné**</span> qu'un point GPS est manqué à (43.1250, 2.5700)

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "🔙 Faire demi-tour"

<span style="color: #4CAF50">**Alors**</span> l'application détecte l'app GPS installée (Google Maps ou Waze)
<span style="color: #9E9E9E">**Et**</span> ouvre la navigation GPS externe vers (43.1250, 2.5700)

---

## 33. Point manqué au-delà du rayon de tolérance (>100m)

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur passe à 150m du point GPS 6

<span style="color: #FF9800">**Quand**</span> la distance est détectée

<span style="color: #4CAF50">**Alors**</span> aucune popup ne s'affiche (point trop loin)
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut naviguer manuellement avec [▶|]

---

## 34. 📋 Plan: Gestion selon la distance au point

<span style="color: #2196F3">**Étant donné**</span> un point GPS avec rayon 30m et tolérance 100m

<span style="color: #FF9800">**Quand**</span> l'utilisateur passe à <distance> du point

<span style="color: #4CAF50">**Alors**</span> le comportement est <comportement>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur définit un rayon de 50m (au lieu de 30m)

<span style="color: #FF9800">**Quand**</span> un utilisateur entre à 45m du point

<span style="color: #4CAF50">**Alors**</span> la séquence se déclenche automatiquement
<span style="color: #9E9E9E">**Et**</span> le rayon personnalisé est respecté

---

## 36. Rayon minimum et maximum configurables

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur configure un rayon

<span style="color: #FF9800">**Quand**</span> il ajuste le curseur

<span style="color: #4CAF50">**Alors**</span> les valeurs disponibles sont de 10m à 200m
<span style="color: #9E9E9E">**Et**</span> le rayon par défaut suggéré est 30m pour la voiture

---

## 37. Safari-parc avec déclenchement automatique fluide

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur roule dans un safari à 20 km/h

<span style="color: #FF9800">**Quand**</span> il passe devant "Enclos des lions" (point GPS 2)

<span style="color: #4CAF50">**Alors**</span> la séquence 2 démarre automatiquement sans intervention
<span style="color: #9E9E9E">**Et**</span> il peut se concentrer sur la conduite et l'observation

---

## 38. Détour imprévu (travaux sur la route)

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur prend un détour à cause de travaux
<span style="color: #9E9E9E">**Et**</span> que le point GPS 4 devient inaccessible

<span style="color: #FF9800">**Quand**</span> il est loin du point (>100m)
<span style="color: #9E9E9E">**Et**</span> qu'il clique manuellement sur [▶|]

<span style="color: #4CAF50">**Alors**</span> la séquence 4 démarre quand même
<span style="color: #9E9E9E">**Et**</span> l'expérience continue sans blocage

---

## 39. Passager qui navigue librement

<span style="color: #2196F3">**Étant donné**</span> qu'un passager utilise l'application
<span style="color: #9E9E9E">**Et**</span> que le conducteur roule à 50 km/h

<span style="color: #FF9800">**Quand**</span> le passager clique sur "Précédent" pour réécouter

<span style="color: #4CAF50">**Alors**</span> l'action est exécutée immédiatement
<span style="color: #9E9E9E">**Et**</span> un warning apparaît brièvement (sensibilisation)

---

## 40. Embouteillage prolongé

<span style="color: #2196F3">**Étant donné**</span> que la séquence 3 est terminée depuis 10 minutes
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur est bloqué dans un embouteillage
<span style="color: #9E9E9E">**Et**</span> que le point GPS 4 est encore à 1.5 km

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶|]

<span style="color: #4CAF50">**Alors**</span> la séquence 4 démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut passer le temps en écoutant

---

## 41. GPS désactivé en mode voiture

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide voiture est démarré
<span style="color: #9E9E9E">**Et**</span> que le GPS est désactivé

<span style="color: #FF9800">**Quand**</span> l'application détecte l'absence de GPS

<span style="color: #4CAF50">**Alors**</span> une alerte s'affiche:

---

## 42. Action "Passer en mode Manuel"

<span style="color: #2196F3">**Étant donné**</span> que le GPS est désactivé

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "Passer en mode Manuel"

<span style="color: #4CAF50">**Alors**</span> l'audio-guide bascule en navigation 100% manuelle
<span style="color: #9E9E9E">**Et**</span> les boutons [▶|] et [|◀] permettent de naviguer
<span style="color: #9E9E9E">**Et**</span> aucun déclenchement GPS n'est tenté

---

## 43. Précision GPS insuffisante

<span style="color: #2196F3">**Étant donné**</span> que le signal GPS a une précision de ±150m

<span style="color: #FF9800">**Quand**</span> l'utilisateur approche d'un point GPS avec rayon 30m

<span style="color: #4CAF50">**Alors**</span> un avertissement s'affiche:

---

## 44. Perte signal GPS en cours de route

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide voiture est en cours

<span style="color: #FF9800">**Quand**</span> le signal GPS est perdu (tunnel, parking souterrain)

<span style="color: #4CAF50">**Alors**</span> un toast s'affiche: "Signal GPS perdu. Navigation manuelle active."
<span style="color: #9E9E9E">**Et**</span> les boutons de navigation restent actifs

<span style="color: #FF9800">**Quand**</span> le signal GPS revient

<span style="color: #4CAF50">**Alors**</span> un toast s'affiche: "Signal GPS rétabli"
<span style="color: #9E9E9E">**Et**</span> le déclenchement automatique est réactivé

---

## 45. Dépassement de la vitesse recommandée

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide recommande 20-30 km/h
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur roule à 65 km/h

<span style="color: #FF9800">**Quand**</span> la vitesse est détectée

<span style="color: #4CAF50">**Alors**</span> l'affichage vitesse est en orange: "⚠️ 65 km/h"
<span style="color: #9E9E9E">**Et**</span> un message info s'affiche: "Vitesse élevée. Risque de manquer des points."

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que l'utilisateur "jean@example.com" est connecté
> <span style="color: #9E9E9E">**Et**</span> que le GPS est activé
## 1. 📋 Plan: Paramètres par mode de déplacement

<span style="color: #2196F3">**Étant donné**</span> un audio-guide configuré en mode <mode>

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide vélo "Circuit des châteaux de la Loire"
<span style="color: #9E9E9E">**Et**</span> que le point GPS 3 a un rayon de 50m

<span style="color: #FF9800">**Quand**</span> l'utilisateur à vélo entre à 45m du point

<span style="color: #4CAF50">**Alors**</span> la séquence 3 "Château de Chambord" se déclenche automatiquement

---

## 3. Rayon plus large justifié pour le vélo

<span style="color: #2196F3">**Étant donné**</span> qu'un cycliste roule sur piste cyclable
<span style="color: #9E9E9E">**Et**</span> que sa vitesse varie entre 8 et 22 km/h (arrêts fréquents)
<span style="color: #9E9E9E">**Et**</span> que le tracé est moins prévisible qu'en voiture

<span style="color: #FF9800">**Quand**</span> un point GPS avec rayon 50m est défini

<span style="color: #4CAF50">**Alors**</span> le rayon plus large compense la variabilité de trajectoire

---

## 4. Warning sécurité dès 5 km/h en vélo

<span style="color: #2196F3">**Étant donné**</span> un audio-guide vélo en cours
<span style="color: #9E9E9E">**Et**</span> que la vitesse actuelle est 12 km/h

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶|] "Suivant"

<span style="color: #4CAF50">**Alors**</span> l'action est exécutée
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la vitesse actuelle à vélo est <vitesse> km/h

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur un bouton de navigation

<span style="color: #4CAF50">**Alors**</span> le warning est affiché : <warning>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> qu'un cycliste passe à 65m du point GPS 4
<span style="color: #9E9E9E">**Et**</span> que le rayon de déclenchement est 50m
<span style="color: #9E9E9E">**Et**</span> que le rayon de tolérance est 75m

<span style="color: #FF9800">**Quand**</span> la distance est détectée

<span style="color: #4CAF50">**Alors**</span> la popup "Point manqué" s'affiche avec 3 options
<span style="color: #9E9E9E">**Et**</span> le système tolère l'écart (trajectoire vélo moins prévisible)

---

## 7. Affichage adapté au vélo

<span style="color: #2196F3">**Étant donné**</span> un audio-guide vélo en cours

<span style="color: #FF9800">**Quand**</span> l'interface est affichée

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un cycliste suit un circuit nature
<span style="color: #9E9E9E">**Et**</span> qu'il s'arrête régulièrement (feux, photos, fatigue)

<span style="color: #FF9800">**Quand**</span> il s'arrête à 40m d'un point GPS (rayon 50m)

<span style="color: #4CAF50">**Alors**</span> la séquence se déclenche automatiquement
<span style="color: #9E9E9E">**Et**</span> le rayon large permet le déclenchement malgré l'arrêt

---

## 9. Cas d'usage - Circulation mixte piétons/vélos

<span style="color: #2196F3">**Étant donné**</span> qu'un cycliste roule sur voie partagée
<span style="color: #9E9E9E">**Et**</span> qu'il doit ralentir fréquemment pour éviter les piétons

<span style="color: #FF9800">**Quand**</span> sa vitesse varie entre 5 et 20 km/h

<span style="color: #4CAF50">**Alors**</span> le système s'adapte avec le rayon 50m
<span style="color: #9E9E9E">**Et**</span> le déclenchement reste fiable

---

## 10. Déclenchement automatique avec rayon 100m (mode transport)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide transport "Ligne touristique Paris"
<span style="color: #9E9E9E">**Et**</span> que le point GPS "Tour Eiffel" a un rayon de 100m

<span style="color: #FF9800">**Quand**</span> le bus touristique entre à 85m du point

<span style="color: #4CAF50">**Alors**</span> la séquence "Tour Eiffel" se déclenche automatiquement

---

## 11. Rayon très large justifié pour le transport

<span style="color: #2196F3">**Étant donné**</span> qu'un bus touristique suit une ligne fixe
<span style="color: #9E9E9E">**Et**</span> qu'il effectue des arrêts fréquents (stations)
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur n'a aucun contrôle sur la trajectoire

<span style="color: #FF9800">**Quand**</span> un point GPS avec rayon 100m est défini

<span style="color: #4CAF50">**Alors**</span> le rayon large compense les arrêts et la ligne fixe

---

## 12. Pas de warning sécurité en mode transport

<span style="color: #2196F3">**Étant donné**</span> un audio-guide transport en cours
<span style="color: #9E9E9E">**Et**</span> que le bus roule à 50 km/h

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶|] "Suivant"

<span style="color: #4CAF50">**Alors**</span> l'action est exécutée immédiatement
<span style="color: #9E9E9E">**Et**</span> aucun warning n'est affiché

---

## 13. Vitesse recommandée "Selon ligne"

<span style="color: #2196F3">**Étant donné**</span> un audio-guide transport

<span style="color: #FF9800">**Quand**</span> l'interface est affichée

<span style="color: #4CAF50">**Alors**</span> la vitesse recommandée indique "Selon ligne"
<span style="color: #9E9E9E">**Et**</span> aucune valeur fixe n'est affichée (car ligne de transport varie)

---

## 14. Tolérance horaire pour retards

<span style="color: #2196F3">**Étant donné**</span> qu'un bus touristique est en retard de 3 minutes
<span style="color: #9E9E9E">**Et**</span> qu'il arrive au point GPS "Musée du Louvre" avec retard

<span style="color: #FF9800">**Quand**</span> il entre dans le rayon de 100m

<span style="color: #4CAF50">**Alors**</span> la séquence se déclenche normalement
<span style="color: #9E9E9E">**Et**</span> le système tolère le retard (pas de pénalité temporelle)

---

## 15. Tolérance spatiale très large (150m)

<span style="color: #2196F3">**Étant donné**</span> qu'un bus passe à 120m du point GPS "Arc de Triomphe"
<span style="color: #9E9E9E">**Et**</span> que le rayon de déclenchement est 100m
<span style="color: #9E9E9E">**Et**</span> que le rayon de tolérance est 150m

<span style="color: #FF9800">**Quand**</span> la distance est détectée

<span style="color: #4CAF50">**Alors**</span> la popup "Point manqué" s'affiche avec 3 options

---

## 16. Affichage adapté au transport

<span style="color: #2196F3">**Étant donné**</span> un audio-guide transport en cours

<span style="color: #FF9800">**Quand**</span> l'interface est affichée

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un bus touristique "Paris Open Tour"
<span style="color: #9E9E9E">**Et**</span> qu'il suit un circuit fixe avec 15 arrêts

<span style="color: #FF9800">**Quand**</span> il approche de chaque arrêt

<span style="color: #4CAF50">**Alors**</span> la séquence correspondante se déclenche automatiquement
<span style="color: #9E9E9E">**Et**</span> l'utilisateur n'a rien à faire (expérience passive)

---

## 18. Cas d'usage - Train panoramique

<span style="color: #2196F3">**Étant donné**</span> un train touristique "Ligne des Alpes"
<span style="color: #9E9E9E">**Et**</span> qu'il roule à vitesse variable (20-80 km/h)

<span style="color: #FF9800">**Quand**</span> il passe près de points d'intérêt

<span style="color: #4CAF50">**Alors**</span> les séquences se déclenchent avec rayon 100m
<span style="color: #9E9E9E">**Et**</span> le système compense la vitesse élevée

---

## 19. Navigation manuelle conservée (vélo et transport)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en mode <mode>

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur [▶|] ou [|◀]

<span style="color: #4CAF50">**Alors**</span> les boutons manuels fonctionnent normalement
<span style="color: #9E9E9E">**Et**</span> aucune vérification GPS n'est effectuée

---

## 20. Affichage distance + ETA + direction (tous modes)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en mode <mode>

<span style="color: #FF9800">**Quand**</span> l'interface est affichée

<span style="color: #4CAF50">**Alors**</span> les informations distance, ETA et direction sont affichées
<span style="color: #9E9E9E">**Et**</span> le format est identique au mode voiture

---

## 21. Gestion "Point manqué" identique

<span style="color: #2196F3">**Étant donné**</span> un audio-guide en mode <mode>

<span style="color: #FF9800">**Quand**</span> un point GPS est manqué (dans rayon tolérance)

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un utilisateur gratuit écoute un audio-guide en mode <mode>

<span style="color: #FF9800">**Quand**</span> la séquence 5 se termine (1 pub / 5 séquences)

<span style="color: #4CAF50">**Alors**</span> la publicité s'enchaîne automatiquement
<span style="color: #9E9E9E">**Et**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> un cycliste dans une forêt dense
<span style="color: #9E9E9E">**Et**</span> que la précision GPS est ±80m

<span style="color: #FF9800">**Quand**</span> il approche d'un point GPS avec rayon 50m

<span style="color: #4CAF50">**Alors**</span> un avertissement s'affiche:

---

## 24. Bus dévié de son itinéraire (transport)

<span style="color: #2196F3">**Étant donné**</span> un bus touristique avec déviation
<span style="color: #9E9E9E">**Et**</span> que plusieurs points GPS deviennent inaccessibles

<span style="color: #FF9800">**Quand**</span> l'utilisateur est informé

<span style="color: #4CAF50">**Alors**</span> un message s'affiche:

---

## 25. Changement de mode en cours de route

<span style="color: #2196F3">**Étant donné**</span> un audio-guide démarré en mode "Vélo"

<span style="color: #FF9800">**Quand**</span> l'utilisateur décide de continuer à pied
<span style="color: #9E9E9E">**Et**</span> qu'il ouvre les paramètres

<span style="color: #4CAF50">**Alors**</span> il peut changer le mode vers "Piéton"
<span style="color: #9E9E9E">**Et**</span> les rayons sont reconfigurés automatiquement
<span style="color: #9E9E9E">**Et**</span> une confirmation s'affiche:

---

## 26. Détection automatique incohérente

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur marche rapidement (7 km/h)
<span style="color: #9E9E9E">**Et**</span> que le système détecte "Vélo" par erreur

<span style="color: #FF9800">**Quand**</span> la suggestion s'affiche

<span style="color: #4CAF50">**Alors**</span> l'utilisateur peut cliquer sur "Changer"
<span style="color: #9E9E9E">**Et**</span> sélectionner manuellement "Piéton"

---

## 27. Batterie en mode vélo longue distance

<span style="color: #2196F3">**Étant donné**</span> un circuit vélo de 50 km avec 20 séquences
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur roule pendant 3 heures

<span style="color: #FF9800">**Quand**</span> la batterie atteint 15%

<span style="color: #4CAF50">**Alors**</span> une notification suggère:

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que le créateur "guide@example.com" est connecté et vérifié
## 1. Création d'un audio-guide Premium

<span style="color: #2196F3">**Étant donné**</span> que le créateur crée un audio-guide "Visite VIP Versailles"

<span style="color: #FF9800">**Quand**</span> il accède aux paramètres de monétisation (étape 4)

<span style="color: #4CAF50">**Alors**</span> il peut choisir:

    | option | description |
    |---|---|
    | Gratuit | Accessible à tous (avec pubs) |
    | Premium | Réservé abonnés Premium |


---

## 2. Badge Premium visible sur l'audio-guide

<span style="color: #2196F3">**Étant donné**</span> un audio-guide configuré en Premium

<span style="color: #FF9800">**Quand**</span> il est affiché dans les résultats de recherche

<span style="color: #4CAF50">**Alors**</span> un badge "👑 Premium" est visible
<span style="color: #9E9E9E">**Et**</span> la cover image a un cadre doré subtil

---

## 3. Preview 3 premières séquences pour utilisateurs gratuits

<span style="color: #2196F3">**Étant donné**</span> un audio-guide Premium "Visite VIP Versailles" avec 15 séquences
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur gratuit ouvre l'audio-guide

<span style="color: #FF9800">**Quand**</span> il consulte la liste des séquences

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un utilisateur gratuit
<span style="color: #9E9E9E">**Et**</span> un audio-guide Premium avec preview

<span style="color: #FF9800">**Quand**</span> il écoute les séquences 1, 2 et 3

<span style="color: #4CAF50">**Alors**</span> aucune publicité n'est insérée (preview = teasing)
<span style="color: #9E9E9E">**Et**</span> l'écoute est fluide

---

## 5. Paywall après la 3ème séquence

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur gratuit termine la séquence 3

<span style="color: #FF9800">**Quand**</span> la séquence se termine

<span style="color: #4CAF50">**Alors**</span> un overlay paywall s'affiche immédiatement:

---

## 6. Bouton "Passer Premium" vers tunnel d'abonnement

<span style="color: #2196F3">**Étant donné**</span> que l'overlay paywall Premium est affiché

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "Passer Premium"

<span style="color: #4CAF50">**Alors**</span> il est redirigé vers la page d'abonnement Mangopay
<span style="color: #9E9E9E">**Et**</span> l'audio-guide actuel est marqué en "pending" (reprise après souscription)

---

## 7. Reprise automatique après souscription Premium

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur s'est abonné Premium depuis un paywall audio-guide

<span style="color: #FF9800">**Quand**</span> l'abonnement est activé

<span style="color: #4CAF50">**Alors**</span> il est redirigé vers l'audio-guide automatiquement
<span style="color: #9E9E9E">**Et**</span> la séquence 4 démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> un toast de bienvenue s'affiche: "✨ Bienvenue Premium ! Profitez de votre audio-guide"

---

## 8. Utilisateur Premium - Accès complet immédiat

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium ouvre un audio-guide Premium

<span style="color: #FF9800">**Quand**</span> il consulte la liste des séquences

<span style="color: #4CAF50">**Alors**</span> toutes les 15 séquences sont accessibles
<span style="color: #9E9E9E">**Et**</span> aucun paywall ne s'affiche
<span style="color: #9E9E9E">**Et**</span> aucune publicité n'est insérée

---

## 9. Pas de preview si l'audio-guide a <3 séquences

<span style="color: #2196F3">**Étant donné**</span> un audio-guide Premium avec seulement 2 séquences

<span style="color: #FF9800">**Quand**</span> un utilisateur gratuit tente de l'ouvrir

<span style="color: #4CAF50">**Alors**</span> un paywall s'affiche immédiatement (avant lecture)
<span style="color: #9E9E9E">**Et**</span> aucune preview n'est disponible

---

## 10. Rémunération créateur pour audio-guide Premium

<span style="color: #2196F3">**Étant donné**</span> un créateur avec un audio-guide Premium
<span style="color: #9E9E9E">**Et**</span> que 50 utilisateurs Premium ont écouté l'audio-guide ce mois

<span style="color: #FF9800">**Quand**</span> la répartition des revenus est calculée

<span style="color: #4CAF50">**Alors**</span> le créateur reçoit 70% des revenus proportionnels
<span style="color: #9E9E9E">**Et**</span> la formule est: (Écoutes créateur / Total écoutes Premium) × 70% pool Premium

---

## 11. Dashboard revenus par audio-guide

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur a 3 audio-guides Premium publiés

<span style="color: #FF9800">**Quand**</span> il consulte son dashboard revenus

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> 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 € |


<span style="color: #FF9800">**Quand**</span> il consulte son dashboard

<span style="color: #4CAF50">**Alors**</span> il peut comparer les performances
<span style="color: #9E9E9E">**Et**</span> constater que Premium génère plus de revenus par écoute

---

## 13. Seuil minimum de paiement (20€)

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur a généré 18€ de revenus ce mois

<span style="color: #FF9800">**Quand**</span> le paiement mensuel est traité

<span style="color: #4CAF50">**Alors**</span> le montant est reporté au mois suivant
<span style="color: #9E9E9E">**Et**</span> un message s'affiche: "Seuil minimum non atteint (20€). Montant reporté."

---

## 14. Paiement automatique mensuel

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur a généré 138.50€ de revenus en janvier

<span style="color: #FF9800">**Quand**</span> le 5 février arrive

<span style="color: #4CAF50">**Alors**</span> le paiement est initié automatiquement via Mangopay
<span style="color: #9E9E9E">**Et**</span> le créateur reçoit une notification: "Paiement de 138.50€ en cours"
<span style="color: #9E9E9E">**Et**</span> les fonds arrivent sous 2-3 jours ouvrés

---

## 15. Insertion publicité toutes les 5 séquences (gratuit)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide gratuit avec 12 séquences
<span style="color: #9E9E9E">**Et**</span> un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> il termine la séquence 5

<span style="color: #4CAF50">**Alors**</span> une publicité démarre automatiquement

<span style="color: #FF9800">**Quand**</span> il termine la séquence 10

<span style="color: #4CAF50">**Alors**</span> une deuxième publicité démarre

---

## 16. Publicité après séquence en mode piéton (avec pause)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide piéton gratuit

<span style="color: #FF9800">**Quand**</span> la séquence 5 se termine

<span style="color: #4CAF50">**Alors**</span> la publicité démarre automatiquement (pas d'attente bouton)
<span style="color: #9E9E9E">**Et**</span> la pub est skippable après 5 secondes

<span style="color: #FF9800">**Quand**</span> la publicité se termine

<span style="color: #4CAF50">**Alors**</span> le player se met en pause
<span style="color: #9E9E9E">**Et**</span> l'utilisateur doit cliquer sur [▶|] pour continuer

---

## 17. Publicité en mode voiture/vélo/transport (automatique)

<span style="color: #2196F3">**Étant donné**</span> un audio-guide voiture gratuit

<span style="color: #FF9800">**Quand**</span> la séquence 5 se termine

<span style="color: #4CAF50">**Alors**</span> la publicité démarre automatiquement

<span style="color: #FF9800">**Quand**</span> la publicité se termine

<span style="color: #4CAF50">**Alors**</span> la séquence 6 démarre automatiquement (pas de pause)

---

## 18. Publicités géolocalisées dans audio-guides

<span style="color: #2196F3">**Étant donné**</span> un audio-guide dans la région "Île-de-France"

<span style="color: #FF9800">**Quand**</span> une publicité doit être insérée

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide gratuit génère 200 écoutes complètes
<span style="color: #9E9E9E">**Et**</span> que chaque écoute complète = 2 publicités (séq. 5 et 10)

<span style="color: #FF9800">**Quand**</span> les revenus pub sont calculés

<span style="color: #4CAF50">**Alors**</span> 400 impressions sont comptabilisées
<span style="color: #9E9E9E">**Et**</span> le créateur reçoit 0.80€ (400 × 0.002€)

---

## 20. CTA Premium après audio-guide gratuit complété

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur gratuit complète un audio-guide gratuit

<span style="color: #FF9800">**Quand**</span> il termine la dernière séquence

<span style="color: #4CAF50">**Alors**</span> un overlay s'affiche:

---

## 21. Recommandations d'audio-guides Premium après gratuit

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur termine un audio-guide gratuit "Tour de Paris"

<span style="color: #FF9800">**Quand**</span> l'overlay de fin s'affiche

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un audio-guide Premium avec >500 écoutes et note >4.5/5

<span style="color: #FF9800">**Quand**</span> il est affiché dans les résultats de recherche

<span style="color: #4CAF50">**Alors**</span> un badge "⭐ Premium recommandé" est visible
<span style="color: #9E9E9E">**Et**</span> il est mis en avant dans les résultats

---

## 23. Conversion tracking pour attribution créateur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur découvre Premium via un audio-guide créateur

<span style="color: #FF9800">**Quand**</span> il s'abonne

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le créateur bénéficie d'un bonus de conversion

---

## 24. Essai gratuit 7 jours Premium via audio-guide

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur gratuit atteint le paywall d'un audio-guide Premium
<span style="color: #9E9E9E">**Et**</span> qu'il n'a jamais essayé Premium

<span style="color: #FF9800">**Quand**</span> l'overlay s'affiche

<span style="color: #4CAF50">**Alors**</span> une offre d'essai est proposée:

---

## 25. Activation immédiate après essai gratuit

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur démarre un essai gratuit 7 jours

<span style="color: #FF9800">**Quand**</span> l'essai est activé

<span style="color: #4CAF50">**Alors**</span> l'audio-guide Premium démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> toutes les séquences sont débloquées
<span style="color: #9E9E9E">**Et**</span> aucune publicité n'est insérée

---

## 26. Rappel 2 jours avant fin d'essai

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a démarré un essai gratuit le 15/01

<span style="color: #FF9800">**Quand**</span> le 20/01 arrive (J-2)

<span style="color: #4CAF50">**Alors**</span> une notification est envoyée:

---

## 27. Créateur mix gratuit + Premium

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> un utilisateur découvre son profil

<span style="color: #4CAF50">**Alors**</span> les audio-guides gratuits servent de teasing
<span style="color: #9E9E9E">**Et**</span> les audio-guides Premium sont mis en avant avec badge

---

## 28. Utilisateur hésite à s'abonner

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur atteint le paywall d'un audio-guide Premium
<span style="color: #9E9E9E">**Et**</span> qu'il clique sur "Découvrir d'autres audio-guides gratuits"

<span style="color: #FF9800">**Quand**</span> il revient 2 jours plus tard sur le même audio-guide

<span style="color: #4CAF50">**Alors**</span> le paywall s'affiche à nouveau
<span style="color: #9E9E9E">**Et**</span> une réduction temporaire est proposée: "Offre spéciale : -20% premier mois"

---

## 29. Échec du paiement Premium via paywall

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur tente de s'abonner Premium

<span style="color: #FF9800">**Quand**</span> le paiement Mangopay échoue

<span style="color: #4CAF50">**Alors**</span> un message d'erreur s'affiche:

---

## 30. Abonnement Premium expiré pendant écoute

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium écoute un audio-guide Premium
<span style="color: #9E9E9E">**Et**</span> que son abonnement expire pendant l'écoute (séquence 8/15)

<span style="color: #FF9800">**Quand**</span> l'expiration est détectée

<span style="color: #4CAF50">**Alors**</span> l'écoute continue jusqu'à la fin de la séquence en cours
<span style="color: #9E9E9E">**Et**</span> un overlay s'affiche ensuite:

---

## 31. Créateur change audio-guide de gratuit à Premium

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide gratuit a 50 utilisateurs avec progression

<span style="color: #FF9800">**Quand**</span> le créateur le passe en Premium

<span style="color: #4CAF50">**Alors**</span> les utilisateurs ayant déjà commencé gardent l'accès complet
<span style="color: #9E9E9E">**Et**</span> seuls les nouveaux utilisateurs sont soumis au paywall
<span style="color: #9E9E9E">**Et**</span> un message de transparence s'affiche:

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que l'utilisateur "jean@example.com" est connecté
## 1. Sauvegarde automatique toutes les 10 secondes

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide "Visite du Louvre" est en cours
<span style="color: #9E9E9E">**Et**</span> que la séquence 3 est à la position 1:24

<span style="color: #FF9800">**Quand**</span> 10 secondes s'écoulent

<span style="color: #4CAF50">**Alors**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> qu'une sauvegarde est déclenchée

<span style="color: #FF9800">**Quand**</span> la progression est enregistrée

<span style="color: #4CAF50">**Alors**</span> les données sont écrites en SQLite local
<span style="color: #9E9E9E">**Et**</span> l'écriture prend moins de 50ms
<span style="color: #9E9E9E">**Et**</span> l'application reste fluide

---

## 3. Synchronisation cloud en arrière-plan

<span style="color: #2196F3">**Étant donné**</span> qu'une sauvegarde locale est effectuée

<span style="color: #FF9800">**Quand**</span> 30 secondes s'écoulent

<span style="color: #4CAF50">**Alors**</span> la progression est synchronisée vers PostgreSQL cloud
<span style="color: #9E9E9E">**Et**</span> la synchronisation s'effectue en arrière-plan
<span style="color: #9E9E9E">**Et**</span> elle n'impacte pas les performances

---

## 4. Sauvegarde immédiate lors de la fermeture

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide est en cours à la séquence 4 position 2:15

<span style="color: #FF9800">**Quand**</span> l'utilisateur ferme l'application

<span style="color: #4CAF50">**Alors**</span> la progression est sauvegardée immédiatement (local + cloud)
<span style="color: #9E9E9E">**Et**</span> les données sont écrites avant la fermeture complète

---

## 5. Sauvegarde des séquences complétées

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide de 12 séquences est en cours
<span style="color: #9E9E9E">**Et**</span> que les séquences 1, 2, 4, 5 ont été écoutées à >80%

<span style="color: #FF9800">**Quand**</span> la progression est sauvegardée

<span style="color: #4CAF50">**Alors**</span> les séquences complétées sont enregistrées:

---

## 6. Historique des écoutes pour statistiques

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté 3 séquences d'un audio-guide

<span style="color: #FF9800">**Quand**</span> les données sont sauvegardées

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a quitté l'app à la séquence 6 position 2:34

<span style="color: #FF9800">**Quand**</span> il rouvre l'audio-guide "Visite du Louvre"

<span style="color: #4CAF50">**Alors**</span> une popup s'affiche:

---

## 8. Action "Reprendre" - Position exacte restaurée

<span style="color: #2196F3">**Étant donné**</span> qu'une popup de reprise est affichée

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "▶️ Reprendre"

<span style="color: #4CAF50">**Alors**</span> la séquence 6 "Vénus de Milo" se charge
<span style="color: #9E9E9E">**Et**</span> la position exacte 2:34 est restaurée
<span style="color: #9E9E9E">**Et**</span> la lecture démarre automatiquement après 1 seconde

---

## 9. Action "Recommencer" - Réinitialisation complète

<span style="color: #2196F3">**Étant donné**</span> qu'une popup de reprise est affichée

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "🔄 Recommencer"

<span style="color: #4CAF50">**Alors**</span> l'audio-guide redémarre depuis la séquence 1 position 0:00
<span style="color: #9E9E9E">**Et**</span> toutes les séquences sont marquées ⭕ "À écouter"
<span style="color: #9E9E9E">**Et**</span> l'historique d'écoute est réinitialisé pour cette session

---

## 10. Reprise après 7 jours d'inactivité

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a arrêté un audio-guide le 15/01/2026
<span style="color: #9E9E9E">**Et**</span> qu'il le rouvre le 22/01/2026 (7 jours plus tard)

<span style="color: #FF9800">**Quand**</span> l'audio-guide se charge

<span style="color: #4CAF50">**Alors**</span> la popup de reprise s'affiche normalement
<span style="color: #9E9E9E">**Et**</span> toutes les données de progression sont conservées

---

## 11. Reprise sur un autre appareil (synchronisation cloud)

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur écoute un audio-guide sur iPhone
<span style="color: #9E9E9E">**Et**</span> qu'il quitte à la séquence 4 position 1:20

<span style="color: #FF9800">**Quand**</span> il ouvre le même audio-guide sur iPad

<span style="color: #4CAF50">**Alors**</span> la popup de reprise s'affiche avec la progression iPhone
<span style="color: #9E9E9E">**Et**</span> il peut reprendre exactement où il s'était arrêté

---

## 12. Conflit de synchronisation (dernier appareil gagne)

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur écoute sur iPhone à la séquence 3
<span style="color: #9E9E9E">**Et**</span> simultanément sur iPad à la séquence 7

<span style="color: #FF9800">**Quand**</span> les deux appareils synchronisent

<span style="color: #4CAF50">**Alors**</span> la progression la plus récente (timestamp) est conservée
<span style="color: #9E9E9E">**Et**</span> l'appareil avec ancienne progression affiche une notification:

---

## 13. Mode hors-ligne - Sauvegarde locale uniquement

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur écoute un audio-guide hors connexion
<span style="color: #9E9E9E">**Et**</span> qu'il atteint la séquence 5

<span style="color: #FF9800">**Quand**</span> la progression est sauvegardée

<span style="color: #4CAF50">**Alors**</span> les données sont écrites localement (SQLite)
<span style="color: #9E9E9E">**Et**</span> une icône "☁️ Non synchronisé" s'affiche discrètement

---

## 14. Synchronisation automatique à la reconnexion

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a écouté hors ligne jusqu'à la séquence 8
<span style="color: #9E9E9E">**Et**</span> que 5 progressions locales ne sont pas synchronisées

<span style="color: #FF9800">**Quand**</span> la connexion réseau est rétablie

<span style="color: #4CAF50">**Alors**</span> les 5 progressions sont synchronisées automatiquement
<span style="color: #9E9E9E">**Et**</span> un toast s'affiche brièvement: "✅ Progression synchronisée"

---

## 15. Suppression de la progression (recommencer proprement)

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur est à la séquence 10/12

<span style="color: #FF9800">**Quand**</span> il ouvre les paramètres de l'audio-guide
<span style="color: #9E9E9E">**Et**</span> qu'il clique sur "🔄 Réinitialiser progression"

<span style="color: #4CAF50">**Alors**</span> une confirmation s'affiche:
<span style="color: #9E9E9E">**Et**</span> si confirmé, la progression est effacée

---

## 16. Taux de complétion global de l'audio-guide

<span style="color: #2196F3">**Étant donné**</span> un audio-guide de 12 séquences
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur a écouté complètement 8 séquences
<span style="color: #9E9E9E">**Et**</span> partiellement 1 séquence (45%)

<span style="color: #FF9800">**Quand**</span> les statistiques sont calculées

<span style="color: #4CAF50">**Alors**</span> le taux de complétion affiché est "67%" (8/12)

---

## 17. Badge "Audio-guide complété" à 100%

<span style="color: #2196F3">**Étant donné**</span> un audio-guide de 12 séquences

<span style="color: #FF9800">**Quand**</span> l'utilisateur écoute la 12ème séquence à 100%

<span style="color: #4CAF50">**Alors**</span> un badge "✅ Audio-guide complété" s'affiche
<span style="color: #9E9E9E">**Et**</span> une notification de félicitations est envoyée
<span style="color: #9E9E9E">**Et**</span> le statut "Complété le 22/01/2026" est visible dans l'historique

---

## 18. Temps total passé sur l'audio-guide

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté un audio-guide sur 2 sessions:

    | session | durée |
    |---|---|
    | 1 | 25 min |
    | 2 | 18 min |


<span style="color: #FF9800">**Quand**</span> les statistiques sont calculées

<span style="color: #4CAF50">**Alors**</span> le temps total est "43 minutes"
<span style="color: #9E9E9E">**Et**</span> il est affiché dans l'historique personnel

---

## 19. Liste des audio-guides "En cours" dans le profil

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> il consulte son profil "Audio-guides"

<span style="color: #4CAF50">**Alors**</span> la section "📍 En cours" affiche les 3 audio-guides
<span style="color: #9E9E9E">**Et**</span> chaque élément montre la progression sous forme de barre

---

## 20. Liste des audio-guides "Complétés" dans le profil

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> il consulte son profil "Audio-guides"

<span style="color: #4CAF50">**Alors**</span> la section "✅ Complétés" affiche les 2 audio-guides
<span style="color: #9E9E9E">**Et**</span> la date de complétion est visible

---

## 21. Badge "Complétiste" pour 10 audio-guides complétés

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur complète son 10ème audio-guide

<span style="color: #FF9800">**Quand**</span> la complétion est enregistrée

<span style="color: #4CAF50">**Alors**</span> un badge "🏆 Complétiste" est débloqué
<span style="color: #9E9E9E">**Et**</span> il apparaît sur son profil
<span style="color: #9E9E9E">**Et**</span> une notification est envoyée:

---

## 22. 📋 Plan: Niveaux de badges selon nombre d'audio-guides complétés

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur complète <nombre> audio-guides

<span style="color: #FF9800">**Quand**</span> le badge est attribué

<span style="color: #4CAF50">**Alors**</span> il reçoit le badge "<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

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur a publié l'audio-guide "Visite du Louvre"

<span style="color: #FF9800">**Quand**</span> il consulte son dashboard

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un audio-guide de 12 séquences

<span style="color: #FF9800">**Quand**</span> le créateur consulte les statistiques détaillées

<span style="color: #4CAF50">**Alors**</span> un graphique en barres affiche:

    | séquence | taux_completion |
    |---|---|
    | 1 | 100% |
    | 2 | 95% |
    | 3 | 89% |
    | ... | ... |
    | 12 | 58% |


---

## 25. Détection des points d'abandon

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide a un taux de complétion de 58%
<span style="color: #9E9E9E">**Et**</span> que 35% des utilisateurs abandonnent à la séquence 7

<span style="color: #FF9800">**Quand**</span> le créateur consulte les insights

<span style="color: #4CAF50">**Alors**</span> un avertissement s'affiche:

---

## 26. Heatmap géographique des écoutes

<span style="color: #2196F3">**Étant donné**</span> un audio-guide géolocalisé

<span style="color: #FF9800">**Quand**</span> le créateur consulte la heatmap

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur analyse son audio-guide

<span style="color: #FF9800">**Quand**</span> il consulte les statistiques temporelles

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide atteint 1000 écoutes

<span style="color: #FF9800">**Quand**</span> le seuil est franchi

<span style="color: #4CAF50">**Alors**</span> une notification est envoyée au créateur:

---

## 29. Corruption de données de sauvegarde

<span style="color: #2196F3">**Étant donné**</span> qu'une sauvegarde locale (SQLite) est corrompue

<span style="color: #FF9800">**Quand**</span> l'application tente de charger la progression

<span style="color: #4CAF50">**Alors**</span> une récupération depuis le cloud est tentée
<span style="color: #9E9E9E">**Et**</span> si réussie, les données cloud sont restaurées
<span style="color: #9E9E9E">**Et**</span> la base locale est reconstruite

---

## 30. Échec de synchronisation cloud

<span style="color: #2196F3">**Étant donné**</span> que l'API cloud est indisponible

<span style="color: #FF9800">**Quand**</span> une tentative de synchronisation est effectuée

<span style="color: #4CAF50">**Alors**</span> l'application continue avec sauvegarde locale uniquement
<span style="color: #9E9E9E">**Et**</span> un retry automatique est programmé dans 5 minutes
<span style="color: #9E9E9E">**Et**</span> l'icône "☁️ Non synchronisé" reste affichée

---

## 31. Suppression accidentelle de progression (récupération)

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur réinitialise un audio-guide par erreur

<span style="color: #FF9800">**Quand**</span> il contacte le support dans les 7 jours

<span style="color: #4CAF50">**Alors**</span> l'équipe peut restaurer la progression depuis les backups
<span style="color: #9E9E9E">**Et**</span> les données sont récupérables (backup quotidien conservé 30 jours)

---

## 32. Nettoyage automatique des vieilles progressions

<span style="color: #2196F3">**Étant donné**</span> qu'une progression n'a pas été mise à jour depuis 6 mois

<span style="color: #FF9800">**Quand**</span> le nettoyage automatique s'exécute

<span style="color: #4CAF50">**Alors**</span> la progression est archivée (mais pas supprimée)
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut la restaurer via l'historique

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Créateur doit classifier son contenu à la publication

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur connecté

<span style="color: #FF9800">**Quand**</span> je crée un nouveau contenu audio

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je crée un contenu audio

<span style="color: #FF9800">**Quand**</span> j'essaie de publier sans sélectionner de classification

<span style="color: #4CAF50">**Alors**</span> la publication échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Vous devez sélectionner une classification d'âge"

---

## 3. Utilisateur 13-15 ans voit uniquement du contenu "Tout public"

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de 14 ans
<span style="color: #9E9E9E">**Et**</span> qu'il existe des contenus avec les classifications suivantes:

    | classification | nombre |
    |---|---|
    | Tout public | 20 |
    | 13+ | 15 |
    | 16+ | 10 |
    | 18+ | 5 |


<span style="color: #FF9800">**Quand**</span> je demande des recommandations

<span style="color: #4CAF50">**Alors**</span> je vois uniquement les 20 contenus "Tout public"
<span style="color: #9E9E9E">**Et**</span> les autres contenus ne sont jamais proposés

---

## 4. Utilisateur 16-17 ans voit "Tout public" et "13+"

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de 17 ans
<span style="color: #9E9E9E">**Et**</span> qu'il existe des contenus avec les classifications suivantes:

    | classification | nombre |
    |---|---|
    | Tout public | 20 |
    | 13+ | 15 |
    | 16+ | 10 |
    | 18+ | 5 |


<span style="color: #FF9800">**Quand**</span> je demande des recommandations

<span style="color: #4CAF50">**Alors**</span> je vois 35 contenus (Tout public + 13+)
<span style="color: #9E9E9E">**Et**</span> les contenus 16+ et 18+ ne sont pas proposés

---

## 5. Utilisateur 18+ voit tous les contenus

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de 25 ans
<span style="color: #9E9E9E">**Et**</span> qu'il existe des contenus avec toutes les classifications

<span style="color: #FF9800">**Quand**</span> je demande des recommandations

<span style="color: #4CAF50">**Alors**</span> je vois tous les contenus sans restriction
<span style="color: #9E9E9E">**Et**</span> aucun filtre d'âge n'est appliqué

---

## 6. Mode Kids activé automatiquement pour les moins de 13 ans

<span style="color: #2196F3">**Étant donné**</span> que je m'inscris avec une date de naissance "2013-01-21"

<span style="color: #4CAF50">**Alors**</span> le mode Kids est activé automatiquement
<span style="color: #9E9E9E">**Et**</span> je vois uniquement du contenu "Tout public"
<span style="color: #9E9E9E">**Et**</span> des protections supplémentaires sont appliquées

---

## 7. Modérateur reclassifie un contenu mal catégorisé

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est publié avec la classification "Tout public"
<span style="color: #9E9E9E">**Et**</span> que ce contenu contient du langage inapproprié détecté en modération

<span style="color: #FF9800">**Quand**</span> le modérateur reclassifie ce contenu en "16+"

<span style="color: #4CAF50">**Alors**</span> la nouvelle classification est appliquée immédiatement
<span style="color: #9E9E9E">**Et**</span> le contenu n'est plus visible pour les utilisateurs de moins de 16 ans
<span style="color: #9E9E9E">**Et**</span> le créateur reçoit une notification de reclassification

---

## 8. Strike si classification volontairement incorrecte

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur a publié un contenu "18+" classifié comme "Tout public"
<span style="color: #9E9E9E">**Et**</span> que ce contenu a été signalé

<span style="color: #FF9800">**Quand**</span> le modérateur confirme la mauvaise classification volontaire

<span style="color: #4CAF50">**Alors**</span> le créateur reçoit 1 strike
<span style="color: #9E9E9E">**Et**</span> le contenu est reclassifié en "18+"
<span style="color: #9E9E9E">**Et**</span> le créateur reçoit une notification explicative

---

## 9. Créateur peut voir la distribution d'âge de son audience

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur
<span style="color: #9E9E9E">**Et**</span> que j'ai publié des contenus avec différentes classifications

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de 16 ans

<span style="color: #FF9800">**Quand**</span> je recherche des contenus

<span style="color: #4CAF50">**Alors**</span> les résultats incluent uniquement:

    | classification |
    |---|
    | Tout public |
    | 13+ |

<span style="color: #9E9E9E">**Et**</span> je ne vois pas les contenus 16+ et 18+ dans les résultats

---

## 11. Notification si tentative d'accès à contenu non autorisé

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de 14 ans
<span style="color: #9E9E9E">**Et**</span> qu'un contenu "16+" est partagé avec moi via un lien direct

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder au contenu

<span style="color: #4CAF50">**Alors**</span> l'accès est refusé
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un nouveau créateur
<span style="color: #9E9E9E">**Et**</span> que je publie mon premier contenu classifié "18+"

<span style="color: #FF9800">**Quand**</span> le modérateur valide mon contenu

<span style="color: #4CAF50">**Alors**</span> il vérifie que la classification "18+" est appropriée
<span style="color: #9E9E9E">**Et**</span> peut la modifier si nécessaire avant validation

---

## 13. Statistiques de classification dans l'interface créateur

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur

<span style="color: #FF9800">**Quand**</span> je consulte mes contenus publiés

<span style="color: #4CAF50">**Alors**</span> je vois pour chaque contenu:

    | information | exemple |
    |---|---|
    | Classification actuelle | 13+ |
    | Nombre de signalements | 2 |
    | Reclassifications | Aucune / 1× par modérateur |


---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur existe avec:
        | email | mot_de_passe |
        |---|---|
        | user@test.fr | Password123 |


## 1. Connexion réussie avec identifiants valides

<span style="color: #FF9800">**Quand**</span> je me connecte avec:

    | email | mot_de_passe |
    |---|---|
    | user@test.fr | Password123 |


<span style="color: #4CAF50">**Alors**</span> je suis connecté avec succès
<span style="color: #9E9E9E">**Et**</span> je reçois un access token valide pour 15 minutes
<span style="color: #9E9E9E">**Et**</span> je reçois un refresh token valide pour 30 jours

---

## 2. Connexion échouée avec email inexistant

<span style="color: #FF9800">**Quand**</span> je me connecte avec l'email "inexistant@test.fr"

<span style="color: #4CAF50">**Alors**</span> la connexion échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Email ou mot de passe incorrect"

---

## 3. Connexion échouée avec mot de passe incorrect

<span style="color: #FF9800">**Quand**</span> je me connecte avec:

    | email | mot_de_passe |
    |---|---|
    | user@test.fr | MauvaisPass1 |


<span style="color: #4CAF50">**Alors**</span> la connexion échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Email ou mot de passe incorrect"

---

## 4. Blocage après 5 tentatives échouées

<span style="color: #2196F3">**Étant donné**</span> que j'ai échoué 4 tentatives de connexion

<span style="color: #FF9800">**Quand**</span> j'échoue une 5ème tentative de connexion

<span style="color: #4CAF50">**Alors**</span> mon compte est temporairement bloqué
<span style="color: #9E9E9E">**Et**</span> je vois le message "Compte bloqué pour 15 minutes après 5 tentatives échouées"
<span style="color: #9E9E9E">**Et**</span> je reçois un email de notification de blocage

---

## 5. Tentative de connexion pendant le blocage

<span style="color: #2196F3">**Étant donné**</span> que mon compte est bloqué suite à 5 tentatives échouées
<span style="color: #9E9E9E">**Et**</span> que seulement 5 minutes se sont écoulées

<span style="color: #FF9800">**Quand**</span> j'essaie de me connecter avec les bons identifiants

<span style="color: #4CAF50">**Alors**</span> la connexion échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Compte bloqué. Réessayez dans 10 minutes"

---

## 6. Déblocage automatique après 15 minutes

<span style="color: #2196F3">**Étant donné**</span> que mon compte est bloqué suite à 5 tentatives échouées
<span style="color: #9E9E9E">**Et**</span> que 15 minutes se sont écoulées

<span style="color: #FF9800">**Quand**</span> je me connecte avec les bons identifiants

<span style="color: #4CAF50">**Alors**</span> je suis connecté avec succès
<span style="color: #9E9E9E">**Et**</span> le compteur de tentatives est réinitialisé

---

## 7. Reset du compteur après connexion réussie

<span style="color: #2196F3">**Étant donné**</span> que j'ai échoué 3 tentatives de connexion

<span style="color: #FF9800">**Quand**</span> je me connecte avec les bons identifiants

<span style="color: #4CAF50">**Alors**</span> je suis connecté avec succès
<span style="color: #9E9E9E">**Et**</span> le compteur de tentatives est remis à 0

---

## 8. Reset automatique du compteur après 15 minutes sans blocage

<span style="color: #2196F3">**Étant donné**</span> que j'ai échoué 3 tentatives de connexion
<span style="color: #9E9E9E">**Et**</span> que 15 minutes se sont écoulées sans nouvelle tentative

<span style="color: #FF9800">**Quand**</span> je consulte mon compteur de tentatives

<span style="color: #4CAF50">**Alors**</span> le compteur est réinitialisé à 0

---

## 9. Déblocage via lien "Mot de passe oublié"

<span style="color: #2196F3">**Étant donné**</span> que mon compte est bloqué suite à 5 tentatives échouées

<span style="color: #FF9800">**Quand**</span> j'utilise la fonction "Mot de passe oublié"
<span style="color: #9E9E9E">**Et**</span> que je réinitialise mon mot de passe

<span style="color: #4CAF50">**Alors**</span> le blocage est levé immédiatement
<span style="color: #9E9E9E">**Et**</span> je peux me connecter avec le nouveau mot de passe

---

## 10. Email de notification lors d'un blocage

<span style="color: #2196F3">**Étant donné**</span> que j'ai échoué 5 tentatives de connexion

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté sur un appareil iOS

<span style="color: #FF9800">**Quand**</span> je me connecte également sur un appareil Android

<span style="color: #4CAF50">**Alors**</span> les deux sessions sont actives simultanément
<span style="color: #9E9E9E">**Et**</span> je peux utiliser l'application sur les deux appareils

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que Zitadel est configuré
## 1. Inscription réussie avec données valides

<span style="color: #2196F3">**Étant donné**</span> que l'email "nouveau@example.com" n'existe pas

<span style="color: #FF9800">**Quand**</span> 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 |


<span style="color: #4CAF50">**Alors**</span> mon compte est créé avec succès
<span style="color: #9E9E9E">**Et**</span> je reçois un email de vérification
<span style="color: #9E9E9E">**Et**</span> le lien de vérification expire dans 7 jours
<span style="color: #9E9E9E">**Et**</span> je suis redirigé vers l'application

---

## 2. Inscription avec email déjà existant

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur existe avec l'email "existant@example.com"

<span style="color: #FF9800">**Quand**</span> je m'inscris avec l'email "existant@example.com"

<span style="color: #4CAF50">**Alors**</span> l'inscription échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Cet email est déjà utilisé"

---

## 3. Inscription avec mot de passe invalide - trop court

<span style="color: #FF9800">**Quand**</span> je m'inscris avec un mot de passe de moins de 8 caractères "Pass1"

<span style="color: #4CAF50">**Alors**</span> l'inscription échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le mot de passe doit contenir au moins 8 caractères"

---

## 4. Inscription avec mot de passe invalide - sans majuscule

<span style="color: #FF9800">**Quand**</span> je m'inscris avec un mot de passe sans majuscule "password123"

<span style="color: #4CAF50">**Alors**</span> l'inscription échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le mot de passe doit contenir au moins une majuscule"

---

## 5. Inscription avec mot de passe invalide - sans chiffre

<span style="color: #FF9800">**Quand**</span> je m'inscris avec un mot de passe sans chiffre "Password"

<span style="color: #4CAF50">**Alors**</span> l'inscription échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le mot de passe doit contenir au moins un chiffre"

---

## 6. Inscription avec pseudo invalide - trop court

<span style="color: #FF9800">**Quand**</span> je m'inscris avec un pseudo de 2 caractères "ab"

<span style="color: #4CAF50">**Alors**</span> l'inscription échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le pseudo doit contenir entre 3 et 30 caractères"

---

## 7. Inscription avec pseudo invalide - caractères spéciaux

<span style="color: #FF9800">**Quand**</span> je m'inscris avec un pseudo contenant des caractères spéciaux "user@123"

<span style="color: #4CAF50">**Alors**</span> l'inscription échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le pseudo ne peut contenir que des lettres, chiffres et underscores"

---

## 8. Inscription avec email invalide

<span style="color: #FF9800">**Quand**</span> je m'inscris avec un email invalide "email.invalide"

<span style="color: #4CAF50">**Alors**</span> l'inscription échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Format d'email invalide"

---

## 9. 📋 Plan: Inscription avec âge minimum non respecté

<span style="color: #2196F3">**Étant donné**</span> la date du jour est "2026-01-21"

<span style="color: #FF9800">**Quand**</span> je m'inscris avec une date de naissance "<date_naissance>"

<span style="color: #4CAF50">**Alors**</span> l'inscription échoue
<span style="color: #9E9E9E">**Et**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> la date du jour est "2026-01-21"

<span style="color: #FF9800">**Quand**</span> je m'inscris avec une date de naissance "2013-01-21"

<span style="color: #4CAF50">**Alors**</span> mon compte est créé avec succès
<span style="color: #9E9E9E">**Et**</span> le mode Kids est activé automatiquement

---

## 11. Inscription avec âge supérieur à 18 ans

<span style="color: #2196F3">**Étant donné**</span> la date du jour est "2026-01-21"

<span style="color: #FF9800">**Quand**</span> je m'inscris avec une date de naissance "1990-06-15"

<span style="color: #4CAF50">**Alors**</span> mon compte est créé avec succès
<span style="color: #9E9E9E">**Et**</span> j'ai accès à tous les contenus sans restriction d'âge

---

## 12. Données minimales requises à l'inscription

<span style="color: #FF9800">**Quand**</span> je m'inscris sans fournir de nom complet
<span style="color: #9E9E9E">**Et**</span> sans fournir de photo de profil
<span style="color: #9E9E9E">**Et**</span> sans fournir de bio

<span style="color: #4CAF50">**Alors**</span> mon compte est créé avec succès
<span style="color: #9E9E9E">**Et**</span> un avatar par défaut est généré
<span style="color: #9E9E9E">**Et**</span> les champs optionnels sont vides

---

## 13. Renvoyer l'email de vérification

<span style="color: #2196F3">**Étant donné**</span> que je me suis inscrit avec l'email "nouveau@example.com"
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas vérifié mon email

<span style="color: #FF9800">**Quand**</span> je demande à renvoyer l'email de vérification

<span style="color: #4CAF50">**Alors**</span> un nouvel email de vérification est envoyé
<span style="color: #9E9E9E">**Et**</span> le précédent lien est invalidé

---

## 14. Limite de renvoi d'email de vérification

<span style="color: #2196F3">**Étant donné**</span> que je me suis inscrit avec l'email "nouveau@example.com"
<span style="color: #9E9E9E">**Et**</span> que j'ai déjà renvoyé l'email de vérification 3 fois aujourd'hui

<span style="color: #FF9800">**Quand**</span> je demande à renvoyer l'email de vérification une 4ème fois

<span style="color: #4CAF50">**Alors**</span> la demande échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Vous avez atteint la limite de 3 renvois par jour"

---

## 15. Expiration du lien de vérification

<span style="color: #2196F3">**Étant donné**</span> que je me suis inscrit il y a 8 jours
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas vérifié mon email

<span style="color: #FF9800">**Quand**</span> j'essaie d'utiliser le lien de vérification

<span style="color: #4CAF50">**Alors**</span> la vérification échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Ce lien a expiré"
<span style="color: #9E9E9E">**Et**</span> je peux demander un nouveau lien

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur existe avec l'email "user@test.fr"
## 1. Demander la réinitialisation du mot de passe

<span style="color: #FF9800">**Quand**</span> je clique sur "Mot de passe oublié"
<span style="color: #9E9E9E">**Et**</span> que je saisis mon email "user@test.fr"

<span style="color: #4CAF50">**Alors**</span> je reçois un email avec un lien de réinitialisation
<span style="color: #9E9E9E">**Et**</span> le lien expire dans 1 heure
<span style="color: #9E9E9E">**Et**</span> je vois le message "Email de réinitialisation envoyé"

---

## 2. Email inexistant lors de la demande de réinitialisation

<span style="color: #FF9800">**Quand**</span> je demande une réinitialisation pour l'email "inexistant@test.fr"

<span style="color: #4CAF50">**Alors**</span> je vois le même message "Email de réinitialisation envoyé"
<span style="color: #F44336">**Mais**</span> aucun email n'est envoyé (sécurité - pas d'énumération d'emails)

---

## 3. Réinitialiser le mot de passe avec un lien valide

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé une réinitialisation de mot de passe
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu le lien de réinitialisation

<span style="color: #FF9800">**Quand**</span> je clique sur le lien
<span style="color: #9E9E9E">**Et**</span> que je saisis un nouveau mot de passe "NouveauPass123"
<span style="color: #9E9E9E">**Et**</span> que je confirme le nouveau mot de passe "NouveauPass123"

<span style="color: #4CAF50">**Alors**</span> mon mot de passe est modifié avec succès
<span style="color: #9E9E9E">**Et**</span> je suis déconnecté de tous mes appareils sauf celui en cours
<span style="color: #9E9E9E">**Et**</span> je reçois un email de confirmation de changement

---

## 4. Lien de réinitialisation expiré

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé une réinitialisation il y a 2 heures

<span style="color: #FF9800">**Quand**</span> j'essaie d'utiliser le lien

<span style="color: #4CAF50">**Alors**</span> je vois le message "Ce lien a expiré"
<span style="color: #9E9E9E">**Et**</span> je peux demander un nouveau lien

---

## 5. Nouveau mot de passe ne respecte pas les règles

<span style="color: #2196F3">**Étant donné**</span> que j'ai un lien de réinitialisation valide

<span style="color: #FF9800">**Quand**</span> je saisis un nouveau mot de passe "faible"

<span style="color: #4CAF50">**Alors**</span> la réinitialisation échoue
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai un lien de réinitialisation valide

<span style="color: #FF9800">**Quand**</span> je saisis un nouveau mot de passe "NouveauPass123"
<span style="color: #9E9E9E">**Et**</span> que je confirme avec un mot de passe différent "AutrePass123"

<span style="color: #4CAF50">**Alors**</span> la réinitialisation échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Les mots de passe ne correspondent pas"

---

## 7. Limite de demandes de réinitialisation

<span style="color: #2196F3">**Étant donné**</span> que j'ai déjà demandé 3 réinitialisations dans la dernière heure

<span style="color: #FF9800">**Quand**</span> je demande une 4ème réinitialisation

<span style="color: #4CAF50">**Alors**</span> la demande échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Maximum 3 demandes par heure. Réessayez plus tard."

---

## 8. Compteur de demandes se réinitialise après 1 heure

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé 3 réinitialisations
<span style="color: #9E9E9E">**Et**</span> que 1 heure s'est écoulée

<span style="color: #FF9800">**Quand**</span> je demande une nouvelle réinitialisation

<span style="color: #4CAF50">**Alors**</span> la demande réussit
<span style="color: #9E9E9E">**Et**</span> je reçois un email avec un nouveau lien

---

## 9. Email de notification de changement de mot de passe

<span style="color: #2196F3">**Étant donné**</span> que je viens de réinitialiser mon mot de passe

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je me suis toujours connecté depuis mon iPhone
<span style="color: #9E9E9E">**Et**</span> que je réinitialise mon mot de passe depuis un PC Windows

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté sur 4 appareils différents
<span style="color: #9E9E9E">**Et**</span> que je réinitialise mon mot de passe depuis un navigateur web

<span style="color: #4CAF50">**Alors**</span> les 3 autres appareils sont déconnectés immédiatement
<span style="color: #9E9E9E">**Et**</span> seule la session du navigateur web reste active
<span style="color: #9E9E9E">**Et**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> que j'ai réinitialisé mon mot de passe avec un lien

<span style="color: #FF9800">**Quand**</span> j'essaie de réutiliser le même lien

<span style="color: #4CAF50">**Alors**</span> je vois le message "Ce lien a déjà été utilisé"
<span style="color: #9E9E9E">**Et**</span> je peux demander un nouveau lien si nécessaire

---

## 13. Nouveau lien invalide l'ancien

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé une réinitialisation et reçu un lien

<span style="color: #FF9800">**Quand**</span> je demande une nouvelle réinitialisation

<span style="color: #4CAF50">**Alors**</span> l'ancien lien est invalidé
<span style="color: #9E9E9E">**Et**</span> seul le nouveau lien fonctionne

---

## 14. Réinitialisation débloque un compte bloqué

<span style="color: #2196F3">**Étant donné**</span> que mon compte est bloqué après 5 tentatives de connexion

<span style="color: #FF9800">**Quand**</span> je réinitialise mon mot de passe via email

<span style="color: #4CAF50">**Alors**</span> le blocage est levé immédiatement
<span style="color: #9E9E9E">**Et**</span> je peux me connecter avec le nouveau mot de passe
<span style="color: #9E9E9E">**Et**</span> le compteur de tentatives est remis à 0

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté avec succès
## 1. Access token expire après 15 minutes

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu un access token
<span style="color: #9E9E9E">**Et**</span> que 15 minutes se sont écoulées

<span style="color: #FF9800">**Quand**</span> je fais une requête API avec cet access token

<span style="color: #4CAF50">**Alors**</span> la requête échoue avec le code 401
<span style="color: #9E9E9E">**Et**</span> je vois le message "Token expiré"

---

## 2. Refresh automatique du token avec refresh token

<span style="color: #2196F3">**Étant donné**</span> que mon access token a expiré
<span style="color: #9E9E9E">**Et**</span> que mon refresh token est valide

<span style="color: #FF9800">**Quand**</span> l'application demande un nouveau access token

<span style="color: #4CAF50">**Alors**</span> je reçois un nouvel access token valide pour 15 minutes
<span style="color: #9E9E9E">**Et**</span> je reçois un nouveau refresh token (rotation)
<span style="color: #9E9E9E">**Et**</span> l'ancien refresh token est invalidé

---

## 3. Refresh token expire après 30 jours d'inactivité

<span style="color: #2196F3">**Étant donné**</span> que je me suis connecté il y a 30 jours
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas utilisé l'application depuis

<span style="color: #FF9800">**Quand**</span> j'essaie d'utiliser mon refresh token

<span style="color: #4CAF50">**Alors**</span> la requête échoue
<span style="color: #9E9E9E">**Et**</span> je dois me reconnecter avec email/password

---

## 4. Prolongation automatique de la session si l'app est utilisée

<span style="color: #2196F3">**Étant donné**</span> que je me suis connecté il y a 25 jours
<span style="color: #9E9E9E">**Et**</span> que j'utilise l'application régulièrement

<span style="color: #FF9800">**Quand**</span> je fais une requête API

<span style="color: #4CAF50">**Alors**</span> ma session est automatiquement prolongée
<span style="color: #9E9E9E">**Et**</span> mon refresh token reste valide

---

## 5. Détection de token replay attack

<span style="color: #2196F3">**Étant donné**</span> que j'ai rafraîchi mon token
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu un nouveau refresh token

<span style="color: #FF9800">**Quand**</span> j'essaie de réutiliser l'ancien refresh token

<span style="color: #4CAF50">**Alors**</span> la requête échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Token invalide ou révoqué"
<span style="color: #9E9E9E">**Et**</span> toutes mes sessions sont révoquées par sécurité

---

## 6. Voir la liste des appareils connectés

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté sur 3 appareils différents

<span style="color: #FF9800">**Quand**</span> je consulte la liste de mes appareils connectés

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté sur mon iPhone et mon iPad

<span style="color: #FF9800">**Quand**</span> je révoque la session de mon iPad depuis les paramètres

<span style="color: #4CAF50">**Alors**</span> la session iPad est immédiatement déconnectée
<span style="color: #9E9E9E">**Et**</span> ma session iPhone reste active

---

## 8. Déconnecter tous les appareils sauf celui en cours

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté sur 4 appareils

<span style="color: #FF9800">**Quand**</span> je clique sur "Déconnecter tous les appareils"

<span style="color: #4CAF50">**Alors**</span> les 3 autres appareils sont déconnectés
<span style="color: #9E9E9E">**Et**</span> seul l'appareil actuel reste connecté

---

## 9. Alerte de connexion depuis nouveau device

<span style="color: #2196F3">**Étant donné**</span> que je me suis toujours connecté depuis Paris

<span style="color: #FF9800">**Quand**</span> je me connecte depuis un nouvel appareil à Lyon

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push sur mes autres appareils
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je me suis toujours connecté depuis la France

<span style="color: #FF9800">**Quand**</span> je me connecte depuis un appareil aux États-Unis

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push immédiate
<span style="color: #9E9E9E">**Et**</span> je reçois un email d'alerte de sécurité
<span style="color: #9E9E9E">**Et**</span> la nouvelle session nécessite une validation 2FA même si désactivée

---

## 11. Déconnexion après 30 jours d'inactivité totale

<span style="color: #2196F3">**Étant donné**</span> que je ne me suis pas connecté depuis 30 jours

<span style="color: #FF9800">**Quand**</span> j'ouvre l'application

<span style="color: #4CAF50">**Alors**</span> je suis automatiquement déconnecté
<span style="color: #9E9E9E">**Et**</span> je dois me reconnecter avec email/password
<span style="color: #9E9E9E">**Et**</span> je vois le message "Session expirée après 30 jours d'inactivité"

---

## 12. Sessions multiples simultanées autorisées

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté sur:

    | appareil |
    |---|
    | iPhone |
    | iPad |
    | PC Windows (Web) |


<span style="color: #FF9800">**Quand**</span> je fais des actions sur les 3 appareils simultanément

<span style="color: #4CAF50">**Alors**</span> toutes les sessions fonctionnent sans conflit
<span style="color: #9E9E9E">**Et**</span> chaque appareil maintient sa propre session

---

## 13. Validation de JWT via Zitadel

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu un access token JWT

<span style="color: #FF9800">**Quand**</span> l'API RoadWave valide le token

<span style="color: #4CAF50">**Alors**</span> la validation est faite localement avec la clé publique Zitadel
<span style="color: #9E9E9E">**Et**</span> aucune requête externe n'est effectuée (performance)
<span style="color: #9E9E9E">**Et**</span> 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 |


---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté à mon compte
## 1. Activer la 2FA TOTP (Time-based One-Time Password)

<span style="color: #2196F3">**Étant donné**</span> que la 2FA n'est pas activée sur mon compte

<span style="color: #FF9800">**Quand**</span> je choisis d'activer la 2FA TOTP

<span style="color: #4CAF50">**Alors**</span> je vois un QR code à scanner
<span style="color: #9E9E9E">**Et**</span> je vois le secret partagé en texte clair (backup)
<span style="color: #9E9E9E">**Et**</span> je dois entrer un code de vérification depuis mon app authenticator

<span style="color: #FF9800">**Quand**</span> je saisis un code TOTP valide

<span style="color: #4CAF50">**Alors**</span> la 2FA TOTP est activée avec succès
<span style="color: #9E9E9E">**Et**</span> je reçois des codes de backup (10 codes)

---

## 2. Connexion avec 2FA TOTP activée

<span style="color: #2196F3">**Étant donné**</span> que la 2FA TOTP est activée sur mon compte

<span style="color: #FF9800">**Quand**</span> je me connecte avec email/password

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers la page de saisie du code 2FA

<span style="color: #FF9800">**Quand**</span> je saisis un code TOTP valide de mon authenticator

<span style="color: #4CAF50">**Alors**</span> je suis connecté avec succès

---

## 3. Connexion échouée avec code TOTP invalide

<span style="color: #2196F3">**Étant donné**</span> que la 2FA TOTP est activée

<span style="color: #FF9800">**Quand**</span> je me connecte avec email/password
<span style="color: #9E9E9E">**Et**</span> que je saisis un code TOTP invalide "000000"

<span style="color: #4CAF50">**Alors**</span> la connexion échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Code d'authentification invalide"
<span style="color: #9E9E9E">**Et**</span> je peux réessayer

---

## 4. Utiliser un code de backup pour 2FA

<span style="color: #2196F3">**Étant donné**</span> que la 2FA TOTP est activée
<span style="color: #9E9E9E">**Et**</span> que j'ai perdu l'accès à mon authenticator

<span style="color: #FF9800">**Quand**</span> je me connecte avec email/password
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Utiliser un code de backup"
<span style="color: #9E9E9E">**Et**</span> que je saisis un code de backup valide

<span style="color: #4CAF50">**Alors**</span> je suis connecté avec succès
<span style="color: #9E9E9E">**Et**</span> le code de backup utilisé est invalidé
<span style="color: #9E9E9E">**Et**</span> il me reste 9 codes de backup

---

## 5. Activer la 2FA par email

<span style="color: #2196F3">**Étant donné**</span> que la 2FA n'est pas activée

<span style="color: #FF9800">**Quand**</span> je choisis d'activer la 2FA par email

<span style="color: #4CAF50">**Alors**</span> la 2FA email est activée immédiatement
<span style="color: #9E9E9E">**Et**</span> je vois le message "2FA email activée. Vous recevrez un code à chaque connexion"

---

## 6. Connexion avec 2FA email

<span style="color: #2196F3">**Étant donné**</span> que la 2FA email est activée

<span style="color: #FF9800">**Quand**</span> je me connecte avec email/password

<span style="color: #4CAF50">**Alors**</span> je reçois un email avec un code à 6 chiffres
<span style="color: #9E9E9E">**Et**</span> le code expire dans 10 minutes
<span style="color: #9E9E9E">**Et**</span> je dois saisir ce code pour terminer la connexion

---

## 7. Code 2FA email expiré

<span style="color: #2196F3">**Étant donné**</span> que la 2FA email est activée
<span style="color: #9E9E9E">**Et**</span> que je me suis connecté avec email/password
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu un code 2FA par email il y a 11 minutes

<span style="color: #FF9800">**Quand**</span> je saisis ce code

<span style="color: #4CAF50">**Alors**</span> la connexion échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Code expiré. Demandez un nouveau code."

---

## 8. Renvoyer le code 2FA email

<span style="color: #2196F3">**Étant donné**</span> que la 2FA email est activée
<span style="color: #9E9E9E">**Et**</span> que je suis sur la page de saisie du code 2FA

<span style="color: #FF9800">**Quand**</span> je clique sur "Renvoyer le code"

<span style="color: #4CAF50">**Alors**</span> je reçois un nouveau code par email
<span style="color: #9E9E9E">**Et**</span> l'ancien code est invalidé

---

## 9. Ajouter un appareil de confiance (skip 2FA pendant 30 jours)

<span style="color: #2196F3">**Étant donné**</span> que la 2FA TOTP est activée

<span style="color: #FF9800">**Quand**</span> je me connecte avec email/password et code TOTP
<span style="color: #9E9E9E">**Et**</span> que je coche "Ne plus demander sur cet appareil"

<span style="color: #4CAF50">**Alors**</span> je suis connecté avec succès
<span style="color: #9E9E9E">**Et**</span> cet appareil est enregistré comme "appareil de confiance"

<span style="color: #FF9800">**Quand**</span> je me reconnecte dans les 30 jours suivants sur ce même appareil

<span style="color: #4CAF50">**Alors**</span> je ne dois pas saisir de code 2FA

---

## 10. Appareil de confiance expire après 30 jours

<span style="color: #2196F3">**Étant donné**</span> que j'ai enregistré un appareil de confiance il y a 31 jours

<span style="color: #FF9800">**Quand**</span> je me connecte depuis cet appareil

<span style="color: #4CAF50">**Alors**</span> je dois saisir un code 2FA
<span style="color: #9E9E9E">**Et**</span> je vois le message "Appareil de confiance expiré. Veuillez vous authentifier"

---

## 11. Voir la liste des appareils de confiance

<span style="color: #2196F3">**Étant donné**</span> que j'ai enregistré 3 appareils de confiance

<span style="color: #FF9800">**Quand**</span> je consulte mes paramètres de sécurité

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai un iPhone enregistré comme appareil de confiance

<span style="color: #FF9800">**Quand**</span> je révoque cet appareil depuis les paramètres

<span style="color: #4CAF50">**Alors**</span> l'appareil est supprimé de la liste

<span style="color: #FF9800">**Quand**</span> je me reconnecte depuis cet iPhone

<span style="color: #4CAF50">**Alors**</span> je dois saisir un code 2FA

---

## 13. Révoquer tous les appareils de confiance

<span style="color: #2196F3">**Étant donné**</span> que j'ai 5 appareils de confiance enregistrés

<span style="color: #FF9800">**Quand**</span> je clique sur "Révoquer tous les appareils de confiance"

<span style="color: #4CAF50">**Alors**</span> tous les appareils sont révoqués
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai un appareil de confiance enregistré en France
<span style="color: #9E9E9E">**Et**</span> que je me connecte depuis ce même appareil mais avec une IP américaine

<span style="color: #FF9800">**Quand**</span> je tente de me connecter

<span style="color: #4CAF50">**Alors**</span> la 2FA est requise malgré l'appareil de confiance
<span style="color: #9E9E9E">**Et**</span> je vois le message "Connexion suspecte détectée. Authentification requise."

---

## 15. Désactiver la 2FA

<span style="color: #2196F3">**Étant donné**</span> que la 2FA TOTP est activée

<span style="color: #FF9800">**Quand**</span> je désactive la 2FA depuis mes paramètres
<span style="color: #9E9E9E">**Et**</span> que je confirme avec mon mot de passe

<span style="color: #4CAF50">**Alors**</span> la 2FA est désactivée
<span style="color: #9E9E9E">**Et**</span> tous les codes de backup sont invalidés
<span style="color: #9E9E9E">**Et**</span> tous les appareils de confiance sont révoqués

---

## 16. Régénérer les codes de backup

<span style="color: #2196F3">**Étant donné**</span> que la 2FA est activée
<span style="color: #9E9E9E">**Et**</span> que j'ai utilisé 8 codes de backup sur 10

<span style="color: #FF9800">**Quand**</span> je demande à régénérer les codes de backup

<span style="color: #4CAF50">**Alors**</span> je reçois 10 nouveaux codes
<span style="color: #9E9E9E">**Et**</span> tous les anciens codes (utilisés ou non) sont invalidés

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Auditeur avec email non vérifié - lecture illimitée

<span style="color: #2196F3">**Étant donné**</span> que je suis un auditeur avec email non vérifié

<span style="color: #FF9800">**Quand**</span> j'essaie d'écouter du contenu

<span style="color: #4CAF50">**Alors**</span> je peux écouter tous les contenus sans limite

---

## 2. Auditeur avec email non vérifié - création limitée à 5 contenus

<span style="color: #2196F3">**Étant donné**</span> que je suis un auditeur avec email non vérifié
<span style="color: #9E9E9E">**Et**</span> que j'ai créé 4 contenus

<span style="color: #FF9800">**Quand**</span> je crée un 5ème contenu

<span style="color: #4CAF50">**Alors**</span> le contenu est créé avec succès
<span style="color: #F44336">**Mais**</span> quand j'essaie de créer un 6ème contenu
<span style="color: #4CAF50">**Alors**</span> la création échoue
<span style="color: #9E9E9E">**Et**</span> 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éé

<span style="color: #2196F3">**Étant donné**</span> que je suis un auditeur avec email non vérifié
<span style="color: #9E9E9E">**Et**</span> que j'ai créé 2 contenus

<span style="color: #FF9800">**Quand**</span> je crée mon 3ème contenu

<span style="color: #4CAF50">**Alors**</span> le contenu est créé avec succès
<span style="color: #9E9E9E">**Et**</span> je vois une notification in-app "Vérifiez votre email pour débloquer la création illimitée"

---

## 4. Auditeur vérifie son email

<span style="color: #2196F3">**Étant donné**</span> que je suis un auditeur avec email non vérifié
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu un lien de vérification

<span style="color: #FF9800">**Quand**</span> je clique sur le lien de vérification dans l'email

<span style="color: #4CAF50">**Alors**</span> mon email est marqué comme vérifié
<span style="color: #9E9E9E">**Et**</span> je vois le message "Email vérifié avec succès"
<span style="color: #9E9E9E">**Et**</span> toutes les fonctionnalités sont débloquées

---

## 5. Créateur doit vérifier son email sous 7 jours pour monétisation

<span style="color: #2196F3">**Étant donné**</span> que je suis inscrit comme créateur
<span style="color: #9E9E9E">**Et**</span> que mon email n'est pas vérifié
<span style="color: #9E9E9E">**Et**</span> que je remplis les conditions de monétisation

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder au programme de monétisation

<span style="color: #4CAF50">**Alors**</span> l'accès est refusé
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec email non vérifié
<span style="color: #9E9E9E">**Et**</span> que j'ai créé 5 contenus

<span style="color: #FF9800">**Quand**</span> j'essaie de créer un 6ème contenu

<span style="color: #4CAF50">**Alors**</span> la création échoue
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec email non vérifié
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu un lien de vérification

<span style="color: #FF9800">**Quand**</span> je clique sur le lien de vérification

<span style="color: #4CAF50">**Alors**</span> mon email est marqué comme vérifié
<span style="color: #9E9E9E">**Et**</span> je peux publier des contenus illimités
<span style="color: #9E9E9E">**Et**</span> je peux accéder au programme de monétisation si j'en remplis les conditions

---

## 8. KYC impossible sans email vérifié

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec email non vérifié

<span style="color: #FF9800">**Quand**</span> j'essaie de compléter le KYC via Mangopay

<span style="color: #4CAF50">**Alors**</span> l'accès au KYC est refusé
<span style="color: #9E9E9E">**Et**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> que j'ai déjà vérifié mon email avec un lien

<span style="color: #FF9800">**Quand**</span> j'essaie de réutiliser le même lien de vérification

<span style="color: #4CAF50">**Alors**</span> la vérification échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Ce lien a déjà été utilisé"

---

## 10. Auditeur vérifié peut créer plus de 5 contenus

<span style="color: #2196F3">**Étant donné**</span> que je suis un auditeur avec email vérifié
<span style="color: #9E9E9E">**Et**</span> que j'ai créé 10 contenus

<span style="color: #FF9800">**Quand**</span> je crée un 11ème contenu

<span style="color: #4CAF50">**Alors**</span> le contenu est créé avec succès
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de limite de création

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis un créateur connecté
> <span style="color: #9E9E9E">**Et**</span> que mon fichier audio est encodé et prêt
## 1. Publication avec toutes les métadonnées obligatoires

<span style="color: #FF9800">**Quand**</span> 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 |


<span style="color: #4CAF50">**Alors**</span> la publication réussit
<span style="color: #9E9E9E">**Et**</span> mon contenu est soumis pour validation

---

## 2. Titre valide entre 5 et 100 caractères

<span style="color: #FF9800">**Quand**</span> je saisis un titre de 50 caractères

<span style="color: #4CAF50">**Alors**</span> le titre est accepté
<span style="color: #9E9E9E">**Et**</span> la validation passe

---

## 3. Titre trop court (<5 caractères)

<span style="color: #FF9800">**Quand**</span> je saisis un titre de 4 caractères "Test"

<span style="color: #4CAF50">**Alors**</span> la publication échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le titre doit contenir entre 5 et 100 caractères"

---

## 4. Titre trop long (>100 caractères)

<span style="color: #FF9800">**Quand**</span> je saisis un titre de 101 caractères

<span style="color: #4CAF50">**Alors**</span> la publication échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le titre doit contenir entre 5 et 100 caractères"

---

## 5. Titre à exactement 5 caractères accepté

<span style="color: #FF9800">**Quand**</span> je saisis un titre de exactement 5 caractères "Titre"

<span style="color: #4CAF50">**Alors**</span> le titre est accepté

---

## 6. Titre à exactement 100 caractères accepté

<span style="color: #FF9800">**Quand**</span> je saisis un titre de exactement 100 caractères

<span style="color: #4CAF50">**Alors**</span> le titre est accepté

---

## 7. Sélectionner type géo "Ancré"

<span style="color: #FF9800">**Quand**</span> je sélectionne le type géo "Ancré"

<span style="color: #4CAF50">**Alors**</span> le système applique une pondération géo de 0.7
<span style="color: #9E9E9E">**Et**</span> je dois définir une zone de diffusion précise

---

## 8. Sélectionner type géo "Contextuel"

<span style="color: #FF9800">**Quand**</span> je sélectionne le type géo "Contextuel"

<span style="color: #4CAF50">**Alors**</span> le système applique une pondération géo de 0.5
<span style="color: #9E9E9E">**Et**</span> je peux définir une zone ville/département/région

---

## 9. Sélectionner type géo "Neutre"

<span style="color: #FF9800">**Quand**</span> je sélectionne le type géo "Neutre"

<span style="color: #4CAF50">**Alors**</span> le système applique une pondération géo de 0.2
<span style="color: #9E9E9E">**Et**</span> je peux définir une zone nationale

---

## 10. Zone diffusion - Point GPS avec rayon

<span style="color: #FF9800">**Quand**</span> je choisis "Point GPS"
<span style="color: #9E9E9E">**Et**</span> que je définis les coordonnées (48.8584, 2.2945)
<span style="color: #9E9E9E">**Et**</span> que je définis un rayon de 500 mètres

<span style="color: #4CAF50">**Alors**</span> la zone est validée
<span style="color: #9E9E9E">**Et**</span> le contenu sera diffusé dans un rayon de 500m autour du point

---

## 11. Zone diffusion - Rayon minimum 100m

<span style="color: #FF9800">**Quand**</span> je définis un rayon de 50 mètres (< 100m)

<span style="color: #4CAF50">**Alors**</span> la validation échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le rayon doit être entre 100m et 10km"

---

## 12. Zone diffusion - Rayon maximum 10km

<span style="color: #FF9800">**Quand**</span> je définis un rayon de 15 km (> 10km)

<span style="color: #4CAF50">**Alors**</span> la validation échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le rayon doit être entre 100m et 10km"

---

## 13. Zone diffusion - Ville depuis référentiel INSEE

<span style="color: #FF9800">**Quand**</span> je choisis "Ville"

<span style="color: #4CAF50">**Alors**</span> je vois une liste de villes du référentiel INSEE

<span style="color: #FF9800">**Quand**</span> je sélectionne "Paris (75000)"

<span style="color: #4CAF50">**Alors**</span> la zone est définie sur toute la ville de Paris

---

## 14. Zone diffusion - Département

<span style="color: #FF9800">**Quand**</span> je choisis "Département"
<span style="color: #9E9E9E">**Et**</span> que je sélectionne "Ille-et-Vilaine (35)"

<span style="color: #4CAF50">**Alors**</span> la zone couvre tout le département 35

---

## 15. Zone diffusion - Région

<span style="color: #FF9800">**Quand**</span> je choisis "Région"
<span style="color: #9E9E9E">**Et**</span> que je sélectionne "Bretagne"

<span style="color: #4CAF50">**Alors**</span> la zone couvre toute la région Bretagne

---

## 16. Zone diffusion - National

<span style="color: #FF9800">**Quand**</span> je choisis "National"

<span style="color: #4CAF50">**Alors**</span> la zone couvre toute la France
<span style="color: #9E9E9E">**Et**</span> aucune restriction géographique n'est appliquée

---

## 17. Zones mutuellement exclusives

<span style="color: #2196F3">**Étant donné**</span> que j'ai sélectionné "Point GPS"

<span style="color: #FF9800">**Quand**</span> j'essaie de sélectionner également "Ville"

<span style="color: #4CAF50">**Alors**</span> la première sélection est remplacée
<span style="color: #9E9E9E">**Et**</span> seule "Ville" reste active

---

## 18. Sélectionner 1 tag minimum

<span style="color: #FF9800">**Quand**</span> je sélectionne 1 tag "Voyage"

<span style="color: #4CAF50">**Alors**</span> la validation passe
<span style="color: #9E9E9E">**Et**</span> le contenu est tagué "Voyage"

---

## 19. Sélectionner 3 tags maximum

<span style="color: #FF9800">**Quand**</span> je sélectionne 3 tags "Automobile", "Technologie", "Sport"

<span style="color: #4CAF50">**Alors**</span> la validation passe
<span style="color: #9E9E9E">**Et**</span> le contenu est tagué avec les 3 tags

---

## 20. Impossible de sélectionner 0 tag

<span style="color: #FF9800">**Quand**</span> j'essaie de publier sans sélectionner de tag

<span style="color: #4CAF50">**Alors**</span> la publication échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Vous devez sélectionner entre 1 et 3 tags"

---

## 21. Impossible de sélectionner 4 tags

<span style="color: #FF9800">**Quand**</span> j'essaie de sélectionner 4 tags

<span style="color: #4CAF50">**Alors**</span> le 4ème tag ne peut pas être ajouté
<span style="color: #9E9E9E">**Et**</span> je vois le message "Maximum 3 tags"

---

## 22. Tags disponibles dans la liste

<span style="color: #FF9800">**Quand**</span> je consulte la liste des tags

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #FF9800">**Quand**</span> j'essaie de publier sans classification âge

<span style="color: #4CAF50">**Alors**</span> la publication échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Vous devez sélectionner une classification d'âge"

---

## 24. 📋 Plan: Sélectionner classification âge

<span style="color: #FF9800">**Quand**</span> je sélectionne la classification "<classification>"

<span style="color: #4CAF50">**Alors**</span> le contenu sera visible pour "<public_cible>"

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que je choisis le type géo "Ancré"
<span style="color: #9E9E9E">**Et**</span> que mon tag principal est "Voyage"

<span style="color: #FF9800">**Quand**</span> la publication est soumise

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je choisis "Contextuel"

<span style="color: #FF9800">**Quand**</span> l'image est générée

<span style="color: #4CAF50">**Alors**</span> l'icône est 🌍 (Contextuel)

---

## 27. Image de couverture type Neutre

<span style="color: #2196F3">**Étant donné**</span> que je choisis "Neutre"

<span style="color: #FF9800">**Quand**</span> l'image est générée

<span style="color: #4CAF50">**Alors**</span> l'icône est 🎧 (Neutre)

---

## 28. 📋 Plan: Couleur selon tag principal

<span style="color: #2196F3">**Étant donné**</span> que mon tag principal est "<tag>"

<span style="color: #FF9800">**Quand**</span> l'image est générée

<span style="color: #4CAF50">**Alors**</span> la couleur de fond est "<couleur>"

**📊 Exemples de données:**

| tag | couleur |
|---|---|
| Automobile | Bleu |
| Voyage | Vert |
| Musique | Rouge |
| Économie | Gris |
| Sport | Orange |

---

## 29. Champs optionnels non obligatoires

<span style="color: #FF9800">**Quand**</span> je publie sans description
<span style="color: #9E9E9E">**Et**</span> sans image de couverture personnalisée

<span style="color: #4CAF50">**Alors**</span> la publication réussit
<span style="color: #9E9E9E">**Et**</span> les champs optionnels restent vides
<span style="color: #9E9E9E">**Et**</span> une image par défaut est générée

---

## 30. Temps de publication estimé 2 minutes

<span style="color: #2196F3">**Étant donné**</span> que mon fichier audio est prêt

<span style="color: #FF9800">**Quand**</span> je commence à remplir les métadonnées

<span style="color: #4CAF50">**Alors**</span> je peux publier en environ 2 minutes

---

## 31. Publication rapide sans friction

<span style="color: #FF9800">**Quand**</span> je publie mon premier contenu

<span style="color: #4CAF50">**Alors**</span> aucun champ complexe n'est demandé
<span style="color: #9E9E9E">**Et**</span> je ne suis pas bloqué sur description ou image
<span style="color: #9E9E9E">**Et**</span> la publication est fluide

---

## 32. Prévisualisation avant publication

<span style="color: #2196F3">**Étant donné**</span> que j'ai rempli toutes les métadonnées

<span style="color: #FF9800">**Quand**</span> je clique sur "Prévisualiser"

<span style="color: #4CAF50">**Alors**</span> je vois un aperçu de mon contenu:

    | élément | affiché |
    |---|---|
    | Titre | ✅ |
    | Image couverture | ✅ |
    | Tags | ✅ |
    | Zone diffusion | ✅ |
    | Durée audio | ✅ |
    | Classification | ✅ |


---

## 33. Enregistrer brouillon

<span style="color: #2196F3">**Étant donné**</span> que j'ai commencé à remplir les métadonnées

<span style="color: #FF9800">**Quand**</span> je clique sur "Enregistrer brouillon"

<span style="color: #4CAF50">**Alors**</span> mes métadonnées sont sauvegardées
<span style="color: #9E9E9E">**Et**</span> je peux reprendre la publication plus tard

---

## 34. Reprendre brouillon

<span style="color: #2196F3">**Étant donné**</span> que j'ai un brouillon sauvegardé

<span style="color: #FF9800">**Quand**</span> j'accède à mes contenus

<span style="color: #4CAF50">**Alors**</span> je vois le brouillon avec statut "📝 Brouillon"
<span style="color: #9E9E9E">**Et**</span> je peux reprendre la publication

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis un créateur connecté
> <span style="color: #9E9E9E">**Et**</span> que j'ai publié un contenu
## 1. Modifier le titre d'un contenu

<span style="color: #2196F3">**Étant donné**</span> que mon contenu a le titre "Histoire de Paris"

<span style="color: #FF9800">**Quand**</span> je modifie le titre en "Histoire fascinante de Paris"

<span style="color: #4CAF50">**Alors**</span> la modification est enregistrée immédiatement
<span style="color: #9E9E9E">**Et**</span> je vois le message "Titre modifié avec succès"

---

## 2. Correction de coquilles dans le titre

<span style="color: #2196F3">**Étant donné**</span> que mon titre contient une faute "Histoore de Paris"

<span style="color: #FF9800">**Quand**</span> je corrige en "Histoire de Paris"

<span style="color: #4CAF50">**Alors**</span> la modification est acceptée
<span style="color: #9E9E9E">**Et**</span> le titre corrigé est affiché

---

## 3. Ajouter une description ultérieurement

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié sans description

<span style="color: #FF9800">**Quand**</span> j'ajoute une description "Découvrez l'histoire de la capitale"

<span style="color: #4CAF50">**Alors**</span> la description est enregistrée
<span style="color: #9E9E9E">**Et**</span> elle est visible sur la page du contenu

---

## 4. Modifier la description existante

<span style="color: #2196F3">**Étant donné**</span> que mon contenu a déjà une description

<span style="color: #FF9800">**Quand**</span> je modifie la description

<span style="color: #4CAF50">**Alors**</span> la nouvelle description remplace l'ancienne
<span style="color: #9E9E9E">**Et**</span> la modification est immédiate

---

## 5. Modifier les tags pour ajuster pertinence

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est tagué "Sport", "Musique"

<span style="color: #FF9800">**Quand**</span> je change les tags en "Sport", "Santé"

<span style="color: #4CAF50">**Alors**</span> les nouveaux tags sont appliqués
<span style="color: #9E9E9E">**Et**</span> l'algorithme utilise les nouveaux tags pour recommandations

---

## 6. Personnaliser l'image de couverture

<span style="color: #2196F3">**Étant donné**</span> que mon contenu a une image auto-générée

<span style="color: #FF9800">**Quand**</span> j'uploade une image personnalisée 800×800px

<span style="color: #4CAF50">**Alors**</span> l'image personnalisée remplace l'image par défaut
<span style="color: #9E9E9E">**Et**</span> elle est visible sur le contenu

---

## 7. Impossible de modifier l'audio

<span style="color: #2196F3">**Étant donné**</span> que mon contenu audio est publié

<span style="color: #FF9800">**Quand**</span> j'essaie de remplacer le fichier audio

<span style="color: #4CAF50">**Alors**</span> la modification est refusée
<span style="color: #9E9E9E">**Et**</span> je vois le message "L'audio ne peut pas être modifié après publication"

---

## 8. Raison - Éviter fraude audio

<span style="color: #2196F3">**Étant donné**</span> que je veux changer l'audio après validation

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est diffusé à Paris

<span style="color: #FF9800">**Quand**</span> j'essaie de changer la zone en "National"

<span style="color: #4CAF50">**Alors**</span> la modification est refusée
<span style="color: #9E9E9E">**Et**</span> je vois le message "La zone de diffusion ne peut pas être modifiée"

---

## 10. Raison - Éviter manipulation algorithme

<span style="color: #2196F3">**Étant donné**</span> que je veux changer ma zone

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est type "Neutre" (pondération 0.2)

<span style="color: #FF9800">**Quand**</span> j'essaie de changer en "Ancré" (pondération 0.7)

<span style="color: #4CAF50">**Alors**</span> la modification est refusée
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le type géographique ne peut pas être modifié"

---

## 12. Raison - Éviter abus de pondération

<span style="color: #2196F3">**Étant donné**</span> que je veux changer le type géo

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est classé "Tout public"

<span style="color: #FF9800">**Quand**</span> j'essaie de changer en "18+"

<span style="color: #4CAF50">**Alors**</span> la modification est refusée
<span style="color: #9E9E9E">**Et**</span> je vois le message "La classification d'âge ne peut pas être modifiée"

---

## 14. Raison - Sécurité mineurs

<span style="color: #2196F3">**Étant donné**</span> que je veux changer la classification

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je veux absolument changer l'audio

<span style="color: #FF9800">**Quand**</span> je consulte les options

<span style="color: #4CAF50">**Alors**</span> je vois "Supprimer et republier le contenu"
<span style="color: #9E9E9E">**Et**</span> c'est la seule solution disponible

---

## 16. Republication après suppression - créateur <3 validations

<span style="color: #2196F3">**Étant donné**</span> que je suis un nouveau créateur (2 contenus validés)
<span style="color: #9E9E9E">**Et**</span> que je supprime puis republie un contenu

<span style="color: #FF9800">**Quand**</span> je republie avec les modifications

<span style="color: #4CAF50">**Alors**</span> le contenu repasse en file de validation
<span style="color: #9E9E9E">**Et**</span> une nouvelle validation est effectuée

---

## 17. Republication après suppression - créateur vérifié

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur vérifié (≥3 contenus validés)
<span style="color: #9E9E9E">**Et**</span> que je supprime puis republie un contenu

<span style="color: #FF9800">**Quand**</span> je republie avec les modifications

<span style="color: #4CAF50">**Alors**</span> le contenu est publié immédiatement
<span style="color: #9E9E9E">**Et**</span> aucune validation préalable n'est requise

---

## 18. Suppression de contenu immédiate

<span style="color: #FF9800">**Quand**</span> je clique sur "Supprimer le contenu"
<span style="color: #9E9E9E">**Et**</span> que je confirme la suppression

<span style="color: #4CAF50">**Alors**</span> le contenu est supprimé immédiatement
<span style="color: #9E9E9E">**Et**</span> disparaît de la liste publique

---

## 19. Confirmation avant suppression

<span style="color: #FF9800">**Quand**</span> je clique sur "Supprimer"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai supprimé un contenu

<span style="color: #FF9800">**Quand**</span> j'essaie de le récupérer

<span style="color: #4CAF50">**Alors**</span> la récupération est impossible
<span style="color: #9E9E9E">**Et**</span> le contenu est définitivement perdu

---

## 21. Suppression BDD + CDN sous 5 minutes

<span style="color: #FF9800">**Quand**</span> je supprime un contenu

<span style="color: #4CAF50">**Alors**</span> l'entrée en base de données est marquée "deleted"
<span style="color: #9E9E9E">**Et**</span> les fichiers CDN sont marqués pour suppression
<span style="color: #9E9E9E">**Et**</span> la suppression effective a lieu sous 5 minutes

---

## 22. Historique auditeurs conservé anonymisé

<span style="color: #2196F3">**Étant donné**</span> que 1000 personnes ont écouté mon contenu

<span style="color: #FF9800">**Quand**</span> je supprime le contenu

<span style="color: #4CAF50">**Alors**</span> leur historique est conservé
<span style="color: #F44336">**Mais**</span> marqué "Contenu supprimé par créateur"
<span style="color: #9E9E9E">**Et**</span> la durée d'écoute est conservée pour leurs stats

---

## 23. Analytics plateforme anonymisées conservées

<span style="color: #2196F3">**Étant donné**</span> que mon contenu a généré 10K écoutes

<span style="color: #FF9800">**Quand**</span> je supprime le contenu

<span style="color: #4CAF50">**Alors**</span> les métriques globales sont conservées anonymement:

    | métrique | conservée |
    |---|---|
    | Total écoutes | ✅ (anonyme) |
    | Durée totale | ✅ (anonyme) |
    | Catégorie | ✅ (anonyme) |
    | Auteur | ❌ (anonymisé) |

<span style="color: #9E9E9E">**Et**</span> c'est conforme RGPD

---

## 24. Fichiers CDN supprimés sous 24h

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est supprimé

<span style="color: #FF9800">**Quand**</span> 24 heures s'écoulent

<span style="color: #4CAF50">**Alors**</span> tous les fichiers audio sont purgés du CDN Bunny
<span style="color: #9E9E9E">**Et**</span> l'espace de stockage est libéré

---

## 25. Pas de notification aux auditeurs

<span style="color: #2196F3">**Étant donné**</span> que 500 utilisateurs ont écouté mon contenu

<span style="color: #FF9800">**Quand**</span> je supprime le contenu

<span style="color: #4CAF50">**Alors**</span> aucune notification n'est envoyée aux auditeurs
<span style="color: #9E9E9E">**Et**</span> il n'y a pas d'effet Streisand

---

## 26. Auditeur tente de réécouter contenu supprimé

<span style="color: #2196F3">**Étant donné**</span> qu'un auditeur a écouté mon contenu
<span style="color: #9E9E9E">**Et**</span> que j'ai supprimé ce contenu

<span style="color: #FF9800">**Quand**</span> l'auditeur tente de le réécouter depuis son historique

<span style="color: #4CAF50">**Alors**</span> il voit le message "Ce contenu n'est plus disponible"
<span style="color: #9E9E9E">**Et**</span> la lecture est impossible

---

## 27. Historique auditeur conserve trace

<span style="color: #2196F3">**Étant donné**</span> qu'un auditeur a écouté mon contenu le 15 janvier
<span style="color: #9E9E9E">**Et**</span> que je supprime le contenu le 20 janvier

<span style="color: #FF9800">**Quand**</span> l'auditeur consulte son historique

<span style="color: #4CAF50">**Alors**</span> il voit "Vous avez écouté ce contenu le 15 janvier 2026"
<span style="color: #9E9E9E">**Et**</span> le titre est remplacé par "Contenu supprimé"
<span style="color: #9E9E9E">**Et**</span> la date d'écoute est conservée

---

## 28. Statistiques créateur après suppression

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié 10 contenus
<span style="color: #9E9E9E">**Et**</span> que je supprime 2 contenus

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques globales

<span style="color: #4CAF50">**Alors**</span> je vois:

    | métrique | valeur |
    |---|---|
    | Contenus publiés | 8 (actifs) |
    | Total historique | 10 |
    | Suppressions | 2 |

<span style="color: #9E9E9E">**Et**</span> l'historique des suppressions est visible

---

## 29. Limite de modifications par contenu

<span style="color: #2196F3">**Étant donné**</span> que j'ai modifié un titre 10 fois

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier une 11ème fois

<span style="color: #4CAF50">**Alors**</span> la modification est acceptée

---

## 30. Historique des modifications visible

<span style="color: #2196F3">**Étant donné**</span> que j'ai modifié un contenu plusieurs fois

<span style="color: #FF9800">**Quand**</span> je consulte l'historique

<span style="color: #4CAF50">**Alors**</span> je vois:

    | date | modification |
    |---|---|
    | 21/01/2026 | Titre changé |
    | 20/01/2026 | Tags modifiés |
    | 19/01/2026 | Description ajoutée |

<span style="color: #9E9E9E">**Et**</span> je peux tracer toutes les modifications

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis un créateur connecté
## 1. Upload fichier MP3 valide

<span style="color: #FF9800">**Quand**</span> j'uploade un fichier MP3 de 50 MB et 30 minutes

<span style="color: #4CAF50">**Alors**</span> l'upload réussit
<span style="color: #9E9E9E">**Et**</span> le fichier est envoyé vers Bunny Storage temporaire
<span style="color: #9E9E9E">**Et**</span> un job d'encodage asynchrone est lancé

---

## 2. Upload fichier AAC valide (.aac)

<span style="color: #FF9800">**Quand**</span> j'uploade un fichier AAC de 80 MB et 1 heure

<span style="color: #4CAF50">**Alors**</span> l'upload réussit
<span style="color: #9E9E9E">**Et**</span> le fichier est accepté
<span style="color: #9E9E9E">**Et**</span> l'encodage démarre

---

## 3. Upload fichier M4A valide

<span style="color: #FF9800">**Quand**</span> j'uploade un fichier M4A de 100 MB et 2 heures

<span style="color: #4CAF50">**Alors**</span> l'upload réussit
<span style="color: #9E9E9E">**Et**</span> le fichier est traité comme AAC
<span style="color: #9E9E9E">**Et**</span> l'encodage démarre

---

## 4. Rejet fichier WAV (non supporté)

<span style="color: #FF9800">**Quand**</span> j'essaie d'uploader un fichier WAV

<span style="color: #4CAF50">**Alors**</span> l'upload échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)"

---

## 5. Rejet fichier FLAC (non supporté)

<span style="color: #FF9800">**Quand**</span> j'essaie d'uploader un fichier FLAC

<span style="color: #4CAF50">**Alors**</span> l'upload échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)"

---

## 6. Validation taille maximale 200 MB

<span style="color: #FF9800">**Quand**</span> j'essaie d'uploader un fichier MP3 de 201 MB

<span style="color: #4CAF50">**Alors**</span> l'upload échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Fichier trop volumineux (max 200 MB)"

---

## 7. Upload à la limite de 200 MB accepté

<span style="color: #FF9800">**Quand**</span> j'uploade un fichier MP3 de exactement 200 MB

<span style="color: #4CAF50">**Alors**</span> l'upload réussit
<span style="color: #9E9E9E">**Et**</span> le fichier est accepté

---

## 8. Validation durée maximale 4 heures

<span style="color: #FF9800">**Quand**</span> j'essaie d'uploader un fichier de 4h 10min

<span style="color: #4CAF50">**Alors**</span> l'upload échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Durée trop longue (max 4 heures)"

---

## 9. Upload à la limite de 4h accepté

<span style="color: #FF9800">**Quand**</span> j'uploade un fichier de exactement 4 heures

<span style="color: #4CAF50">**Alors**</span> l'upload réussit
<span style="color: #9E9E9E">**Et**</span> le fichier est accepté

---

## 10. Validation format côté client

<span style="color: #FF9800">**Quand**</span> je sélectionne un fichier dans l'interface

<span style="color: #4CAF50">**Alors**</span> la validation du format est faite immédiatement côté client
<span style="color: #9E9E9E">**Et**</span> je suis informé avant même de lancer l'upload si le format est invalide

---

## 11. Double validation côté backend

<span style="color: #2196F3">**Étant donné**</span> qu'un fichier a passé la validation client

<span style="color: #FF9800">**Quand**</span> le backend reçoit le fichier

<span style="color: #4CAF50">**Alors**</span> une validation supplémentaire est effectuée
<span style="color: #9E9E9E">**Et**</span> le format et l'intégrité sont vérifiés

---

## 12. Pipeline d'encodage - étape 1 upload

<span style="color: #FF9800">**Quand**</span> j'uploade un fichier MP3 valide

<span style="color: #4CAF50">**Alors**</span> le fichier est stocké temporairement dans Bunny Storage
<span style="color: #9E9E9E">**Et**</span> un job d'encodage est mis en file d'attente

---

## 13. Pipeline d'encodage - validation format

<span style="color: #2196F3">**Étant donné**</span> qu'un job d'encodage est lancé

<span style="color: #FF9800">**Quand**</span> le worker Go traite le fichier

<span style="color: #4CAF50">**Alors**</span> le format est validé avec FFmpeg
<span style="color: #9E9E9E">**Et**</span> l'intégrité du fichier est vérifiée

---

## 14. Pipeline d'encodage - génération 3 profils Opus

<span style="color: #2196F3">**Étant donné**</span> qu'un fichier audio est validé

<span style="color: #FF9800">**Quand**</span> l'encodage démarre

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que les profils Opus sont générés

<span style="color: #FF9800">**Quand**</span> l'encodage continue

<span style="color: #4CAF50">**Alors**</span> un fichier manifest .m3u8 est créé
<span style="color: #9E9E9E">**Et**</span> des segments .ts sont générés
<span style="color: #9E9E9E">**Et**</span> le contenu est prêt pour streaming HLS

---

## 16. Pipeline d'encodage - génération image par défaut

<span style="color: #2196F3">**Étant donné**</span> que l'encodage est en cours

<span style="color: #FF9800">**Quand**</span> les métadonnées sont traitées

<span style="color: #4CAF50">**Alors**</span> une image de couverture par défaut est générée
<span style="color: #9E9E9E">**Et**</span> l'image fait 800×800px au format PNG

---

## 17. Pipeline d'encodage - suppression fichier original

<span style="color: #2196F3">**Étant donné**</span> que l'encodage est terminé avec succès

<span style="color: #FF9800">**Quand**</span> tous les fichiers de sortie sont générés

<span style="color: #4CAF50">**Alors**</span> le fichier original MP3/AAC est supprimé
<span style="color: #9E9E9E">**Et**</span> seuls les profils Opus et HLS sont conservés
<span style="color: #9E9E9E">**Et**</span> l'espace de stockage est économisé

---

## 18. Temps d'encodage contenu 5 minutes

<span style="color: #2196F3">**Étant donné**</span> qu'un fichier de 5 minutes est uploadé

<span style="color: #FF9800">**Quand**</span> l'encodage démarre

<span style="color: #4CAF50">**Alors**</span> l'encodage prend environ 30 secondes
<span style="color: #9E9E9E">**Et**</span> je reçois une notification "Contenu prêt à publier"

---

## 19. Temps d'encodage podcast 1 heure

<span style="color: #2196F3">**Étant donné**</span> qu'un fichier de 1 heure est uploadé

<span style="color: #FF9800">**Quand**</span> l'encodage démarre

<span style="color: #4CAF50">**Alors**</span> l'encodage prend environ 5 minutes
<span style="color: #9E9E9E">**Et**</span> une barre de progression est affichée

---

## 20. Temps d'encodage podcast 4 heures

<span style="color: #2196F3">**Étant donné**</span> qu'un fichier de 4 heures est uploadé

<span style="color: #FF9800">**Quand**</span> l'encodage démarre

<span style="color: #4CAF50">**Alors**</span> l'encodage prend environ 20 minutes
<span style="color: #9E9E9E">**Et**</span> je peux fermer l'app (traitement asynchrone)

---

## 21. Notification "Contenu prêt à publier"

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est en cours d'encodage

<span style="color: #FF9800">**Quand**</span> l'encodage se termine avec succès

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push "✅ Votre contenu est prêt à publier"
<span style="color: #9E9E9E">**Et**</span> je peux accéder à l'interface de publication

---

## 22. Échec d'encodage - fichier corrompu

<span style="color: #2196F3">**Étant donné**</span> qu'un fichier MP3 corrompu est uploadé

<span style="color: #FF9800">**Quand**</span> l'encodage démarre

<span style="color: #4CAF50">**Alors**</span> l'encodage échoue
<span style="color: #9E9E9E">**Et**</span> je reçois une notification "❌ Erreur d'encodage: fichier corrompu"
<span style="color: #9E9E9E">**Et**</span> le fichier temporaire est supprimé

---

## 23. Écoute accélérée - vitesses disponibles

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est publié

<span style="color: #FF9800">**Quand**</span> un auditeur écoute le contenu

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un modérateur
<span style="color: #9E9E9E">**Et**</span> qu'un contenu de 30 secondes est à valider

<span style="color: #FF9800">**Quand**</span> je l'écoute à 2.0x

<span style="color: #4CAF50">**Alors**</span> je termine l'écoute en 15 secondes
<span style="color: #9E9E9E">**Et**</span> ma productivité est doublée

---

## 25. Écoute accélérée pour auditeurs

<span style="color: #2196F3">**Étant donné**</span> que je suis un auditeur
<span style="color: #9E9E9E">**Et**</span> qu'un podcast de 1 heure est disponible

<span style="color: #FF9800">**Quand**</span> je configure la vitesse à 1.5x

<span style="color: #4CAF50">**Alors**</span> j'écoute le podcast en 40 minutes
<span style="color: #9E9E9E">**Et**</span> je gagne 20 minutes

---

## 26. Sauvegarde préférence vitesse d'écoute

<span style="color: #2196F3">**Étant donné**</span> que je configure la vitesse à 1.5x

<span style="color: #FF9800">**Quand**</span> j'écoute plusieurs contenus

<span style="color: #4CAF50">**Alors**</span> tous les contenus sont lus à 1.5x par défaut
<span style="color: #9E9E9E">**Et**</span> ma préférence est sauvegardée

---

## 27. Scalabilité horizontale des workers

<span style="color: #2196F3">**Étant donné**</span> que 100 contenus sont uploadés simultanément

<span style="color: #FF9800">**Quand**</span> les jobs d'encodage sont distribués

<span style="color: #4CAF50">**Alors**</span> plusieurs workers Go traitent les jobs en parallèle
<span style="color: #9E9E9E">**Et**</span> Kubernetes scale automatiquement les pods
<span style="color: #9E9E9E">**Et**</span> tous les contenus sont encodés sans délai excessif

---

## 28. Statut d'encodage visible

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est en cours d'encodage

<span style="color: #FF9800">**Quand**</span> je consulte mes contenus

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que l'encodage de mon contenu a échoué

<span style="color: #FF9800">**Quand**</span> je clique sur "Réessayer"

<span style="color: #4CAF50">**Alors**</span> un nouveau job d'encodage est lancé
<span style="color: #9E9E9E">**Et**</span> je peux tenter à nouveau

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis un nouveau créateur
## 1. Premier contenu passe en file de validation

<span style="color: #FF9800">**Quand**</span> je publie mon premier contenu

<span style="color: #4CAF50">**Alors**</span> le contenu passe en file d'attente modération
<span style="color: #9E9E9E">**Et**</span> je vois le message "Votre contenu est en cours de validation (24-48h)"
<span style="color: #9E9E9E">**Et**</span> le contenu n'est pas encore visible publiquement

---

## 2. Deuxième contenu passe également en validation

<span style="color: #2196F3">**Étant donné**</span> que mon premier contenu a été validé

<span style="color: #FF9800">**Quand**</span> je publie mon deuxième contenu

<span style="color: #4CAF50">**Alors**</span> le contenu passe en file d'attente modération
<span style="color: #9E9E9E">**Et**</span> le délai estimé est 24-48h

---

## 3. Troisième contenu - dernière validation

<span style="color: #2196F3">**Étant donné**</span> que mes 2 premiers contenus ont été validés

<span style="color: #FF9800">**Quand**</span> je publie mon troisième contenu

<span style="color: #4CAF50">**Alors**</span> le contenu passe en file d'attente modération
<span style="color: #9E9E9E">**Et**</span> je vois "Dernière validation avant statut vérifié ✓"

---

## 4. Modérateur écoute 30 secondes du contenu

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est en file de validation

<span style="color: #FF9800">**Quand**</span> le modérateur junior l'examine

<span style="color: #4CAF50">**Alors**</span> il écoute les 30 premières secondes
<span style="color: #9E9E9E">**Et**</span> il vérifie les métadonnées

---

## 5. Validation - Qualité audio acceptable

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a une qualité audio claire

<span style="color: #FF9800">**Quand**</span> le modérateur l'écoute

<span style="color: #4CAF50">**Alors**</span> il vérifie que l'audio est compréhensible
<span style="color: #9E9E9E">**Et**</span> qu'il n'y a pas de grésillement excessif

---

## 6. Rejet - Qualité audio insuffisante

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a un audio très grésillant

<span style="color: #FF9800">**Quand**</span> le modérateur l'écoute

<span style="color: #4CAF50">**Alors**</span> le contenu est rejeté
<span style="color: #9E9E9E">**Et**</span> la raison est "Qualité audio insuffisante"

---

## 7. Validation - Respect des règles

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu respecte les règles

<span style="color: #FF9800">**Quand**</span> le modérateur l'examine

<span style="color: #4CAF50">**Alors**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu contient des propos haineux

<span style="color: #FF9800">**Quand**</span> le modérateur l'écoute

<span style="color: #4CAF50">**Alors**</span> le contenu est rejeté immédiatement
<span style="color: #9E9E9E">**Et**</span> la raison est "Contenu haineux (violation des règles)"
<span style="color: #9E9E9E">**Et**</span> le créateur peut recevoir un strike

---

## 9. Validation - Classification âge cohérente

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu familial est classé "Tout public"

<span style="color: #FF9800">**Quand**</span> le modérateur l'écoute

<span style="color: #4CAF50">**Alors**</span> il vérifie que la classification correspond au contenu
<span style="color: #9E9E9E">**Et**</span> le contenu est accepté

---

## 10. Rejet - Classification incorrecte

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu adulte est classé "Tout public"

<span style="color: #FF9800">**Quand**</span> le modérateur détecte l'incohérence

<span style="color: #4CAF50">**Alors**</span> le contenu est rejeté
<span style="color: #9E9E9E">**Et**</span> la raison est "Classification d'âge incorrecte"

---

## 11. Validation - Tags pertinents

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu sur l'automobile est tagué "Automobile", "Technologie"

<span style="color: #FF9800">**Quand**</span> le modérateur vérifie les tags

<span style="color: #4CAF50">**Alors**</span> il confirme que les tags correspondent au contenu
<span style="color: #9E9E9E">**Et**</span> le contenu est accepté

---

## 12. Rejet - Tags non pertinents

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu musical est tagué "Automobile", "Sport"

<span style="color: #FF9800">**Quand**</span> le modérateur détecte l'incohérence

<span style="color: #4CAF50">**Alors**</span> le contenu est rejeté
<span style="color: #9E9E9E">**Et**</span> la raison est "Tags non pertinents avec le contenu"

---

## 13. Validation - Zone diffusion cohérente

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide de la Tour Eiffel est en "Point GPS" Paris

<span style="color: #FF9800">**Quand**</span> le modérateur vérifie la cohérence

<span style="color: #4CAF50">**Alors**</span> la zone est appropriée
<span style="color: #9E9E9E">**Et**</span> le contenu est accepté

---

## 14. Rejet - Zone incohérente

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide de la Tour Eiffel est en zone "National"

<span style="color: #FF9800">**Quand**</span> le modérateur détecte l'incohérence

<span style="color: #4CAF50">**Alors**</span> le contenu est rejeté
<span style="color: #9E9E9E">**Et**</span> la raison est "Zone de diffusion incohérente (devrait être Point GPS)"

---

## 15. Délai de validation 24-48h jours ouvrés

<span style="color: #2196F3">**Étant donné**</span> que je publie un contenu un lundi

<span style="color: #FF9800">**Quand**</span> le contenu entre en file de validation

<span style="color: #4CAF50">**Alors**</span> le délai estimé est 24-48h (mercredi maximum)

---

## 16. Délai étendu le weekend

<span style="color: #2196F3">**Étant donné**</span> que je publie un contenu un vendredi soir

<span style="color: #FF9800">**Quand**</span> le contenu entre en file de validation

<span style="color: #4CAF50">**Alors**</span> le délai peut atteindre 72h (lundi)
<span style="color: #9E9E9E">**Et**</span> je vois "Validation en cours, délai 24-72h (weekend)"

---

## 17. Priorité FIFO (First In First Out)

<span style="color: #2196F3">**Étant donné**</span> que 10 contenus sont en file de validation

<span style="color: #FF9800">**Quand**</span> les modérateurs traitent la file

<span style="color: #4CAF50">**Alors**</span> les contenus sont traités dans l'ordre d'arrivée
<span style="color: #9E9E9E">**Et**</span> pas de traitement prioritaire

---

## 18. Notification acceptation

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est validé et accepté

<span style="color: #4CAF50">**Alors**</span> je reçois un email "✅ Votre contenu '[Titre]' est en ligne !"
<span style="color: #9E9E9E">**Et**</span> je reçois une notification push
<span style="color: #9E9E9E">**Et**</span> je vois un lien direct vers le contenu

---

## 19. Compteur de validation

<span style="color: #2196F3">**Étant donné**</span> que mon premier contenu est accepté

<span style="color: #4CAF50">**Alors**</span> je vois "1/3 contenus validés pour devenir créateur vérifié"

<span style="color: #FF9800">**Quand**</span> mon deuxième contenu est accepté

<span style="color: #4CAF50">**Alors**</span> je vois "2/3 contenus validés pour devenir créateur vérifié"

---

## 20. Notification refus avec raison détaillée

<span style="color: #2196F3">**Étant donné**</span> que mon contenu est rejeté

<span style="color: #4CAF50">**Alors**</span> je reçois un email "❌ Contenu '[Titre]' refusé"
<span style="color: #9E9E9E">**Et**</span> je reçois une notification push
<span style="color: #9E9E9E">**Et**</span> je vois la raison exacte: "Qualité audio insuffisante"
<span style="color: #9E9E9E">**Et**</span> je vois un lien vers les règles de publication

---

## 21. Possibilité de correction et resoumission

<span style="color: #2196F3">**Étant donné**</span> que mon contenu a été rejeté pour "Tags non pertinents"

<span style="color: #FF9800">**Quand**</span> je corrige les tags
<span style="color: #9E9E9E">**Et**</span> que je resoumets le contenu

<span style="color: #4CAF50">**Alors**</span> le contenu repasse en file de validation
<span style="color: #9E9E9E">**Et**</span> une nouvelle validation est effectuée

---

## 22. Après 3 validations - Statut vérifié obtenu

<span style="color: #2196F3">**Étant donné**</span> que mes 3 premiers contenus ont été validés

<span style="color: #4CAF50">**Alors**</span> j'obtiens le statut "Créateur Vérifié"
<span style="color: #9E9E9E">**Et**</span> je reçois une notification "🎉 Vous êtes maintenant créateur vérifié !"
<span style="color: #9E9E9E">**Et**</span> un badge ✓ apparaît sur mon profil

---

## 23. Badge vérifié visible publiquement

<span style="color: #2196F3">**Étant donné**</span> que j'ai le statut vérifié

<span style="color: #FF9800">**Quand**</span> un utilisateur consulte mon profil

<span style="color: #4CAF50">**Alors**</span> il voit le badge ✓ à côté de mon pseudo
<span style="color: #9E9E9E">**Et**</span> une mention "Créateur vérifié"

---

## 24. Contenus futurs publiés immédiatement

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur vérifié

<span style="color: #FF9800">**Quand**</span> je publie un 4ème contenu

<span style="color: #4CAF50">**Alors**</span> le contenu est publié immédiatement
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de validation préalable
<span style="color: #9E9E9E">**Et**</span> je vois "✅ Contenu publié"

---

## 25. Modération a posteriori uniquement

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur vérifié
<span style="color: #9E9E9E">**Et**</span> que je publie un contenu

<span style="color: #FF9800">**Quand**</span> le contenu est en ligne

<span style="color: #4CAF50">**Alors**</span> il peut être signalé par les utilisateurs
<span style="color: #9E9E9E">**Et**</span> sera modéré uniquement si signalé

---

## 26. Interface modérateur - Queue de contenus

<span style="color: #2196F3">**Étant donné**</span> que je suis un modérateur junior

<span style="color: #FF9800">**Quand**</span> j'accède à l'interface de modération

<span style="color: #4CAF50">**Alors**</span> je vois la file des contenus à valider
<span style="color: #9E9E9E">**Et**</span> je vois le nombre total en attente
<span style="color: #9E9E9E">**Et**</span> les contenus sont triés par ordre FIFO

---

## 27. Interface modérateur - Écoute accélérée

<span style="color: #2196F3">**Étant donné**</span> que je suis un modérateur

<span style="color: #FF9800">**Quand**</span> j'écoute un contenu de 30 secondes

<span style="color: #4CAF50">**Alors**</span> je peux choisir la vitesse 1.5x ou 2.0x
<span style="color: #9E9E9E">**Et**</span> je termine l'écoute en 15 secondes à 2x
<span style="color: #9E9E9E">**Et**</span> ma productivité est doublée

---

## 28. Interface modérateur - Raccourcis clavier

<span style="color: #2196F3">**Étant donné**</span> que je modère un contenu

<span style="color: #FF9800">**Quand**</span> j'utilise les raccourcis clavier

<span style="color: #4CAF50">**Alors**</span> je peux:

    | touche | action |
    |---|---|
    | A | Accepter |
    | R | Rejeter |
    | Espace | Play/Pause |

<span style="color: #9E9E9E">**Et**</span> la modération est accélérée

---

## 29. Historique créateur visible

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur soumet son 2ème contenu

<span style="color: #FF9800">**Quand**</span> le modérateur examine le contenu

<span style="color: #4CAF50">**Alors**</span> il voit l'historique:

    | contenu | statut |
    |---|---|
    | Contenu 1 | Validé |
    | Contenu 2 | En cours |

<span style="color: #9E9E9E">**Et**</span> il peut juger la cohérence du créateur

---

## 30. Temps de modération estimé 1.5 min/créateur

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur soumet 3 contenus

<span style="color: #FF9800">**Quand**</span> les modérateurs traitent ces contenus

<span style="color: #4CAF50">**Alors**</span> le temps total est environ:

    | action | temps |
    |---|---|
    | Écoute 30s × 3 | 90s |
    | Vérification metadata | 15s |
    | Décision | 5s |
    | Total | 110s |


---





<div style="page-break-after: always;"></div>


# Élargissement automatique de zone quand aucun contenu n'est disponible
**9 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur connecté
> <span style="color: #9E9E9E">**Et**</span> que la géolocalisation est activée
> <span style="color: #9E9E9E">**Et**</span> que je suis en mode écoute
## 1. Aucun contenu dans rayon 50km - élargissement à 100km

<span style="color: #2196F3">**Étant donné**</span> que je suis situé à la position GPS 48.8566, 2.3522
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu n'existe dans un rayon de 50 km autour de ma position
<span style="color: #F44336">**Mais**</span> qu'au moins 1 contenu existe dans un rayon de 100 km

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu à me proposer

<span style="color: #4CAF50">**Alors**</span> le système élargit automatiquement la zone de recherche à 100 km
<span style="color: #9E9E9E">**Et**</span> je reçois un message "Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)"
<span style="color: #9E9E9E">**Et**</span> un contenu dans le rayon de 100 km m'est proposé

---

## 2. Aucun contenu dans rayon 100km - élargissement au département

<span style="color: #2196F3">**Étant donné**</span> que je suis situé dans le département "75" (Paris)
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position
<span style="color: #F44336">**Mais**</span> qu'au moins 1 contenu existe avec la zone "département" pour "75"

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu à me proposer

<span style="color: #4CAF50">**Alors**</span> le système élargit automatiquement la zone de recherche au département
<span style="color: #9E9E9E">**Et**</span> je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre département"
<span style="color: #9E9E9E">**Et**</span> un contenu départemental m'est proposé

---

## 3. Aucun contenu départemental - élargissement à la région

<span style="color: #2196F3">**Étant donné**</span> que je suis situé dans la région "Île-de-France"
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu départemental n'existe pour mon département
<span style="color: #F44336">**Mais**</span> qu'au moins 1 contenu existe avec la zone "région" pour "Île-de-France"

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu à me proposer

<span style="color: #4CAF50">**Alors**</span> le système élargit automatiquement la zone de recherche à la région
<span style="color: #9E9E9E">**Et**</span> je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre région"
<span style="color: #9E9E9E">**Et**</span> un contenu régional m'est proposé

---

## 4. Aucun contenu régional - basculement sur contenu national

<span style="color: #2196F3">**Étant donné**</span> que je suis situé en France
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu départemental n'existe pour mon département
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu régional n'existe pour ma région

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu à me proposer

<span style="color: #4CAF50">**Alors**</span> le système bascule automatiquement sur du contenu national
<span style="color: #9E9E9E">**Et**</span> je reçois un message "Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser"
<span style="color: #9E9E9E">**Et**</span> un contenu national m'est proposé
<span style="color: #9E9E9E">**Et**</span> je ne reste jamais sans contenu disponible

---

## 5. Élargissement progressif avec plusieurs étapes

<span style="color: #2196F3">**Étant donné**</span> que je suis situé dans une zone rurale isolée
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu n'existe dans un rayon de 50 km
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu n'existe dans un rayon de 100 km
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu départemental n'existe
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu régional n'existe

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu à me proposer

<span style="color: #4CAF50">**Alors**</span> le système essaie d'abord 50 km
<span style="color: #9E9E9E">**Et**</span> tout ce processus se fait de manière transparente et automatique
<span style="color: #9E9E9E">**Et**</span> je reçois le message correspondant au dernier niveau trouvé

---

## 6. Message personnalisé selon la distance trouvée

<span style="color: #2196F3">**Étant donné**</span> que je suis situé à la position GPS 43.6047, 1.4442
<span style="color: #9E9E9E">**Et**</span> que <niveau_geo> contenu(s) est/sont trouvé(s)

<span style="color: #FF9800">**Quand**</span> le système me propose du contenu

<span style="color: #4CAF50">**Alors**</span> je reçois le message "<message_attendu>"

---

## 7. Le contenu national sert de filet de sécurité

<span style="color: #2196F3">**Étant donné**</span> que le système a épuisé toutes les zones géographiques locales

<span style="color: #FF9800">**Quand**</span> le système bascule sur du contenu national

<span style="color: #4CAF50">**Alors**</span> je dois toujours avoir au moins 1 contenu disponible
<span style="color: #9E9E9E">**Et**</span> 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"

<span style="color: #2196F3">**Étant donné**</span> que je lance l'application
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu local n'est disponible dans ma zone

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu

<span style="color: #4CAF50">**Alors**</span> je ne dois jamais voir un message d'erreur "Aucun contenu disponible"
<span style="color: #9E9E9E">**Et**</span> je ne dois jamais voir un écran vide
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis situé dans une zone rurale
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu n'existe dans un rayon de 50 km
<span style="color: #9E9E9E">**Et**</span> que mes centres d'intérêt incluent "Automobile" à 80% et "Voyage" à 70%
<span style="color: #9E9E9E">**Et**</span> qu'un contenu national existe avec le tag "Automobile"
<span style="color: #9E9E9E">**Et**</span> qu'un contenu national existe avec le tag "Politique"

<span style="color: #FF9800">**Quand**</span> le système bascule sur du contenu national

<span style="color: #4CAF50">**Alors**</span> le contenu national proposé prend en compte mes centres d'intérêt
<span style="color: #9E9E9E">**Et**</span> le contenu "Automobile" a un score supérieur au contenu "Politique"

---





<div style="page-break-after: always;"></div>


# Gestion d'un contenu supprimé pendant l'écoute
**11 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur connecté
> <span style="color: #9E9E9E">**Et**</span> que je suis en mode écoute
> <span style="color: #9E9E9E">**Et**</span> qu'un contenu "C123" est en cours de lecture
## 1. Contenu supprimé pendant lecture - fin de lecture sans interruption

<span style="color: #2196F3">**Étant donné**</span> que j'écoute le contenu "C123" depuis 30 secondes
<span style="color: #9E9E9E">**Et**</span> que la durée totale du contenu est de 120 secondes

<span style="color: #FF9800">**Quand**</span> le contenu est supprimé par la modération côté backend

<span style="color: #4CAF50">**Alors**</span> la lecture du contenu continue sans interruption
<span style="color: #9E9E9E">**Et**</span> je peux écouter le contenu jusqu'à la fin
<span style="color: #9E9E9E">**Et**</span> aucune interruption brutale ne se produit

---

## 2. Passage automatique après fin du contenu supprimé

<span style="color: #2196F3">**Étant donné**</span> que le contenu "C123" a été supprimé pendant ma lecture
<span style="color: #9E9E9E">**Et**</span> que j'ai écouté le contenu jusqu'à la fin

<span style="color: #FF9800">**Quand**</span> le contenu se termine

<span style="color: #4CAF50">**Alors**</span> le système attend 2 secondes
<span style="color: #9E9E9E">**Et**</span> passe automatiquement au contenu suivant
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le contenu "C123" a été supprimé pendant ma lecture
<span style="color: #9E9E9E">**Et**</span> que je suis passé au contenu suivant "C456"

<span style="color: #FF9800">**Quand**</span> j'essaie d'appuyer sur le bouton "Précédent"

<span style="color: #4CAF50">**Alors**</span> le bouton "Précédent" ne me ramène pas au contenu supprimé
<span style="color: #9E9E9E">**Et**</span> je reçois un message "Ce contenu n'est plus disponible"
<span style="color: #9E9E9E">**Et**</span> la lecture du contenu actuel "C456" continue

---

## 4. Tentative de retour manuel au contenu supprimé

<span style="color: #2196F3">**Étant donné**</span> que je suis sur le contenu "C456"
<span style="color: #9E9E9E">**Et**</span> que le contenu précédent "C123" a été supprimé

<span style="color: #FF9800">**Quand**</span> j'appuie sur le bouton "Précédent" pour revenir au contenu supprimé

<span style="color: #4CAF50">**Alors**</span> je reçois un message "Ce contenu n'est plus disponible"
<span style="color: #9E9E9E">**Et**</span> la lecture reste sur le contenu actuel "C456"
<span style="color: #9E9E9E">**Et**</span> aucune action n'est effectuée

---

## 5. Notification discrète pendant la conduite

<span style="color: #2196F3">**Étant donné**</span> que je conduis à une vitesse de 60 km/h
<span style="color: #9E9E9E">**Et**</span> que le contenu "C123" est supprimé pendant ma lecture

<span style="color: #FF9800">**Quand**</span> le contenu se termine

<span style="color: #4CAF50">**Alors**</span> la notification "Contenu précédent retiré (violation règles)" s'affiche en toast discret
<span style="color: #9E9E9E">**Et**</span> la notification disparaît automatiquement après 5 secondes
<span style="color: #9E9E9E">**Et**</span> aucune popup modale n'interrompt ma conduite
<span style="color: #9E9E9E">**Et**</span> le contenu suivant démarre automatiquement après 2 secondes

---

## 6. Message informatif mais non alarmiste

<span style="color: #2196F3">**Étant donné**</span> que le contenu "C123" a été supprimé
<span style="color: #9E9E9E">**Et**</span> que je passe au contenu suivant

<span style="color: #FF9800">**Quand**</span> la notification s'affiche

<span style="color: #4CAF50">**Alors**</span> le message doit être informatif: "Contenu précédent retiré (violation règles)"
<span style="color: #9E9E9E">**Et**</span> le ton ne doit pas être alarmiste
<span style="color: #9E9E9E">**Et**</span> le message doit être bref et compréhensible
<span style="color: #9E9E9E">**Et**</span> aucun détail technique n'est affiché pendant la conduite

---

## 7. Contenu supprimé retiré de l'historique

<span style="color: #2196F3">**Étant donné**</span> que le contenu "C123" a été supprimé

<span style="color: #FF9800">**Quand**</span> je consulte mon historique d'écoute

<span style="color: #4CAF50">**Alors**</span> le contenu "C123" n'apparaît plus dans mon historique
<span style="color: #9E9E9E">**Et**</span> je ne peux pas relancer la lecture de ce contenu
<span style="color: #9E9E9E">**Et**</span> l'historique affiche "[Contenu retiré]" à la place du titre

---

## 8. Contenu supprimé non accessible via lien direct

<span style="color: #2196F3">**Étant donné**</span> que le contenu "C123" a été supprimé
<span style="color: #9E9E9E">**Et**</span> que j'ai un lien de partage "roadwave.fr/share/c/C123"

<span style="color: #FF9800">**Quand**</span> je clique sur le lien de partage

<span style="color: #4CAF50">**Alors**</span> je reçois un message "Ce contenu a été retiré pour violation des règles de la communauté"
<span style="color: #9E9E9E">**Et**</span> je suis redirigé vers l'accueil de l'application
<span style="color: #9E9E9E">**Et**</span> aucune lecture n'est possible

---

## 9. Plusieurs contenus supprimés dans l'historique récent

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté les contenus suivants:

    | id | statut |
    |---|---|
    | C123 | supprimé |
    | C456 | actif |
    | C789 | supprimé |

<span style="color: #9E9E9E">**Et**</span> que je suis actuellement sur le contenu "C456"

<span style="color: #FF9800">**Quand**</span> j'appuie plusieurs fois sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> je ne peux pas revenir aux contenus "C123" ou "C789"
<span style="color: #9E9E9E">**Et**</span> le système saute automatiquement les contenus supprimés
<span style="color: #9E9E9E">**Et**</span> je reviens au dernier contenu actif disponible avant "C456"

---

## 10. Consultation détaillée du contenu supprimé à l'arrêt

<span style="color: #2196F3">**Étant donné**</span> que je suis à l'arrêt
<span style="color: #9E9E9E">**Et**</span> que le contenu "C123" a été supprimé pendant ma session

<span style="color: #FF9800">**Quand**</span> j'ouvre les détails de la notification de suppression

<span style="color: #4CAF50">**Alors**</span> je peux voir les informations suivantes:

    | information |
    |---|
    | Titre du contenu |
    | Créateur |
    | Raison de suppression |
    | Date de suppression |

<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté le contenu "C123" pendant 80 secondes (66%)
<span style="color: #9E9E9E">**Et**</span> que mes jauges d'intérêt ont été mises à jour pendant l'écoute

<span style="color: #FF9800">**Quand**</span> le contenu est supprimé après mon écoute

<span style="color: #4CAF50">**Alors**</span> les modifications de mes jauges d'intérêt sont conservées
<span style="color: #9E9E9E">**Et**</span> l'écoute déjà effectuée reste comptabilisée
<span style="color: #9E9E9E">**Et**</span> seules les futures écoutes de ce contenu sont bloquées

---





<div style="page-break-after: always;"></div>


# Mode dégradé sans géolocalisation
**19 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur connecté
> <span style="color: #9E9E9E">**Et**</span> que j'ai refusé ou désactivé l'accès à la géolocalisation
## 1. Types de contenu disponibles sans géolocalisation

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée

<span style="color: #FF9800">**Quand**</span> j'ouvre l'application

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que c'est mon premier lancement de l'application
<span style="color: #9E9E9E">**Et**</span> que j'ai refusé l'accès à la géolocalisation

<span style="color: #FF9800">**Quand**</span> l'application détecte que le GPS est désactivé

<span style="color: #4CAF50">**Alors**</span> une popup s'affiche avec le message:
<span style="color: #9E9E9E">**Et**</span> la popup contient les boutons suivants:

    | bouton | action |
    |---|---|
    | Activer | Redirection vers paramètres OS |
    | Continuer sans | Ferme popup et lance en mode dégradé |

<span style="color: #9E9E9E">**Et**</span> une checkbox "Ne plus me demander" est disponible

---

## 3. Popup non affichée si case "Ne plus me demander" cochée

<span style="color: #2196F3">**Étant donné**</span> que j'ai déjà vu la popup de géolocalisation
<span style="color: #9E9E9E">**Et**</span> que j'ai coché "Ne plus me demander"

<span style="color: #FF9800">**Quand**</span> je lance l'application avec le GPS désactivé

<span style="color: #4CAF50">**Alors**</span> la popup de géolocalisation ne s'affiche pas
<span style="color: #9E9E9E">**Et**</span> l'application démarre directement en mode dégradé
<span style="color: #9E9E9E">**Et**</span> le banner permanent de rappel s'affiche

---

## 4. Redirection vers paramètres OS lors du clic sur "Activer"

<span style="color: #2196F3">**Étant donné**</span> que la popup de géolocalisation est affichée

<span style="color: #FF9800">**Quand**</span> je clique sur "Activer"

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers les paramètres de géolocalisation de mon OS
<span style="color: #9E9E9E">**Et**</span> sur iOS, j'arrive dans "Réglages > RoadWave > Localisation"
<span style="color: #9E9E9E">**Et**</span> sur Android, j'arrive dans "Paramètres > Applications > RoadWave > Autorisations > Position"

---

## 5. Banner de rappel permanent sans GPS

<span style="color: #2196F3">**Étant donné**</span> que j'ai cliqué sur "Continuer sans" géolocalisation

<span style="color: #FF9800">**Quand**</span> l'application s'affiche

<span style="color: #4CAF50">**Alors**</span> un bandeau s'affiche en haut de l'écran
<span style="color: #9E9E9E">**Et**</span> le bandeau contient le texte: "Mode limité : géolocalisation désactivée. [Activer]"
<span style="color: #9E9E9E">**Et**</span> le bandeau a un fond de couleur avertissement (jaune/orange)
<span style="color: #9E9E9E">**Et**</span> le bandeau n'est pas intrusif mais reste visible
<span style="color: #9E9E9E">**Et**</span> le bandeau reste affiché sur toutes les pages de l'application

---

## 6. Clic sur le bouton "Activer" du banner

<span style="color: #2196F3">**Étant donné**</span> que le banner "Mode limité" est affiché

<span style="color: #FF9800">**Quand**</span> je clique sur le lien "[Activer]" dans le banner

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers les paramètres de géolocalisation de mon OS

---

## 7. Disparition du banner après activation GPS

<span style="color: #2196F3">**Étant donné**</span> que le banner "Mode limité" est affiché
<span style="color: #9E9E9E">**Et**</span> que je reviens dans l'application après avoir activé le GPS dans les paramètres

<span style="color: #FF9800">**Quand**</span> l'application détecte que la géolocalisation est maintenant active

<span style="color: #4CAF50">**Alors**</span> le banner disparaît automatiquement
<span style="color: #9E9E9E">**Et**</span> l'application bascule en mode normal avec contenu géolocalisé
<span style="color: #9E9E9E">**Et**</span> un toast de confirmation s'affiche: "Géolocalisation activée"

---

## 8. Lecture de contenu national sans GPS

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée
<span style="color: #9E9E9E">**Et**</span> que du contenu national existe (actualités Le Monde, podcasts génériques)

<span style="color: #FF9800">**Quand**</span> je lance la lecture

<span style="color: #4CAF50">**Alors**</span> je peux écouter le contenu national sans restriction
<span style="color: #9E9E9E">**Et**</span> l'algorithme de recommandation se base uniquement sur:

    | critère |
    |---|
    | Mes centres d'intérêt |
    | Mon historique d'écoute |
    | Popularité générale |

<span style="color: #9E9E9E">**Et**</span> la proximité géographique n'est pas prise en compte

---

## 9. Lecture de contenu téléchargé sans GPS

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée
<span style="color: #9E9E9E">**Et**</span> que j'ai téléchargé 30 contenus quand j'avais le GPS activé

<span style="color: #FF9800">**Quand**</span> j'accède à mes contenus téléchargés

<span style="color: #4CAF50">**Alors**</span> je peux lire tous mes contenus téléchargés normalement
<span style="color: #9E9E9E">**Et**</span> les contenus géolocalisés téléchargés restent accessibles
<span style="color: #9E9E9E">**Et**</span> le filtre géographique n'est pas appliqué pour les contenus offline

---

## 10. Contenu "Neutre" géographiquement disponible

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée
<span style="color: #9E9E9E">**Et**</span> qu'un créateur a publié du contenu avec la classification géographique "Neutre"

<span style="color: #FF9800">**Quand**</span> je recherche du contenu

<span style="color: #4CAF50">**Alors**</span> les contenus "Neutre" sont inclus dans les résultats
<span style="color: #9E9E9E">**Et**</span> ils sont mélangés avec le contenu national
<span style="color: #9E9E9E">**Et**</span> l'algorithme les priorise selon mes centres d'intérêt

---

## 11. Audio-guides inaccessibles sans GPS

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée

<span style="color: #FF9800">**Quand**</span> je recherche un audio-guide spécifique

<span style="color: #4CAF50">**Alors**</span> les audio-guides apparaissent dans les résultats de recherche
<span style="color: #F44336">**Mais**</span> un badge "GPS requis" est affiché sur chaque audio-guide
<span style="color: #9E9E9E">**Et**</span> quand je clique sur un audio-guide, un message s'affiche:
<span style="color: #9E9E9E">**Et**</span> je peux choisir "Activer" ou "Annuler"

---

## 12. Notifications push géo-déclenchées désactivées

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée
<span style="color: #9E9E9E">**Et**</span> que je suis abonné à un créateur qui diffuse du contenu géolocalisé

<span style="color: #FF9800">**Quand**</span> le créateur publie un nouveau contenu géolocalisé

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de notification push géo-déclenchée
<span style="color: #F44336">**Mais**</span> je reçois une notification push standard (non géo-déclenchée) si le créateur publie du contenu national
<span style="color: #9E9E9E">**Et**</span> la notification précise: "Nouveau contenu national de [Créateur]"

---

## 13. Contenu géolocalisé non proposé dans le feed

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée

<span style="color: #FF9800">**Quand**</span> le système génère mon feed de contenu

<span style="color: #4CAF50">**Alors**</span> aucun contenu "Ancré" ou "Contextuel" n'est inclus
<span style="color: #9E9E9E">**Et**</span> seuls les contenus "Neutre" et "National" sont proposés
<span style="color: #9E9E9E">**Et**</span> mon feed contient au minimum 20 contenus disponibles

---

## 14. Application fonctionnelle sans GPS (pas de blocage)

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée

<span style="color: #FF9800">**Quand**</span> j'utilise l'application

<span style="color: #4CAF50">**Alors**</span> je ne suis jamais bloqué par un écran "GPS requis"
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> je peux créer et publier du contenu national

---

## 15. Respect du choix utilisateur de ne pas activer GPS

<span style="color: #2196F3">**Étant donné**</span> que j'ai coché "Ne plus me demander" pour la géolocalisation

<span style="color: #FF9800">**Quand**</span> j'utilise l'application pendant plusieurs semaines

<span style="color: #4CAF50">**Alors**</span> la popup de demande GPS ne s'affiche plus jamais automatiquement
<span style="color: #9E9E9E">**Et**</span> seul le banner permanent reste affiché
<span style="color: #9E9E9E">**Et**</span> l'application ne force jamais l'activation du GPS

---

## 16. Bascule automatique en mode normal après activation GPS

<span style="color: #2196F3">**Étant donné**</span> que j'utilise l'application en mode dégradé depuis 1 semaine
<span style="color: #9E9E9E">**Et**</span> que je décide d'activer la géolocalisation

<span style="color: #FF9800">**Quand**</span> l'application détecte que le GPS est maintenant actif

<span style="color: #4CAF50">**Alors**</span> le mode dégradé est désactivé automatiquement
<span style="color: #9E9E9E">**Et**</span> le banner "Mode limité" disparaît
<span style="color: #9E9E9E">**Et**</span> le contenu géolocalisé devient disponible immédiatement
<span style="color: #9E9E9E">**Et**</span> mon feed se rafraîchit avec du contenu local pertinent
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder à une fonctionnalité nécessitant le GPS (ex: audio-guide)

<span style="color: #4CAF50">**Alors**</span> une popup contextuelle s'affiche:
<span style="color: #9E9E9E">**Et**</span> je peux accepter ou refuser
<span style="color: #9E9E9E">**Et**</span> si j'accepte, je suis redirigé vers les paramètres OS
<span style="color: #9E9E9E">**Et**</span> si je refuse, je reste en mode dégradé sans message d'erreur répétitif

---

## 18. Statistiques de contenu local disponible non affiché

<span style="color: #2196F3">**Étant donné**</span> que la géolocalisation est désactivée

<span style="color: #FF9800">**Quand**</span> je navigue dans l'application

<span style="color: #4CAF50">**Alors**</span> le banner peut afficher occasionnellement:
<span style="color: #9E9E9E">**Et**</span> ce message incitatif change tous les 3 jours
<span style="color: #9E9E9E">**Et**</span> il reste non intrusif (pas de popup, juste le banner)

---

## 19. Onboarding différent pour utilisateurs sans GPS

<span style="color: #2196F3">**Étant donné**</span> que c'est ma première utilisation de RoadWave
<span style="color: #9E9E9E">**Et**</span> que j'ai refusé la géolocalisation

<span style="color: #FF9800">**Quand**</span> l'onboarding se termine

<span style="color: #4CAF50">**Alors**</span> un écran explicatif s'affiche:
<span style="color: #9E9E9E">**Et**</span> je peux continuer avec un bouton "Compris"

---





<div style="page-break-after: always;"></div>


# Gestion de la perte de réseau et buffering adaptatif
**17 scénarios** (16 standards, 1 plan)

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur connecté
> <span style="color: #9E9E9E">**Et**</span> que je suis en mode écoute
> <span style="color: #9E9E9E">**Et**</span> qu'un contenu est en cours de lecture
## 1. 📋 Plan: Paramètres de buffer selon le type de réseau

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté en "<type_reseau>"

<span style="color: #FF9800">**Quand**</span> le système initialise le buffer audio

<span style="color: #4CAF50">**Alors**</span> le buffer minimum est de <buffer_min> secondes
<span style="color: #9E9E9E">**Et**</span> le buffer cible est de <buffer_cible> secondes
<span style="color: #9E9E9E">**Et**</span> le buffer maximum est de <buffer_max> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté en 4G
<span style="color: #9E9E9E">**Et**</span> que le buffer contient 45 secondes de contenu

<span style="color: #FF9800">**Quand**</span> la latence réseau dépasse 500ms

<span style="color: #4CAF50">**Alors**</span> aucun message n'est affiché immédiatement
<span style="color: #9E9E9E">**Et**</span> la lecture continue normalement sur le buffer
<span style="color: #9E9E9E">**Et**</span> le système tente de continuer le téléchargement en arrière-plan

---

## 3. Connexion instable pendant plus de 10 secondes - toast discret

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté en 4G
<span style="color: #9E9E9E">**Et**</span> que la latence réseau dépasse 500ms depuis 10 secondes

<span style="color: #FF9800">**Quand**</span> le système détecte la latence prolongée

<span style="color: #4CAF50">**Alors**</span> un toast discret s'affiche: "Connexion instable"
<span style="color: #9E9E9E">**Et**</span> le toast disparaît automatiquement après 3 secondes
<span style="color: #9E9E9E">**Et**</span> la lecture continue normalement

---

## 4. Perte totale de réseau - lecture sur buffer

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté en WiFi
<span style="color: #9E9E9E">**Et**</span> que le buffer contient 30 secondes de contenu

<span style="color: #FF9800">**Quand**</span> je perds totalement la connexion réseau

<span style="color: #4CAF50">**Alors**</span> la lecture continue sur le buffer disponible
<span style="color: #9E9E9E">**Et**</span> un toast s'affiche: "Hors ligne, lecture sur buffer (30s restantes)"
<span style="color: #9E9E9E">**Et**</span> un compte à rebours du temps de buffer restant est visible

---

## 5. Buffer qui s'épuise pendant la perte réseau

<span style="color: #2196F3">**Étant donné**</span> que je suis hors ligne
<span style="color: #9E9E9E">**Et**</span> que le buffer contient 30 secondes de contenu

<span style="color: #FF9800">**Quand**</span> le contenu continue de jouer

<span style="color: #4CAF50">**Alors**</span> le compte à rebours diminue en temps réel
<span style="color: #9E9E9E">**Et**</span> le toast affiche "Hors ligne, lecture sur buffer (15s restantes)" après 15 secondes
<span style="color: #9E9E9E">**Et**</span> le toast affiche "Hors ligne, lecture sur buffer (5s restantes)" après 25 secondes

---

## 6. Pause automatique après épuisement du buffer

<span style="color: #2196F3">**Étant donné**</span> que je suis hors ligne depuis 30 secondes
<span style="color: #9E9E9E">**Et**</span> que le buffer est complètement épuisé

<span style="color: #FF9800">**Quand**</span> il n'y a plus de contenu audio à lire

<span style="color: #4CAF50">**Alors**</span> la lecture se met en pause automatiquement
<span style="color: #9E9E9E">**Et**</span> un overlay s'affiche: "Connexion perdue. Reconnexion en cours..."
<span style="color: #9E9E9E">**Et**</span> le système tente de se reconnecter automatiquement

---

## 7. Tentatives de reconnexion automatique

<span style="color: #2196F3">**Étant donné**</span> que la lecture est en pause suite à l'épuisement du buffer

<span style="color: #FF9800">**Quand**</span> le système tente de se reconnecter

<span style="color: #4CAF50">**Alors**</span> une tentative de reconnexion est effectuée toutes les 5 secondes
<span style="color: #9E9E9E">**Et**</span> un maximum de 6 tentatives sont effectuées (30 secondes au total)
<span style="color: #9E9E9E">**Et**</span> l'overlay affiche "Tentative de reconnexion... (X/6)"

---

## 8. Proposition du mode offline après 30 secondes d'échec

<span style="color: #2196F3">**Étant donné**</span> que 6 tentatives de reconnexion ont échoué
<span style="color: #9E9E9E">**Et**</span> que cela fait 30 secondes que je suis déconnecté

<span style="color: #FF9800">**Quand**</span> la 6ème tentative échoue

<span style="color: #4CAF50">**Alors**</span> une popup s'affiche: "Voulez-vous continuer avec vos contenus téléchargés ?"
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la popup de mode offline est affichée
<span style="color: #9E9E9E">**Et**</span> que j'ai téléchargé 20 contenus dans ma zone géographique

<span style="color: #FF9800">**Quand**</span> je clique sur "Mode offline"

<span style="color: #4CAF50">**Alors**</span> le système bascule sur les contenus téléchargés
<span style="color: #9E9E9E">**Et**</span> un nouveau contenu téléchargé démarre automatiquement
<span style="color: #9E9E9E">**Et**</span> un bandeau permanent indique "Mode hors ligne - Contenus téléchargés"

---

## 10. Aucun contenu téléchargé disponible

<span style="color: #2196F3">**Étant donné**</span> que la popup de mode offline est affichée
<span style="color: #9E9E9E">**Et**</span> que je n'ai aucun contenu téléchargé

<span style="color: #FF9800">**Quand**</span> je clique sur "Mode offline"

<span style="color: #4CAF50">**Alors**</span> un message s'affiche: "Aucun contenu téléchargé disponible"
<span style="color: #9E9E9E">**Et**</span> je suis invité à me connecter en WiFi pour télécharger du contenu
<span style="color: #9E9E9E">**Et**</span> le bouton "Réessayer" reste la seule option

---

## 11. Reprise automatique après reconnexion

<span style="color: #2196F3">**Étant donné**</span> que la lecture est en pause depuis 15 secondes
<span style="color: #9E9E9E">**Et**</span> que j'étais à 02:35 du contenu en cours

<span style="color: #FF9800">**Quand**</span> la connexion réseau est rétablie

<span style="color: #4CAF50">**Alors**</span> la lecture reprend automatiquement au point d'arrêt exact (02:35)
<span style="color: #9E9E9E">**Et**</span> un toast s'affiche: "Connexion rétablie"
<span style="color: #9E9E9E">**Et**</span> le toast disparaît après 3 secondes
<span style="color: #9E9E9E">**Et**</span> le buffer se remplit progressivement selon le type de réseau

---

## 12. Reconnexion avec changement de type de réseau

<span style="color: #2196F3">**Étant donné**</span> que j'étais connecté en WiFi
<span style="color: #9E9E9E">**Et**</span> que j'ai perdu la connexion

<span style="color: #FF9800">**Quand**</span> je me reconnecte en 4G

<span style="color: #4CAF50">**Alors**</span> le système ajuste automatiquement les paramètres de buffer
<span style="color: #9E9E9E">**Et**</span> le buffer minimum passe de 5s à 10s
<span style="color: #9E9E9E">**Et**</span> le buffer cible passe de 30s à 45s
<span style="color: #9E9E9E">**Et**</span> la lecture reprend normalement

---

## 13. Passage dans un tunnel avec perte de signal

<span style="color: #2196F3">**Étant donné**</span> que je conduis à 90 km/h sur autoroute
<span style="color: #9E9E9E">**Et**</span> que je suis connecté en 4G avec un buffer de 45 secondes

<span style="color: #FF9800">**Quand**</span> j'entre dans un tunnel et perds le signal

<span style="color: #4CAF50">**Alors**</span> la lecture continue sur le buffer pendant 45 secondes maximum
<span style="color: #9E9E9E">**Et**</span> aucune notification n'est affichée pendant les 10 premières secondes
<span style="color: #9E9E9E">**Et**</span> un toast discret s'affiche après 10 secondes: "Connexion instable"

---

## 14. Sortie du tunnel avant épuisement du buffer

<span style="color: #2196F3">**Étant donné**</span> que je suis dans un tunnel depuis 30 secondes
<span style="color: #9E9E9E">**Et**</span> qu'il reste 15 secondes de buffer

<span style="color: #FF9800">**Quand**</span> je sors du tunnel et récupère le signal 4G

<span style="color: #4CAF50">**Alors**</span> la lecture continue sans interruption
<span style="color: #9E9E9E">**Et**</span> le buffer se remplit à nouveau
<span style="color: #9E9E9E">**Et**</span> un toast s'affiche: "Connexion rétablie"

---

## 15. Changement de cellule 4G pendant la lecture

<span style="color: #2196F3">**Étant donné**</span> que je conduis et change de cellule mobile toutes les 5-10 minutes
<span style="color: #9E9E9E">**Et**</span> que le buffer contient 45 secondes de contenu

<span style="color: #FF9800">**Quand**</span> un handoff de cellule se produit

<span style="color: #4CAF50">**Alors**</span> la lecture continue sans interruption grâce au buffer
<span style="color: #9E9E9E">**Et**</span> la connexion à la nouvelle cellule se fait de manière transparente
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté en WiFi
<span style="color: #9E9E9E">**Et**</span> que j'ai activé le téléchargement automatique

<span style="color: #FF9800">**Quand**</span> le système détecte que je suis à l'arrêt en WiFi

<span style="color: #4CAF50">**Alors**</span> le système me propose de télécharger du contenu pour mon trajet
<span style="color: #9E9E9E">**Et**</span> je peux sélectionner une zone géographique à télécharger
<span style="color: #9E9E9E">**Et**</span> le téléchargement se fait en arrière-plan

---

## 17. Tracking des événements de perte réseau pour amélioration

<span style="color: #2196F3">**Étant donné**</span> que je perds la connexion réseau

<span style="color: #FF9800">**Quand**</span> l'événement de perte est détecté

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ces métriques sont anonymisées et envoyées en batch lors de la prochaine connexion WiFi
<span style="color: #9E9E9E">**Et**</span> les données servent à améliorer les paramètres de buffer

---





<div style="page-break-after: always;"></div>


# 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](#abonnements/audio-guides-pieton) | 29 |
| [Impact des abonnements sur l'algorithme](#abonnements/impact-algorithme) | 16 |
| [Limites d'abonnements et désabonnement](#abonnements/limites-desabonnement) | 27 |
| [Notifications contextuelles selon le mode de déplacement](#abonnements/notifications-contextuelles) | 28 |

*4 fonctionnalités • 100 scénarios*

## 🎧 Audio Guides

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Audio-guide mode piéton (navigation manuelle)](#audio-guides/mode-pieton) | 29 |
| [Audio-guide mode voiture (GPS automatique)](#audio-guides/mode-voiture) | 45 |
| [Audio-guides Premium et monétisation](#audio-guides/premium-monetisation) | 31 |
| [Audio-guides modes vélo et transport](#audio-guides/modes-velo-transport) | 27 |
| [Création d'audio-guide multi-séquences](#audio-guides/creation-audio-guide) | 35 |
| [Intégration audio-guides avec autres fonctionnalités](#audio-guides/integration-fonctionnalites) | 39 |
| [Sauvegarde et reprise de progression audio-guide](#audio-guides/progression-sauvegarde) | 32 |

*7 fonctionnalités • 238 scénarios*

## 🔐 Authentication

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Authentification à deux facteurs (2FA)](#authentication/two-factor-authentication) | 16 |
| [Classification des contenus par âge](#authentication/classification-age) | 13 |
| [Connexion utilisateur](#authentication/connexion) | 11 |
| [Gestion des sessions et tokens](#authentication/sessions-tokens) | 13 |
| [Inscription utilisateur](#authentication/inscription) | 15 |
| [Récupération de compte](#authentication/recuperation-compte) | 14 |
| [Vérification d'email](#authentication/verification-email) | 10 |

*7 fonctionnalités • 92 scénarios*

## 🎨 Content Creation

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Modification et suppression de contenu](#content-creation/modification-suppression) | 30 |
| [Métadonnées et publication de contenu](#content-creation/metadonnees-publication) | 34 |
| [Upload et encodage de contenu audio](#content-creation/upload-encodage) | 29 |
| [Validation des 3 premiers contenus](#content-creation/validation-premiers-contenus) | 30 |

*4 fonctionnalités • 123 scénarios*

## ⚠️ Error Handling

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Gestion d'un contenu supprimé pendant l'écoute](#error-handling/contenu-supprime-pendant-ecoute) | 11 |
| [Gestion de la perte de réseau et buffering adaptatif](#error-handling/perte-reseau) | 17 |
| [Mode dégradé sans géolocalisation](#error-handling/geolocalisation-desactivee) | 19 |
| [Élargissement automatique de zone quand aucun contenu n'est disponible](#error-handling/aucun-contenu-disponible) | 9 |

*4 fonctionnalités • 56 scénarios*

## 📊 Interest Gauges

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Jauge initiale et cold start](#interest-gauges/jauge-initiale) | 15 |
| [Pas de dégradation temporelle des jauges](#interest-gauges/degradation-temporelle) | 16 |
| [Évolution des jauges d'intérêt](#interest-gauges/evolution-jauges) | 21 |

*3 fonctionnalités • 52 scénarios*

## 📴 Mode Offline

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Synchronisation actions offline](#mode-offline/synchronisation-actions) | 45 |
| [Téléchargement de contenus offline](#mode-offline/telechargement) | 49 |
| [Validité et renouvellement contenus offline](#mode-offline/validite-renouvellement) | 38 |

*3 fonctionnalités • 132 scénarios*

## 🛡️ Moderation

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Modération préventive](#moderation/moderation-preventive) | 22 |
| [Sanctions et notifications de modération](#moderation/sanctions-notifications) | 27 |
| [Signalement de contenu inapproprié](#moderation/signalement) | 23 |
| [Traitement des signalements par l'IA et les modérateurs](#moderation/traitement-signalements) | 25 |

*4 fonctionnalités • 97 scénarios*

## 💰 Monetisation

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Conditions d'activation de la monétisation](#monetisation/conditions-activation) | 28 |
| [Contenus Premium exclusifs](#monetisation/contenus-premium-exclusifs) | 34 |
| [Désactivation et suspension monétisation](#monetisation/desactivation-suspension) | 35 |
| [KYC et inscription à la monétisation](#monetisation/kyc-inscription) | 37 |
| [Obligations fiscales](#monetisation/obligations-fiscales) | 30 |
| [Paiement des créateurs](#monetisation/paiement-createurs) | 35 |
| [Sources de revenus créateurs](#monetisation/sources-revenus) | 34 |

*7 fonctionnalités • 233 scénarios*

## 🧭 Navigation

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Actions complémentaires à l'arrêt](#navigation/actions-complementaires) | 23 |
| [Commande "Précédent"](#navigation/commande-precedent) | 19 |
| [Commandes au volant et interactions simplifiées](#navigation/commandes-volant) | 21 |
| [Commandes vocales CarPlay et Android Auto](#navigation/commandes-vocales) | 25 |
| [File d'attente et commande "Suivant"](#navigation/file-attente-suivant) | 20 |
| [Lecture en boucle et enchaînement automatique](#navigation/lecture-enchainement) | 27 |

*6 fonctionnalités • 135 scénarios*

## 🔗 Partage

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Partage de contenu](#partage/partage-contenu) | 22 |

*1 fonctionnalités • 22 scénarios*

## ⭐ Premium

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Avantages Premium](#premium/avantages-premium) | 37 |
| [Gestion abonnement Premium](#premium/gestion-abonnement) | 41 |
| [Multi-devices et détection simultanée](#premium/multi-devices-detection) | 30 |
| [Offre et tarification Premium](#premium/offre-tarification) | 31 |

*4 fonctionnalités • 139 scénarios*

## 👤 Profil

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Profil créateur](#profil/profil-createur) | 31 |

*1 fonctionnalités • 31 scénarios*

## 📢 Publicites

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Caractéristiques et facturation des publicités](#publicites/caracteristiques-pub) | 32 |
| [Création de campagnes publicitaires](#publicites/campagnes-publicitaires) | 30 |
| [Gestion du budget et alertes publicitaires](#publicites/gestion-budget-pub) | 30 |
| [Insertion et fréquence des publicités](#publicites/insertion-frequence-pub) | 31 |
| [Métriques d'engagement et dashboard publicitaire](#publicites/metriques-engagement-pub) | 27 |
| [Validation et modération des publicités](#publicites/validation-moderation-pub) | 29 |

*6 fonctionnalités • 179 scénarios*

## 📻 Radio Live

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Architecture technique radio live](#radio-live/architecture-technique-live) | 24 |
| [Arrêt du live](#radio-live/arret-live) | 19 |
| [Comportement auditeur pendant un live](#radio-live/comportement-auditeur) | 27 |
| [Démarrage d'un live](#radio-live/demarrage-live) | 20 |

*4 fonctionnalités • 90 scénarios*

## 🔍 Recherche

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Recherche de contenu](#recherche/recherche) | 55 |

*1 fonctionnalités • 55 scénarios*

## 🎯 Recommendation

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Classification de géo-pertinence des contenus](#recommendation/classification-geo) | 10 |
| [Contenus géolocalisés en mode voiture](#recommendation/declenchement-geo) | 36 |
| [Formule de scoring et recommandation](#recommendation/scoring-recommandation) | 21 |
| [Gestion de l'historique et reproposition](#recommendation/historique-reproposition) | 19 |
| [Gestion du contenu politique (MVP simplifié)](#recommendation/contenu-politique) | 13 |
| [Mode Kids pour utilisateurs 13-15 ans](#recommendation/mode-kids) | 15 |
| [Médias traditionnels sur RoadWave](#recommendation/medias-traditionnels) | 21 |
| [Paramétrabilité admin et A/B testing](#recommendation/parametrabilite-admin) | 20 |
| [Paramétrabilité utilisateur et profils](#recommendation/parametrabilite-utilisateur) | 25 |

*9 fonctionnalités • 180 scénarios*

## 🔒 Rgpd Compliance

| Fonctionnalité | Scénarios |
|----------------|:---------:|
| [Anonymisation des données GPS après 24h](#rgpd-compliance/anonymisation-gps) | 18 |
| [Conformité administrative RGPD (Registre, Breach, DPO)](#rgpd-compliance/compliance-administrative) | 22 |
| [Cookies et analytics avec Matomo self-hosted](#rgpd-compliance/cookies-analytics) | 20 |
| [Durée de conservation des données et purge automatique](#rgpd-compliance/conservation-donnees) | 19 |
| [Gestion du consentement RGPD](#rgpd-compliance/consentement) | 16 |
| [Mode dégradé avec GeoIP (sans GPS précis)](#rgpd-compliance/mode-degrade-geoip) | 20 |
| [Portabilité des données (Article 20 RGPD)](#rgpd-compliance/portabilite-donnees) | 22 |
| [Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)](#rgpd-compliance/suppression-compte) | 21 |

*8 fonctionnalités • 158 scénarios*





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est connecté
## 1. Aucune dégradation automatique avec le temps

<span style="color: #2196F3">**Étant donné**</span> que ma jauge "Économie" est à 80%
<span style="color: #9E9E9E">**Et**</span> que je n'écoute aucun contenu pendant 30 jours

<span style="color: #FF9800">**Quand**</span> je me reconnecte après 30 jours

<span style="color: #4CAF50">**Alors**</span> ma jauge "Économie" est toujours à 80%
<span style="color: #9E9E9E">**Et**</span> aucune dégradation temporelle n'a été appliquée

---

## 2. Jauges conservées après 6 mois d'inactivité

<span style="color: #2196F3">**Étant donné**</span> que mes jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 75% |
    | Voyage | 60% |
    | Musique | 45% |

<span style="color: #9E9E9E">**Et**</span> que je pars en vacances pendant 6 mois sans utiliser l'app

<span style="color: #FF9800">**Quand**</span> je me reconnecte après 6 mois

<span style="color: #4CAF50">**Alors**</span> mes jauges sont exactement les mêmes:

    | catégorie | niveau |
    |---|---|
    | Automobile | 75% |
    | Voyage | 60% |
    | Musique | 45% |


---

## 3. Évolution naturelle par les actions

<span style="color: #2196F3">**Étant donné**</span> que j'aimais "Économie" il y a 1 an (jauge 80%)
<span style="color: #9E9E9E">**Et**</span> que depuis, je skip tous les contenus "Économie"
<span style="color: #9E9E9E">**Et**</span> que j'ai skippé 50 contenus "Économie" en 1 an

<span style="color: #4CAF50">**Alors**</span> ma jauge "Économie" descend naturellement via les skips
<span style="color: #9E9E9E">**Et**</span> atteint environ 55% (80% - 50 × 0.5% = 55%)
<span style="color: #9E9E9E">**Et**</span> la dégradation vient des actions, pas du temps

---

## 4. Pas de cron job de dégradation

<span style="color: #2196F3">**Étant donné**</span> que le système vérifie les jauges quotidiennement

<span style="color: #FF9800">**Quand**</span> un utilisateur n'a pas d'activité depuis 90 jours

<span style="color: #4CAF50">**Alors**</span> aucun job de dégradation n'est exécuté
<span style="color: #9E9E9E">**Et**</span> les jauges restent inchangées
<span style="color: #9E9E9E">**Et**</span> aucune ressource CPU n'est consommée pour la dégradation

---

## 5. Comportement prévisible après absence

<span style="color: #2196F3">**Étant donné**</span> que ma jauge "Sport" était à 70%
<span style="color: #9E9E9E">**Et**</span> que je n'utilise pas l'app pendant 1 an

<span style="color: #FF9800">**Quand**</span> je reviens et demande des recommandations

<span style="color: #4CAF50">**Alors**</span> mes recommandations reflètent toujours mes goûts d'avant
<span style="color: #9E9E9E">**Et**</span> je reçois du contenu "Sport" prioritaire
<span style="color: #9E9E9E">**Et**</span> le comportement est cohérent et prévisible

---

## 6. Réinitialiser manuellement mes centres d'intérêt

<span style="color: #2196F3">**Étant donné**</span> que je veux repartir de zéro

<span style="color: #FF9800">**Quand**</span> je vais dans les paramètres
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Réinitialiser mes centres d'intérêt"
<span style="color: #9E9E9E">**Et**</span> que je confirme l'action

<span style="color: #4CAF50">**Alors**</span> toutes mes jauges reviennent à 50%
<span style="color: #9E9E9E">**Et**</span> je vois le message "Vos centres d'intérêt ont été réinitialisés"

---

## 7. Confirmation avant réinitialisation

<span style="color: #2196F3">**Étant donné**</span> que je suis dans les paramètres

<span style="color: #FF9800">**Quand**</span> je clique sur "Réinitialiser mes centres d'intérêt"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai cliqué sur "Réinitialiser mes centres d'intérêt"
<span style="color: #9E9E9E">**Et**</span> que la confirmation est affichée

<span style="color: #FF9800">**Quand**</span> je clique sur "Annuler"

<span style="color: #4CAF50">**Alors**</span> mes jauges ne sont pas modifiées
<span style="color: #9E9E9E">**Et**</span> je reviens aux paramètres

---

## 9. Raison de réinitialisation - changement de vie

<span style="color: #2196F3">**Étant donné**</span> que j'utilisais RoadWave pour mes trajets professionnels
<span style="color: #9E9E9E">**Et**</span> que mes jauges reflétaient "Économie" (85%) et "Technologie" (75%)
<span style="color: #9E9E9E">**Et**</span> que je change de vie et deviens musicien

<span style="color: #FF9800">**Quand**</span> je réinitialise mes centres d'intérêt

<span style="color: #4CAF50">**Alors**</span> je peux repartir avec toutes les jauges à 50%
<span style="color: #9E9E9E">**Et**</span> découvrir du contenu "Musique" et "Culture" sans biais

---

## 10. Pas de suggestion automatique de réinitialisation

<span style="color: #2196F3">**Étant donné**</span> que je n'ai pas utilisé l'app depuis 1 an

<span style="color: #FF9800">**Quand**</span> je me reconnecte

<span style="color: #4CAF50">**Alors**</span> aucune suggestion de réinitialisation n'est affichée
<span style="color: #9E9E9E">**Et**</span> mes jauges sont conservées telles quelles
<span style="color: #9E9E9E">**Et**</span> je garde le contrôle total

---

## 11. Historique conservé après réinitialisation

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté 500 contenus

<span style="color: #FF9800">**Quand**</span> je réinitialise mes centres d'intérêt

<span style="color: #4CAF50">**Alors**</span> mes jauges reviennent à 50%
<span style="color: #F44336">**Mais**</span> mon historique d'écoute est conservé
<span style="color: #9E9E9E">**Et**</span> je peux toujours consulter mes anciens contenus écoutés

---

## 12. Évolution future basée sur nouvelles actions

<span style="color: #2196F3">**Étant donné**</span> que j'ai réinitialisé mes jauges à 50%

<span style="color: #FF9800">**Quand**</span> j'écoute 5 contenus "Voyage" à >80%

<span style="color: #4CAF50">**Alors**</span> ma jauge "Voyage" monte à 60% (50% + 5 × 2%)
<span style="color: #9E9E9E">**Et**</span> l'algorithme recommence à apprendre mes nouvelles préférences

---

## 13. Respect de l'historique utilisateur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur aime "Cryptomonnaie" depuis 2 ans
<span style="color: #9E9E9E">**Et**</span> que sa jauge est à 90%

<span style="color: #FF9800">**Quand**</span> 2 ans s'écoulent sans dégradation temporelle

<span style="color: #4CAF50">**Alors**</span> sa jauge reste à 90%
<span style="color: #9E9E9E">**Et**</span> le système ne fait pas d'"oubli" artificiel

---

## 14. Coût infrastructure zéro

<span style="color: #2196F3">**Étant donné**</span> qu'aucune dégradation temporelle n'existe

<span style="color: #FF9800">**Quand**</span> le système calcule les jauges

<span style="color: #4CAF50">**Alors**</span> aucun calcul de date n'est nécessaire
<span style="color: #9E9E9E">**Et**</span> aucun batch nocturne ne tourne
<span style="color: #9E9E9E">**Et**</span> aucun bug de fuseau horaire ne peut survenir
<span style="color: #9E9E9E">**Et**</span> le coût CPU est minimal

---

## 15. UX prévisible - jauge = actions

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur consulte sa jauge "Sport" à 65%

<span style="color: #FF9800">**Quand**</span> il se demande pourquoi elle est à 65%

<span style="color: #4CAF50">**Alors**</span> il peut retracer ses actions:

    | action | impact |
    |---|---|
    | 10 likes automatiques | +10% |
    | 3 abonnements Sport | +15% |
    | 5 skips de contenu non-Sport | 0% |

<span style="color: #9E9E9E">**Et**</span> il comprend que c'est le reflet exact de ses actions
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de mystère ou automatisme caché

---

## 16. Statistiques affichées sans date

<span style="color: #2196F3">**Étant donné**</span> que je consulte mes centres d'intérêt

<span style="color: #FF9800">**Quand**</span> je vois mes jauges

<span style="color: #4CAF50">**Alors**</span> je vois:

    | information | affiché |
    |---|---|
    | Niveau actuel | ✅ 75% |
    | Évolution depuis début | ✅ +25% |
    | Dernière mise à jour | ❌ |

<span style="color: #9E9E9E">**Et**</span> aucune date n'est affichée car non pertinente
<span style="color: #9E9E9E">**Et**</span> seules les actions comptent

---





<div style="page-break-after: always;"></div>


# É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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est connecté
## 1. Like automatique renforcé après écoute ≥80%

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 5 minutes est tagué "Automobile"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Automobile" est à 45%

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu pendant 4 minutes 30 secondes (90%)

<span style="color: #4CAF50">**Alors**</span> je reçois un like automatique renforcé
<span style="color: #9E9E9E">**Et**</span> ma jauge "Automobile" augmente de 2%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Automobile" est maintenant à 47%

---

## 2. Like automatique renforcé exactement à 80%

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 10 minutes est tagué "Voyage"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Voyage" est à 60%

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu pendant exactement 8 minutes (80%)

<span style="color: #4CAF50">**Alors**</span> je reçois un like automatique renforcé
<span style="color: #9E9E9E">**Et**</span> ma jauge "Voyage" augmente de 2%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Voyage" est maintenant à 62%

---

## 3. Like automatique standard après écoute 30-79%

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 5 minutes est tagué "Automobile"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Automobile" est à 45%

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu pendant 2 minutes 30 secondes (50%)

<span style="color: #4CAF50">**Alors**</span> je reçois un like automatique standard
<span style="color: #9E9E9E">**Et**</span> ma jauge "Automobile" augmente de 1%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Automobile" est maintenant à 46%

---

## 4. Like automatique standard à 30% exactement

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 10 minutes est tagué "Musique"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Musique" est à 40%

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu pendant exactement 3 minutes (30%)

<span style="color: #4CAF50">**Alors**</span> je reçois un like automatique standard
<span style="color: #9E9E9E">**Et**</span> ma jauge "Musique" augmente de 1%

---

## 5. Like automatique standard à 79%

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 10 minutes est tagué "Sport"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Sport" est à 55%

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu pendant 7 minutes 54 secondes (79%)

<span style="color: #4CAF50">**Alors**</span> je reçois un like automatique standard
<span style="color: #9E9E9E">**Et**</span> ma jauge "Sport" augmente de 1%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Sport" est maintenant à 56%

---

## 6. Like explicite (manuel) +2%

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est tagué "Économie"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Économie" est à 70%

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu partiellement
<span style="color: #9E9E9E">**Et**</span> que je clique manuellement sur le bouton "Like"

<span style="color: #4CAF50">**Alors**</span> ma jauge "Économie" augmente de 2%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Économie" est maintenant à 72%

---

## 7. Like manuel cumulable avec like automatique

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 5 minutes est tagué "Automobile"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Automobile" est à 45%

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu pendant 2 minutes 30 secondes (50%)

<span style="color: #4CAF50">**Alors**</span> je reçois un like automatique standard (+1%)

<span style="color: #FF9800">**Quand**</span> je clique ensuite sur le bouton "Like"

<span style="color: #4CAF50">**Alors**</span> ma jauge augmente encore de 2% (like manuel)
<span style="color: #9E9E9E">**Et**</span> ma jauge "Automobile" a augmenté de 3% au total
<span style="color: #9E9E9E">**Et**</span> ma jauge "Automobile" est maintenant à 48%

---

## 8. Abonnement créateur impacte tous ses tags

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur publie des contenus tagués "Automobile" et "Technologie"
<span style="color: #9E9E9E">**Et**</span> que mes jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 50% |
    | Technologie | 45% |


<span style="color: #FF9800">**Quand**</span> je m'abonne à ce créateur

<span style="color: #4CAF50">**Alors**</span> ma jauge "Automobile" augmente de 5%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Technologie" augmente de 5%
<span style="color: #9E9E9E">**Et**</span> mes nouvelles jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 55% |
    | Technologie | 50% |


---

## 9. Skip rapide (<10s) diminue la jauge

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est tagué "Économie"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Économie" est à 45%

<span style="color: #FF9800">**Quand**</span> je skip le contenu après 5 secondes

<span style="color: #4CAF50">**Alors**</span> ma jauge "Économie" diminue de 0.5%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Économie" est maintenant à 44.5%

---

## 10. Skip à exactement 10s ne diminue pas la jauge

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est tagué "Politique"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Politique" est à 50%

<span style="color: #FF9800">**Quand**</span> je skip le contenu après exactement 10 secondes

<span style="color: #4CAF50">**Alors**</span> ma jauge "Politique" ne change pas
<span style="color: #9E9E9E">**Et**</span> reste à 50%

---

## 11. Skip tardif (≥30%) est neutre

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 10 minutes est tagué "Musique"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Musique" est à 60%

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 3 minutes (30%)
<span style="color: #9E9E9E">**Et**</span> que je skip ensuite

<span style="color: #4CAF50">**Alors**</span> ma jauge "Musique" ne diminue pas (signal neutre)
<span style="color: #9E9E9E">**Et**</span> ma jauge reste à 60% (plus le +1% de like auto si applicable)

---

## 12. Contenu avec plusieurs tags impacte toutes les jauges

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est tagué "Automobile" et "Voyage"
<span style="color: #9E9E9E">**Et**</span> que mes jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 45% |
    | Voyage | 60% |


<span style="color: #FF9800">**Quand**</span> j'écoute le contenu à 90%

<span style="color: #4CAF50">**Alors**</span> les deux jauges augmentent de 2%
<span style="color: #9E9E9E">**Et**</span> mes nouvelles jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 47% |
    | Voyage | 62% |


---

## 13. Contenu avec 3 tags impacte les 3 jauges

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est tagué "Sport", "Santé" et "Technologie"
<span style="color: #9E9E9E">**Et**</span> que mes jauges sont à 50% pour chaque catégorie

<span style="color: #FF9800">**Quand**</span> je skip rapidement après 5 secondes

<span style="color: #4CAF50">**Alors**</span> les 3 jauges diminuent de 0.5%
<span style="color: #9E9E9E">**Et**</span> toutes passent à 49.5%

---

## 14. Jauges bornées - ne peut pas dépasser 100%

<span style="color: #2196F3">**Étant donné**</span> que ma jauge "Cryptomonnaie" est à 99%
<span style="color: #9E9E9E">**Et**</span> qu'un contenu tagué "Cryptomonnaie" est disponible

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu à 95% (like auto renforcé +2%)

<span style="color: #4CAF50">**Alors**</span> ma jauge "Cryptomonnaie" passe à 100% (maximum)
<span style="color: #9E9E9E">**Et**</span> ne dépasse pas 100%

---

## 15. Jauges bornées - ne peut pas descendre sous 0%

<span style="color: #2196F3">**Étant donné**</span> que ma jauge "Politique" est à 0.3%
<span style="color: #9E9E9E">**Et**</span> qu'un contenu tagué "Politique" est disponible

<span style="color: #FF9800">**Quand**</span> je skip rapidement après 3 secondes (-0.5%)

<span style="color: #4CAF50">**Alors**</span> ma jauge "Politique" passe à 0% (minimum)
<span style="color: #9E9E9E">**Et**</span> ne devient pas négative

---

## 16. Calcul immédiat à chaque action

<span style="color: #2196F3">**Étant donné**</span> que ma jauge "Voyage" est à 50%

<span style="color: #FF9800">**Quand**</span> j'écoute un contenu "Voyage" à 85%

<span style="color: #4CAF50">**Alors**</span> la jauge est mise à jour immédiatement (pas de batch)
<span style="color: #9E9E9E">**Et**</span> passe à 52%

<span style="color: #FF9800">**Quand**</span> je demande mes recommandations dans la seconde suivante

<span style="color: #4CAF50">**Alors**</span> l'algorithme utilise déjà la valeur 52%

---

## 17. Like manuel après écoute <30% (pas de like auto)

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 10 minutes est tagué "Culture"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Culture" est à 60%

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 2 minutes (20%)

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de like automatique

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton "Like"

<span style="color: #4CAF50">**Alors**</span> ma jauge "Culture" augmente de 2% uniquement
<span style="color: #9E9E9E">**Et**</span> ma jauge "Culture" est maintenant à 62%

---

## 18. Unlike retire le like manuel

<span style="color: #2196F3">**Étant donné**</span> que j'ai liké manuellement un contenu "Sport"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Sport" est passée de 55% à 57% (+2%)

<span style="color: #FF9800">**Quand**</span> je clique sur "Unlike"

<span style="color: #4CAF50">**Alors**</span> ma jauge "Sport" diminue de 2%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Sport" revient à 55%

---

## 19. Unlike ne peut pas retirer un like automatique

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté un contenu "Musique" à 90%
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu un like automatique renforcé (+2%)
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Musique" est à 52%

<span style="color: #FF9800">**Quand**</span> j'essaie de faire "Unlike"

<span style="color: #4CAF50">**Alors**</span> l'action n'est pas disponible
<span style="color: #9E9E9E">**Et**</span> ma jauge reste à 52%

---

## 20. Tags définis par créateur à la publication

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur

<span style="color: #FF9800">**Quand**</span> je publie un contenu

<span style="color: #4CAF50">**Alors**</span> je dois sélectionner 1 à 3 tags
<span style="color: #9E9E9E">**Et**</span> ces tags sont fixés après publication
<span style="color: #9E9E9E">**Et**</span> impacteront les jauges de tous les auditeurs

---

## 21. 📋 Plan: Calculs avec différentes durées d'écoute

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de 10 minutes est tagué "Voyage"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Voyage" est à 50%

<span style="color: #FF9800">**Quand**</span> j'écoute pendant <duree> (<pourcentage>)

<span style="color: #4CAF50">**Alors**</span> ma jauge évolue de <impact>
<span style="color: #9E9E9E">**Et**</span> ma nouvelle jauge est à <nouveau_niveau>

**📊 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% |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Inscription - toutes les jauges à 50%

<span style="color: #FF9800">**Quand**</span> je m'inscris sur RoadWave

<span style="color: #4CAF50">**Alors**</span> toutes mes jauges d'intérêt sont initialisées à 50%
<span style="color: #9E9E9E">**Et**</span> je ne dois pas remplir de questionnaire
<span style="color: #9E9E9E">**Et**</span> l'inscription est ultra-rapide

---

## 2. Liste des catégories disponibles

<span style="color: #2196F3">**Étant donné**</span> que je suis un nouvel utilisateur

<span style="color: #FF9800">**Quand**</span> je consulte mes centres d'intérêt

<span style="color: #4CAF50">**Alors**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> que je viens de m'inscrire
<span style="color: #9E9E9E">**Et**</span> que toutes mes jauges sont à 50%

<span style="color: #FF9800">**Quand**</span> j'écoute mon premier podcast "Automobile" à 90%

<span style="color: #4CAF50">**Alors**</span> ma jauge "Automobile" monte à 52% (+2%)
<span style="color: #9E9E9E">**Et**</span> toutes les autres jauges restent à 50%

---

## 4. Cold start - premier skip

<span style="color: #2196F3">**Étant donné**</span> que je viens de m'inscrire
<span style="color: #9E9E9E">**Et**</span> que toutes mes jauges sont à 50%

<span style="color: #FF9800">**Quand**</span> je skip rapidement un contenu "Économie"

<span style="color: #4CAF50">**Alors**</span> ma jauge "Économie" descend à 49.5% (-0.5%)
<span style="color: #9E9E9E">**Et**</span> toutes les autres jauges restent à 50%

---

## 5. Après 10 écoutes, profil commence à se dessiner

<span style="color: #2196F3">**Étant donné**</span> que je suis un nouvel utilisateur
<span style="color: #9E9E9E">**Et**</span> 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% |


<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #FF9800">**Quand**</span> je termine l'inscription

<span style="color: #4CAF50">**Alors**</span> aucun questionnaire de centres d'intérêt n'est affiché
<span style="color: #9E9E9E">**Et**</span> je peux commencer à écouter immédiatement
<span style="color: #9E9E9E">**Et**</span> l'algorithme apprend naturellement

---

## 7. Algorithme avec jauges à 50% - chances égales

<span style="color: #2196F3">**Étant donné**</span> que toutes mes jauges sont à 50%

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule les recommandations

<span style="color: #4CAF50">**Alors**</span> tous les types de contenus ont une chance égale
<span style="color: #9E9E9E">**Et**</span> aucun biais initial n'est appliqué
<span style="color: #9E9E9E">**Et**</span> la géolocalisation prime sur les intérêts

---

## 8. Questionnaire optionnel après 3 écoutes (post-MVP)

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté 3 contenus

<span style="color: #FF9800">**Quand**</span> je termine ma 3ème écoute

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que le questionnaire optionnel est affiché

<span style="color: #FF9800">**Quand**</span> je sélectionne les centres d'intérêt suivants:

    | catégorie |
    |---|
    | Automobile |
    | Voyage |
    | Sport |


<span style="color: #4CAF50">**Alors**</span> les jauges sélectionnées passent à 70%
<span style="color: #9E9E9E">**Et**</span> les jauges non sélectionnées passent à 30%
<span style="color: #9E9E9E">**Et**</span> je vois le message "Vos préférences ont été enregistrées"

---

## 10. Skipper le questionnaire optionnel (post-MVP)

<span style="color: #2196F3">**Étant donné**</span> que le questionnaire optionnel est affiché

<span style="color: #FF9800">**Quand**</span> je clique sur "Plus tard"

<span style="color: #4CAF50">**Alors**</span> toutes mes jauges conservent 50%
<span style="color: #9E9E9E">**Et**</span> l'algorithme continue d'apprendre naturellement
<span style="color: #9E9E9E">**Et**</span> je ne suis plus sollicité

---

## 11. Comportement déterministe et testable

<span style="color: #2196F3">**Étant donné**</span> deux nouveaux utilisateurs A et B

<span style="color: #FF9800">**Quand**</span> les deux s'inscrivent au même moment

<span style="color: #4CAF50">**Alors**</span> leurs jauges sont identiques (toutes à 50%)
<span style="color: #9E9E9E">**Et**</span> leurs recommandations initiales sont identiques (basées sur géo uniquement)

---

## 12. Équité entre créateurs au cold start

<span style="color: #2196F3">**Étant donné**</span> qu'un nouvel utilisateur s'inscrit
<span style="color: #9E9E9E">**Et**</span> qu'il existe 1000 contenus de catégories variées dans sa zone

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les premières recommandations

<span style="color: #4CAF50">**Alors**</span> tous les contenus ont une pondération intérêts identique (50%)
<span style="color: #9E9E9E">**Et**</span> seuls la géolocalisation et l'engagement différencient les contenus
<span style="color: #9E9E9E">**Et**</span> aucun créateur n'a d'avantage initial

---

## 13. Catégories extensibles

<span style="color: #2196F3">**Étant donné**</span> que RoadWave ajoute une nouvelle catégorie "Gastronomie"

<span style="color: #FF9800">**Quand**</span> je consulte mes centres d'intérêt

<span style="color: #4CAF50">**Alors**</span> je vois la nouvelle catégorie "Gastronomie" à 50%
<span style="color: #9E9E9E">**Et**</span> je peux commencer à l'explorer normalement

---

## 14. Voir l'évolution de mes jauges

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur avec historique

<span style="color: #FF9800">**Quand**</span> je consulte mes centres d'intérêt dans les paramètres

<span style="color: #4CAF50">**Alors**</span> je vois mes jauges actuelles:

    | catégorie | niveau | evolution |
    |---|---|---|
    | Automobile | 67% | +17% |
    | Voyage | 82% | +32% |
    | Économie | 34% | -16% |
    | Sport | 50% | 0% |

<span style="color: #9E9E9E">**Et**</span> je comprends mes préférences actuelles

---

## 15. Friction zéro à l'inscription

<span style="color: #2196F3">**Étant donné**</span> que je veux m'inscrire rapidement

<span style="color: #FF9800">**Quand**</span> je remplis les 4 champs obligatoires
<span style="color: #9E9E9E">**Et**</span> que je clique sur "S'inscrire"

<span style="color: #4CAF50">**Alors**</span> mon compte est créé immédiatement
<span style="color: #9E9E9E">**Et**</span> je peux commencer à écouter dans les 30 secondes
<span style="color: #9E9E9E">**Et**</span> aucune configuration supplémentaire n'est requise

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que j'utilise l'application RoadWave
## 1. Like d'un contenu en mode offline

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucune connexion Internet

<span style="color: #FF9800">**Quand**</span> je like un contenu téléchargé

<span style="color: #4CAF50">**Alors**</span> l'action est enregistrée localement dans SQLite:
<span style="color: #9E9E9E">**Et**</span> l'UI affiche immédiatement le like (optimistic update)

---

## 2. Unlike d'un contenu en mode offline

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucune connexion Internet
<span style="color: #9E9E9E">**Et**</span> que j'avais liké un contenu

<span style="color: #FF9800">**Quand**</span> je retire mon like

<span style="color: #4CAF50">**Alors**</span> l'action est enregistrée localement:
<span style="color: #9E9E9E">**Et**</span> l'UI retire immédiatement le like

---

## 3. Abonnement à un créateur en mode offline

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucune connexion Internet

<span style="color: #FF9800">**Quand**</span> je m'abonne à un créateur

<span style="color: #4CAF50">**Alors**</span> l'action est enregistrée localement:
<span style="color: #9E9E9E">**Et**</span> l'UI affiche immédiatement "Abonné ✓"

---

## 4. Désabonnement d'un créateur en mode offline

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucune connexion Internet
<span style="color: #9E9E9E">**Et**</span> que j'étais abonné à un créateur

<span style="color: #FF9800">**Quand**</span> je me désabonne

<span style="color: #4CAF50">**Alors**</span> l'action est enregistrée localement:
<span style="color: #9E9E9E">**Et**</span> l'UI affiche "S'abonner"

---

## 5. Signalement d'un contenu en mode offline

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucune connexion Internet

<span style="color: #FF9800">**Quand**</span> je signale un contenu pour "Contenu inapproprié"

<span style="color: #4CAF50">**Alors**</span> l'action est enregistrée localement:
<span style="color: #9E9E9E">**Et**</span> je vois "Signalement enregistré. Sera envoyé à la reconnexion."

---

## 6. Progression audio-guide en mode offline

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucune connexion Internet
<span style="color: #9E9E9E">**Et**</span> que j'écoute un audio-guide multi-séquences

<span style="color: #FF9800">**Quand**</span> je termine la séquence 3/10

<span style="color: #4CAF50">**Alors**</span> la progression est enregistrée localement:
<span style="color: #9E9E9E">**Et**</span> ma progression est sauvegardée

---

## 7. Multiple actions offline stockées en queue

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucune connexion Internet pendant 2 jours

<span style="color: #FF9800">**Quand**</span> j'effectue plusieurs actions:

    | action | cible |
    |---|---|
    | like | contenu A |
    | like | contenu B |
    | subscribe | créateur X |
    | unlike | contenu C |
    | report | contenu D |


<span style="color: #4CAF50">**Alors**</span> les 5 actions sont stockées dans pending_actions
<span style="color: #9E9E9E">**Et**</span> elles seront synchronisées dans l'ordre à la reconnexion

---

## 8. Détection reconnexion Internet

<span style="color: #2196F3">**Étant donné**</span> que j'étais en mode offline

<span style="color: #FF9800">**Quand**</span> l'app détecte une reconnexion Internet

<span style="color: #4CAF50">**Alors**</span> le processus de synchronisation démarre automatiquement
<span style="color: #9E9E9E">**Et**</span> je vois une notification "Synchronisation en cours..."

---

## 9. Récupération queue locale pendant sync

<span style="color: #2196F3">**Étant donné**</span> que la synchronisation démarre

<span style="color: #FF9800">**Quand**</span> l'app récupère les actions en attente

<span style="color: #4CAF50">**Alors**</span> une requête SQL est exécutée:
<span style="color: #9E9E9E">**Et**</span> toutes les actions sont récupérées dans l'ordre chronologique

---

## 10. Envoi batch API des actions

<span style="color: #2196F3">**Étant donné**</span> que 15 actions sont en attente

<span style="color: #FF9800">**Quand**</span> le batch est envoyé au backend

<span style="color: #4CAF50">**Alors**</span> une requête POST /sync/actions est faite:
<span style="color: #9E9E9E">**Et**</span> toutes les actions sont groupées en une seule requête

---

## 11. Backend traite chaque action

<span style="color: #2196F3">**Étant donné**</span> que le backend reçoit le batch d'actions

<span style="color: #FF9800">**Quand**</span> il traite chaque action

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le backend a traité toutes les actions avec succès

<span style="color: #FF9800">**Quand**</span> la confirmation est reçue par l'app

<span style="color: #4CAF50">**Alors**</span> les actions sont supprimées de la queue locale:
<span style="color: #9E9E9E">**Et**</span> la table pending_actions est vidée

---

## 13. Toast confirmation synchronisation

<span style="color: #2196F3">**Étant donné**</span> que 15 actions ont été synchronisées

<span style="color: #FF9800">**Quand**</span> la synchronisation se termine

<span style="color: #4CAF50">**Alors**</span> je vois un toast:

---

## 14. Synchronisation silencieuse si peu d'actions

<span style="color: #2196F3">**Étant donné**</span> que j'ai seulement 2 actions en attente

<span style="color: #FF9800">**Quand**</span> la synchronisation se termine

<span style="color: #4CAF50">**Alors**</span> aucun toast n'est affiché (sync silencieuse)
<span style="color: #9E9E9E">**Et**</span> l'expérience reste fluide
<span style="color: #F44336">**Mais**</span> je peux voir le détail dans l'historique des syncs

---

## 15. Échec synchronisation - Retry automatique

<span style="color: #2196F3">**Étant donné**</span> que la synchronisation échoue (erreur réseau)

<span style="color: #FF9800">**Quand**</span> l'échec est détecté

<span style="color: #4CAF50">**Alors**</span> un retry automatique est programmé dans 30 secondes
<span style="color: #9E9E9E">**Et**</span> les actions restent dans pending_actions

---

## 16. 3 tentatives échouées - Notification utilisateur

<span style="color: #2196F3">**Étant donné**</span> que 3 tentatives de synchronisation ont échoué

<span style="color: #FF9800">**Quand**</span> la 3ème tentative échoue

<span style="color: #4CAF50">**Alors**</span> je reçois une notification:

---

## 17. Actions conservées jusqu'à sync réussie

<span style="color: #2196F3">**Étant donné**</span> que la synchronisation échoue plusieurs fois

<span style="color: #FF9800">**Quand**</span> les tentatives continuent d'échouer

<span style="color: #4CAF50">**Alors**</span> les actions restent dans pending_actions
<span style="color: #9E9E9E">**Et**</span> aucune action n'est perdue
<span style="color: #9E9E9E">**Et**</span> elles seront envoyées dès que la connexion sera stable

---

## 18. Rétention max 7 jours - Purge automatique

<span style="color: #2196F3">**Étant donné**</span> qu'une action est en attente depuis 7 jours

<span style="color: #FF9800">**Quand**</span> le système détecte cette ancienneté

<span style="color: #4CAF50">**Alors**</span> l'action est automatiquement supprimée de la queue
<span style="color: #9E9E9E">**Et**</span> je vois "1 action trop ancienne supprimée (>7 jours)"
<span style="color: #9E9E9E">**Et**</span> cela évite une queue infinie

---

## 19. Justification rétention 7 jours

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur ne se connecte jamais pendant 2 semaines

<span style="color: #FF9800">**Quand**</span> ses actions ont >7 jours

<span style="color: #4CAF50">**Alors**</span> elles sont purgées automatiquement
<span style="color: #9E9E9E">**Et**</span> évite une queue qui grandit indéfiniment

---

## 20. Retry manuel après échec

<span style="color: #2196F3">**Étant donné**</span> que la synchronisation a échoué

<span style="color: #FF9800">**Quand**</span> je clique sur "Réessayer maintenant"

<span style="color: #4CAF50">**Alors**</span> une nouvelle tentative de synchronisation est lancée immédiatement
<span style="color: #9E9E9E">**Et**</span> si elle réussit, les actions sont synchronisées

---

## 21. Backend retourne contenus supprimés

<span style="color: #2196F3">**Étant donné**</span> que j'ai liké un contenu offline
<span style="color: #F44336">**Mais**</span> que le contenu a été supprimé entre temps

<span style="color: #FF9800">**Quand**</span> le backend traite la synchronisation

<span style="color: #4CAF50">**Alors**</span> il retourne:

---

## 22. App supprime fichiers locaux contenus supprimés

<span style="color: #2196F3">**Étant donné**</span> que le backend retourne deleted_content_ids: [123, 456]

<span style="color: #FF9800">**Quand**</span> l'app traite la réponse

<span style="color: #4CAF50">**Alors**</span> elle supprime les fichiers locaux des contenus 123 et 456
<span style="color: #9E9E9E">**Et**</span> libère l'espace disque
<span style="color: #9E9E9E">**Et**</span> les actions associées sont retirées de la queue

---

## 23. Contenu supprimé en cours d'écoute

<span style="color: #2196F3">**Étant donné**</span> que j'écoute le contenu 123 en offline
<span style="color: #9E9E9E">**Et**</span> que la sync détecte que le contenu a été supprimé

<span style="color: #FF9800">**Quand**</span> la lecture actuelle se termine

<span style="color: #4CAF50">**Alors**</span> l'app attend 2 secondes
<span style="color: #9E9E9E">**Et**</span> passe automatiquement au contenu suivant
<span style="color: #9E9E9E">**Et**</span> le fichier du contenu 123 est supprimé en arrière-plan

---

## 24. Toast notification contenu retiré

<span style="color: #2196F3">**Étant donné**</span> que 2 contenus téléchargés ont été supprimés

<span style="color: #FF9800">**Quand**</span> la synchronisation se termine

<span style="color: #4CAF50">**Alors**</span> je vois un toast:

---

## 25. Contenu modéré après téléchargement

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé un contenu qui est ensuite modéré

<span style="color: #FF9800">**Quand**</span> la synchronisation détecte la modération

<span style="color: #4CAF50">**Alors**</span> le contenu est immédiatement supprimé du device
<span style="color: #9E9E9E">**Et**</span> je ne peux plus l'écouter
<span style="color: #9E9E9E">**Et**</span> cela garantit la conformité même offline

---

## 26. Justification pas de conflit possible

<span style="color: #2196F3">**Étant donné**</span> que les actions offline sont unilatérales (likes, abonnements)

<span style="color: #FF9800">**Quand**</span> elles sont synchronisées

<span style="color: #4CAF50">**Alors**</span> il n'y a pas de conflit de version possible
<span style="color: #9E9E9E">**Et**</span> pas de merge complexe nécessaire

---

## 27. Justification UX fluide offline

<span style="color: #2196F3">**Étant donné**</span> que toutes les actions fonctionnent offline

<span style="color: #FF9800">**Quand**</span> l'utilisateur interagit sans connexion

<span style="color: #4CAF50">**Alors**</span> l'expérience est identique au mode online
<span style="color: #9E9E9E">**Et**</span> l'utilisateur n'est pas bloqué
<span style="color: #9E9E9E">**Et**</span> peut utiliser l'app normalement

---

## 28. Justification batch = Économie requêtes

<span style="color: #2196F3">**Étant donné**</span> que 15 actions sont en attente

<span style="color: #FF9800">**Quand**</span> elles sont synchronisées en batch

<span style="color: #4CAF50">**Alors**</span> 1 seule requête HTTP est envoyée (vs 15 si individuelles)
<span style="color: #9E9E9E">**Et**</span> cela économise la bande passante et la batterie
<span style="color: #9E9E9E">**Et**</span> réduit la charge serveur

---

## 29. Justification conformité modération offline

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu illégal est modéré pendant qu'un user est offline

<span style="color: #FF9800">**Quand**</span> le user se reconnecte

<span style="color: #4CAF50">**Alors**</span> le contenu est immédiatement supprimé de son device
<span style="color: #9E9E9E">**Et**</span> cela garantit que les contenus illégaux disparaissent même offline

---

## 30. Historique synchronisations

<span style="color: #2196F3">**Étant donné**</span> que j'accède à "Paramètres > Synchronisation"

<span style="color: #FF9800">**Quand**</span> je consulte l'historique

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je clique sur une ligne de l'historique

<span style="color: #FF9800">**Quand**</span> le détail s'affiche

<span style="color: #4CAF50">**Alors**</span> je vois:

---

## 32. Compteur actions en attente visible

<span style="color: #2196F3">**Étant donné**</span> que j'ai 12 actions en attente de synchronisation

<span style="color: #FF9800">**Quand**</span> j'accède à l'onglet Profil

<span style="color: #4CAF50">**Alors**</span> je vois un badge "12" sur l'icône de synchronisation
<span style="color: #9E9E9E">**Et**</span> je sais qu'il y a des actions en attente

---

## 33. Synchronisation manuelle forcée

<span style="color: #2196F3">**Étant donné**</span> que je veux forcer une synchronisation immédiate

<span style="color: #FF9800">**Quand**</span> je vais dans "Paramètres > Synchronisation"
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Synchroniser maintenant"

<span style="color: #4CAF50">**Alors**</span> la synchronisation démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> toutes les actions en attente sont envoyées

---

## 34. Statistiques utilisateur - Syncs effectuées

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mes statistiques

<span style="color: #FF9800">**Quand**</span> je consulte la section Synchronisation

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un admin consulte les métriques de synchronisation

<span style="color: #FF9800">**Quand**</span> il accède au dashboard

<span style="color: #4CAF50">**Alors**</span> 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%

<span style="color: #2196F3">**Étant donné**</span> que le taux d'échec sync dépasse 10%

<span style="color: #FF9800">**Quand**</span> le système détecte cette anomalie

<span style="color: #4CAF50">**Alors**</span> une alerte est envoyée:

---

## 37. Synchronisation rapide <2s

<span style="color: #2196F3">**Étant donné**</span> que j'ai 20 actions en attente

<span style="color: #FF9800">**Quand**</span> la synchronisation démarre

<span style="color: #4CAF50">**Alors**</span> le traitement prend <2 secondes
<span style="color: #9E9E9E">**Et**</span> je ne remarque aucun ralentissement de l'app

---

## 38. Synchronisation de gros batch (100 actions)

<span style="color: #2196F3">**Étant donné**</span> que je n'ai pas synchronisé pendant 1 semaine
<span style="color: #9E9E9E">**Et**</span> que j'ai 100 actions en attente

<span style="color: #FF9800">**Quand**</span> la synchronisation démarre

<span style="color: #4CAF50">**Alors**</span> le batch de 100 actions est traité en <5 secondes
<span style="color: #9E9E9E">**Et**</span> toutes les actions sont synchronisées avec succès

---

## 39. Gestion charge serveur - 10 000 syncs simultanées

<span style="color: #2196F3">**Étant donné**</span> que 10 000 utilisateurs se reconnectent simultanément

<span style="color: #FF9800">**Quand**</span> chacun envoie un batch de 20 actions

<span style="color: #4CAF50">**Alors**</span> le serveur traite 200 000 actions
<span style="color: #9E9E9E">**Et**</span> grâce au traitement asynchrone (queue Redis), le temps de réponse reste <3s
<span style="color: #9E9E9E">**Et**</span> aucun timeout n'est constaté

---

## 40. Stockage SQLite optimisé

<span style="color: #2196F3">**Étant donné**</span> que la table pending_actions stocke des centaines d'actions

<span style="color: #FF9800">**Quand**</span> des requêtes sont exécutées

<span style="color: #4CAF50">**Alors**</span> la table est indexée sur created_at
<span style="color: #9E9E9E">**Et**</span> les requêtes SELECT et DELETE sont instantanées (<10ms)
<span style="color: #9E9E9E">**Et**</span> l'expérience utilisateur reste fluide

---

## 41. Nettoyage automatique table pending_actions

<span style="color: #2196F3">**Étant donné**</span> que la table pending_actions grossit avec le temps

<span style="color: #FF9800">**Quand**</span> les actions sont synchronisées et supprimées

<span style="color: #4CAF50">**Alors**</span> la table est automatiquement optimisée (VACUUM sur SQLite)
<span style="color: #9E9E9E">**Et**</span> l'espace disque est libéré
<span style="color: #9E9E9E">**Et**</span> les performances restent optimales

---

## 42. Action dupliquée - Idempotence

<span style="color: #2196F3">**Étant donné**</span> que j'ai liké un contenu offline
<span style="color: #9E9E9E">**Et**</span> que la sync échoue et retry

<span style="color: #FF9800">**Quand**</span> le backend reçoit 2 fois le même like

<span style="color: #4CAF50">**Alors**</span> il applique l'idempotence (1 seul like enregistré)
<span style="color: #9E9E9E">**Et**</span> le compteur de likes n'est pas faussé

---

## 43. Séquence like/unlike offline

<span style="color: #2196F3">**Étant donné**</span> que j'ai liké puis unliké un contenu offline

<span style="color: #FF9800">**Quand**</span> les 2 actions sont synchronisées

<span style="color: #4CAF50">**Alors**</span> le backend applique les 2 actions dans l'ordre
<span style="color: #9E9E9E">**Et**</span> le résultat final est "pas de like" (état correct)

---

## 44. Abonnement puis désabonnement offline

<span style="color: #2196F3">**Étant donné**</span> que je me suis abonné puis désabonné d'un créateur offline

<span style="color: #FF9800">**Quand**</span> les 2 actions sont synchronisées

<span style="color: #4CAF50">**Alors**</span> le backend applique les 2 actions dans l'ordre
<span style="color: #9E9E9E">**Et**</span> le résultat final est "pas abonné"
<span style="color: #9E9E9E">**Et**</span> les jauges évoluent correctement (+5% puis -5% = 0% net)

---

## 45. Créateur supprimé pendant offline

<span style="color: #2196F3">**Étant donné**</span> que je me suis abonné à un créateur offline
<span style="color: #F44336">**Mais**</span> que le créateur a supprimé son compte entre temps

<span style="color: #FF9800">**Quand**</span> la sync traite l'abonnement

<span style="color: #4CAF50">**Alors**</span> le backend retourne "creator_deleted"
<span style="color: #9E9E9E">**Et**</span> l'action est ignorée silencieusement
<span style="color: #9E9E9E">**Et**</span> aucune erreur n'est affichée à l'utilisateur

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis connecté à l'application RoadWave
## 1. Option "Autour de moi" - Rayon 50 km

<span style="color: #2196F3">**Étant donné**</span> que je suis à Paris (position GPS détectée)

<span style="color: #FF9800">**Quand**</span> je sélectionne "Télécharger > Autour de moi"

<span style="color: #4CAF50">**Alors**</span> l'app recherche tous les contenus géolocalisés dans un rayon de 50 km
<span style="color: #9E9E9E">**Et**</span> je vois une liste de contenus de Paris et banlieue proche
<span style="color: #9E9E9E">**Et**</span> l'estimation affiche "~150 contenus disponibles"

---

## 2. Option "Ma ville" - Limite administrative détectée

<span style="color: #2196F3">**Étant donné**</span> que je suis à Lyon (position GPS détectée)

<span style="color: #FF9800">**Quand**</span> je sélectionne "Télécharger > Ma ville"

<span style="color: #4CAF50">**Alors**</span> l'app détecte automatiquement "Lyon" comme ville
<span style="color: #9E9E9E">**Et**</span> recherche tous les contenus géolocalisés "Lyon"
<span style="color: #9E9E9E">**Et**</span> je vois uniquement les contenus de la ville de Lyon (pas banlieue)

---

## 3. Option "Mon département" - Sélection dans liste

<span style="color: #2196F3">**Étant donné**</span> que je veux télécharger des contenus pour un département

<span style="color: #FF9800">**Quand**</span> je sélectionne "Télécharger > Mon département"

<span style="color: #4CAF50">**Alors**</span> je vois une liste de tous les départements français:

    | département |
    |---|
    | 01 - Ain |
    | 02 - Aisne |
    | 75 - Paris |
    | 69 - Rhône |
    | ... |

<span style="color: #9E9E9E">**Et**</span> je peux choisir un département

---

## 4. Sélection département et téléchargement contenus

<span style="color: #2196F3">**Étant donné**</span> que je sélectionne "75 - Paris" dans la liste des départements

<span style="color: #FF9800">**Quand**</span> la sélection est confirmée

<span style="color: #4CAF50">**Alors**</span> l'app recherche tous les contenus géolocalisés "Paris"
<span style="color: #9E9E9E">**Et**</span> je vois "~234 contenus disponibles pour Paris"

---

## 5. Option "Ma région" - Sélection dans liste

<span style="color: #2196F3">**Étant donné**</span> que je veux télécharger des contenus pour une région

<span style="color: #FF9800">**Quand**</span> je sélectionne "Télécharger > Ma région"

<span style="color: #4CAF50">**Alors**</span> je vois une liste de toutes les régions françaises:

    | région |
    |---|
    | Auvergne-Rhône-Alpes |
    | Bretagne |
    | Île-de-France |
    | Nouvelle-Aquitaine |
    | Occitanie |
    | ... |

<span style="color: #9E9E9E">**Et**</span> je peux choisir une région

---

## 6. Sélection région et téléchargement contenus

<span style="color: #2196F3">**Étant donné**</span> que je sélectionne "Bretagne" dans la liste des régions

<span style="color: #FF9800">**Quand**</span> la sélection est confirmée

<span style="color: #4CAF50">**Alors**</span> 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) |

<span style="color: #9E9E9E">**Et**</span> je vois "~487 contenus disponibles pour Bretagne"

---

## 7. Recherche manuelle ville

<span style="color: #2196F3">**Étant donné**</span> que je veux télécharger des contenus pour une ville spécifique

<span style="color: #FF9800">**Quand**</span> je tape "Marseille" dans la barre de recherche

<span style="color: #4CAF50">**Alors**</span> l'app propose des suggestions:

    | suggestion |
    |---|
    | Marseille (13) |
    | Marseille-en-Beauvaisis |

<span style="color: #9E9E9E">**Et**</span> je peux sélectionner "Marseille (13)"

---

## 8. Recherche manuelle avec autocomplétion

<span style="color: #2196F3">**Étant donné**</span> que je tape "Ly" dans la barre de recherche

<span style="color: #FF9800">**Quand**</span> l'autocomplétion s'active

<span style="color: #4CAF50">**Alors**</span> je vois des suggestions:

    | suggestion |
    |---|
    | Lyon (69) |
    | Lys-lez-Lannoy |

<span style="color: #9E9E9E">**Et**</span> je peux affiner ma recherche

---

## 9. Utilisateur gratuit - Limite 50 contenus max

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit
<span style="color: #9E9E9E">**Et**</span> que j'ai déjà téléchargé 45 contenus

<span style="color: #FF9800">**Quand**</span> j'accède à la page Téléchargements

<span style="color: #4CAF50">**Alors**</span> je vois "45 / 50 contenus téléchargés"
<span style="color: #9E9E9E">**Et**</span> je peux télécharger 5 contenus supplémentaires maximum

---

## 10. Utilisateur gratuit - Tentative dépasser limite 50

<span style="color: #2196F3">**Étant donné**</span> que je suis gratuit et j'ai déjà 50 contenus téléchargés

<span style="color: #FF9800">**Quand**</span> j'essaie de télécharger un 51ème contenu

<span style="color: #4CAF50">**Alors**</span> le téléchargement est refusé
<span style="color: #9E9E9E">**Et**</span> je vois le message:

---

## 11. Utilisateur Premium - Téléchargements illimités

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium
<span style="color: #9E9E9E">**Et**</span> que j'ai déjà téléchargé 245 contenus

<span style="color: #FF9800">**Quand**</span> j'accède à la page Téléchargements

<span style="color: #4CAF50">**Alors**</span> je vois "245 contenus (3.2 GB)"
<span style="color: #9E9E9E">**Et**</span> aucune limite n'est affichée
<span style="color: #9E9E9E">**Et**</span> je peux télécharger autant de contenus que je veux

---

## 12. Limite Premium = Espace disque disponible

<span style="color: #2196F3">**Étant donné**</span> que je suis Premium
<span style="color: #9E9E9E">**Et**</span> que mon device a 500 MB d'espace disque disponible

<span style="color: #FF9800">**Quand**</span> j'essaie de télécharger 100 contenus (2 GB)

<span style="color: #4CAF50">**Alors**</span> le téléchargement échoue après ~50 contenus (500 MB)
<span style="color: #9E9E9E">**Et**</span> je vois "Espace disque insuffisant. Libérez de l'espace pour continuer."

---

## 13. Calcul temps écoute disponible gratuit

<span style="color: #2196F3">**Étant donné**</span> que je suis gratuit avec 50 contenus téléchargés
<span style="color: #9E9E9E">**Et**</span> que la durée moyenne d'un contenu est 5 minutes

<span style="color: #FF9800">**Quand**</span> je calcule le temps d'écoute disponible

<span style="color: #4CAF50">**Alors**</span> 50 contenus × 5 min = 250 minutes = 4h10 d'écoute
<span style="color: #9E9E9E">**Et**</span> cela suffit pour un trajet quotidien ou road trip court

---

## 14. Calcul temps écoute disponible Premium illimité

<span style="color: #2196F3">**Étant donné**</span> que je suis Premium avec 300 contenus téléchargés
<span style="color: #9E9E9E">**Et**</span> que la durée moyenne est 5 minutes

<span style="color: #FF9800">**Quand**</span> je calcule le temps d'écoute disponible

<span style="color: #4CAF50">**Alors**</span> 300 contenus × 5 min = 1500 minutes = 25h d'écoute
<span style="color: #9E9E9E">**Et**</span> cela suffit pour un road trip de plusieurs jours

---

## 15. Téléchargement par défaut en WiFi uniquement

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté en WiFi

<span style="color: #FF9800">**Quand**</span> je clique sur "Télécharger 20 contenus"

<span style="color: #4CAF50">**Alors**</span> le téléchargement démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> aucune popup de confirmation n'apparaît

---

## 16. Tentative téléchargement en données mobiles - Popup confirmation

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté en 4G (pas de WiFi)

<span style="color: #FF9800">**Quand**</span> je clique sur "Télécharger 20 contenus"

<span style="color: #4CAF50">**Alors**</span> une popup apparaît:

---

## 17. Calcul estimation consommation data mobile

<span style="color: #2196F3">**Étant donné**</span> que je veux télécharger 20 contenus
<span style="color: #9E9E9E">**Et**</span> que la durée moyenne est 5 minutes
<span style="color: #9E9E9E">**Et**</span> que la qualité Standard est 48 kbps Opus

<span style="color: #FF9800">**Quand**</span> l'estimation est calculée

<span style="color: #4CAF50">**Alors**</span> consommation = 20 contenus × 5 min × 48 kbps / 8 = 72 MB
<span style="color: #9E9E9E">**Et**</span> ce montant est affiché dans la popup

---

## 18. Confirmation téléchargement en données mobiles

<span style="color: #2196F3">**Étant donné**</span> que je vois la popup de confirmation données mobiles

<span style="color: #FF9800">**Quand**</span> je clique sur "Continuer quand même"

<span style="color: #4CAF50">**Alors**</span> le téléchargement démarre immédiatement via 4G
<span style="color: #9E9E9E">**Et**</span> la consommation data est comptabilisée sur mon forfait mobile

---

## 19. Refus téléchargement données mobiles - Attendre WiFi

<span style="color: #2196F3">**Étant donné**</span> que je vois la popup de confirmation données mobiles

<span style="color: #FF9800">**Quand**</span> je clique sur "Attendre WiFi"

<span style="color: #4CAF50">**Alors**</span> les téléchargements sont mis en file d'attente
<span style="color: #9E9E9E">**Et**</span> ils démarreront automatiquement quand le WiFi sera détecté

---

## 20. Détection automatique WiFi et reprise téléchargements

<span style="color: #2196F3">**Étant donné**</span> que j'ai mis 20 contenus en file d'attente (attente WiFi)

<span style="color: #FF9800">**Quand**</span> l'app détecte une connexion WiFi

<span style="color: #4CAF50">**Alors**</span> les téléchargements démarrent automatiquement
<span style="color: #9E9E9E">**Et**</span> je reçois une notification "Téléchargements en cours via WiFi"

---

## 21. Qualité Standard (48 kbps) par défaut

<span style="color: #2196F3">**Étant donné**</span> que je configure mes téléchargements

<span style="color: #FF9800">**Quand**</span> j'accède aux paramètres de qualité

<span style="color: #4CAF50">**Alors**</span> la qualité "Standard (48 kbps - ~20 MB/h)" est sélectionnée par défaut
<span style="color: #9E9E9E">**Et**</span> elle est disponible pour tous (gratuit + Premium)

---

## 22. Qualité Basse (24 kbps) disponible pour tous

<span style="color: #2196F3">**Étant donné**</span> que j'ai peu d'espace disque disponible

<span style="color: #FF9800">**Quand**</span> je sélectionne qualité "Basse (24 kbps - ~10 MB/h)"

<span style="color: #4CAF50">**Alors**</span> mes prochains téléchargements seront en 24 kbps
<span style="color: #9E9E9E">**Et**</span> l'espace utilisé sera divisé par 2 par rapport à Standard
<span style="color: #9E9E9E">**Et**</span> cette option est disponible pour gratuit + Premium

---

## 23. Qualité Haute (64 kbps) réservée Premium

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> je consulte les options de qualité

<span style="color: #4CAF50">**Alors**</span> l'option "Haute (64 kbps - ~30 MB/h)" est grisée
<span style="color: #9E9E9E">**Et**</span> je vois "👑 Premium uniquement"
<span style="color: #9E9E9E">**Et**</span> je ne peux pas la sélectionner

---

## 24. Utilisateur Premium peut choisir qualité Haute

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium

<span style="color: #FF9800">**Quand**</span> je consulte les options de qualité

<span style="color: #4CAF50">**Alors**</span> l'option "Haute (64 kbps - ~30 MB/h)" est disponible
<span style="color: #9E9E9E">**Et**</span> je peux la sélectionner pour mes téléchargements
<span style="color: #9E9E9E">**Et**</span> la qualité audio sera excellente (meilleure restitution voix et ambiances)

---

## 25. Comparaison taille fichiers selon qualité

<span style="color: #2196F3">**Étant donné**</span> que je veux télécharger 50 contenus de 5 min chacun

<span style="color: #FF9800">**Quand**</span> je compare les qualités

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le contenu RoadWave est principalement de la voix

<span style="color: #FF9800">**Quand**</span> la qualité Standard (48 kbps Opus) est utilisée

<span style="color: #4CAF50">**Alors**</span> la qualité est très correcte pour la voix
<span style="color: #9E9E9E">**Et**</span> équivalente à la radio FM
<span style="color: #9E9E9E">**Et**</span> le compromis qualité/taille est optimal

---

## 27. Justification Haute réservée Premium = Incitation upgrade

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur gratuit veut la meilleure qualité

<span style="color: #FF9800">**Quand**</span> il voit que Haute est réservée Premium

<span style="color: #4CAF50">**Alors**</span> cela l'incite à passer Premium pour 4.99€/mois
<span style="color: #9E9E9E">**Et**</span> c'est un avantage tangible supplémentaire de Premium

---

## 28. Changement qualité après téléchargements existants

<span style="color: #2196F3">**Étant donné**</span> que j'ai déjà téléchargé 30 contenus en qualité Standard

<span style="color: #FF9800">**Quand**</span> je change la qualité vers Haute (si Premium)

<span style="color: #4CAF50">**Alors**</span> les 30 contenus existants restent en Standard
<span style="color: #9E9E9E">**Et**</span> seuls les nouveaux téléchargements seront en Haute
<span style="color: #9E9E9E">**Et**</span> je peux manuellement re-télécharger les 30 contenus pour les avoir en Haute

---

## 29. Téléchargement individuel d'un contenu

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page d'un contenu

<span style="color: #FF9800">**Quand**</span> je clique sur l'icône de téléchargement 📥

<span style="color: #4CAF50">**Alors**</span> le téléchargement démarre
<span style="color: #9E9E9E">**Et**</span> une barre de progression apparaît
<span style="color: #9E9E9E">**Et**</span> l'icône devient ✅ quand terminé

---

## 30. Téléchargement batch de contenus sélectionnés

<span style="color: #2196F3">**Étant donné**</span> que je consulte une liste de contenus pour "Paris"

<span style="color: #FF9800">**Quand**</span> je sélectionne 15 contenus manuellement
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Télécharger la sélection"

<span style="color: #4CAF50">**Alors**</span> les 15 contenus sont téléchargés en parallèle (max 3 simultanés)
<span style="color: #9E9E9E">**Et**</span> une notification affiche "15 contenus téléchargés"

---

## 31. Téléchargement automatique recommandations zone

<span style="color: #2196F3">**Étant donné**</span> que je sélectionne "Autour de moi" (Paris)

<span style="color: #FF9800">**Quand**</span> je clique sur "Télécharger les 50 meilleurs contenus"

<span style="color: #4CAF50">**Alors**</span> l'algorithme sélectionne automatiquement les 50 contenus les mieux notés/récents
<span style="color: #9E9E9E">**Et**</span> les télécharge tous
<span style="color: #9E9E9E">**Et**</span> je n'ai pas besoin de choisir manuellement

---

## 32. Barre de progression téléchargement global

<span style="color: #2196F3">**Étant donné**</span> que je télécharge 20 contenus

<span style="color: #FF9800">**Quand**</span> les téléchargements sont en cours

<span style="color: #4CAF50">**Alors**</span> je vois une barre de progression globale:

---

## 33. Téléchargements en tâche de fond

<span style="color: #2196F3">**Étant donné**</span> que je lance le téléchargement de 30 contenus

<span style="color: #FF9800">**Quand**</span> je ferme l'app ou passe à une autre activité

<span style="color: #4CAF50">**Alors**</span> les téléchargements continuent en arrière-plan
<span style="color: #9E9E9E">**Et**</span> je reçois une notification quand tous sont terminés

---

## 34. Pause et reprise téléchargements

<span style="color: #2196F3">**Étant donné**</span> que je télécharge 20 contenus

<span style="color: #FF9800">**Quand**</span> je clique sur "Pause"

<span style="color: #4CAF50">**Alors**</span> les téléchargements en cours se terminent
<span style="color: #9E9E9E">**Et**</span> les téléchargements en attente sont mis en pause
<span style="color: #9E9E9E">**Et**</span> je peux cliquer sur "Reprendre" plus tard

---

## 35. Annulation téléchargements

<span style="color: #2196F3">**Étant donné**</span> que je télécharge 20 contenus

<span style="color: #FF9800">**Quand**</span> je clique sur "Annuler"

<span style="color: #4CAF50">**Alors**</span> tous les téléchargements sont arrêtés
<span style="color: #9E9E9E">**Et**</span> les fichiers partiels sont supprimés
<span style="color: #9E9E9E">**Et**</span> l'espace disque est libéré

---

## 36. Gestion erreurs téléchargement

<span style="color: #2196F3">**Étant donné**</span> que je télécharge un contenu
<span style="color: #F44336">**Mais**</span> que la connexion Internet coupe au milieu

<span style="color: #FF9800">**Quand**</span> la connexion revient

<span style="color: #4CAF50">**Alors**</span> le téléchargement reprend automatiquement où il s'était arrêté
<span style="color: #9E9E9E">**Et**</span> aucune perte de progression n'a lieu

---

## 37. Retry automatique après échec

<span style="color: #2196F3">**Étant donné**</span> qu'un téléchargement échoue 3 fois consécutives

<span style="color: #FF9800">**Quand**</span> l'échec est détecté

<span style="color: #4CAF50">**Alors**</span> le contenu est marqué "Échec"
<span style="color: #9E9E9E">**Et**</span> je vois une notification "3 contenus n'ont pas pu être téléchargés"
<span style="color: #9E9E9E">**Et**</span> je peux retry manuellement en cliquant sur "Réessayer"

---

## 38. Liste contenus téléchargés

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé 45 contenus

<span style="color: #FF9800">**Quand**</span> j'accède à "Téléchargements"

<span style="color: #4CAF50">**Alors**</span> je vois la liste complète de mes 45 contenus
<span style="color: #9E9E9E">**Et**</span> pour chaque contenu: titre, créateur, durée, taille, date téléchargement

---

## 39. Tri contenus téléchargés

<span style="color: #2196F3">**Étant donné**</span> que je consulte ma liste de téléchargements

<span style="color: #FF9800">**Quand**</span> je clique sur "Trier par"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai 200 contenus téléchargés

<span style="color: #FF9800">**Quand**</span> je tape "Tesla" dans la barre de recherche

<span style="color: #4CAF50">**Alors**</span> seuls les contenus contenant "Tesla" s'affichent
<span style="color: #9E9E9E">**Et**</span> je peux rapidement trouver un contenu spécifique

---

## 41. Suppression individuelle contenu téléchargé

<span style="color: #2196F3">**Étant donné**</span> que je veux supprimer un contenu téléchargé

<span style="color: #FF9800">**Quand**</span> je swipe left (iOS) ou long press (Android) sur le contenu
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Supprimer"

<span style="color: #4CAF50">**Alors**</span> le fichier est supprimé du device
<span style="color: #9E9E9E">**Et**</span> l'espace disque est libéré
<span style="color: #9E9E9E">**Et**</span> le compteur est décrémenté (ex: 45/50 → 44/50)

---

## 42. Suppression batch contenus téléchargés

<span style="color: #2196F3">**Étant donné**</span> que je veux supprimer plusieurs contenus

<span style="color: #FF9800">**Quand**</span> je sélectionne 10 contenus
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Supprimer la sélection"

<span style="color: #4CAF50">**Alors**</span> les 10 fichiers sont supprimés
<span style="color: #9E9E9E">**Et**</span> ~100 MB d'espace disque sont libérés
<span style="color: #9E9E9E">**Et**</span> une notification confirme "10 contenus supprimés"

---

## 43. Suppression tous les contenus téléchargés

<span style="color: #2196F3">**Étant donné**</span> que j'ai 45 contenus téléchargés

<span style="color: #FF9800">**Quand**</span> je clique sur "Supprimer tout"
<span style="color: #9E9E9E">**Et**</span> que je confirme l'action

<span style="color: #4CAF50">**Alors**</span> tous les 45 contenus sont supprimés
<span style="color: #9E9E9E">**Et**</span> l'espace disque total est libéré (~450 MB)
<span style="color: #9E9E9E">**Et**</span> le compteur repasse à 0/50

---

## 44. Espace disque utilisé visible

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé 45 contenus

<span style="color: #FF9800">**Quand**</span> j'accède à la page Téléchargements

<span style="color: #4CAF50">**Alors**</span> je vois l'espace disque utilisé:

---

## 45. Statistiques téléchargements

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mes statistiques

<span style="color: #FF9800">**Quand**</span> je consulte la section Téléchargements

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucune connexion Internet (mode avion)
<span style="color: #9E9E9E">**Et**</span> que j'ai des contenus téléchargés

<span style="color: #FF9800">**Quand**</span> je lance un contenu téléchargé

<span style="color: #4CAF50">**Alors**</span> la lecture démarre normalement depuis le fichier local
<span style="color: #9E9E9E">**Et**</span> aucune erreur de connexion n'apparaît

---

## 47. Badge "Téléchargé" sur contenus offline

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé certains contenus

<span style="color: #FF9800">**Quand**</span> je consulte une liste de contenus

<span style="color: #4CAF50">**Alors**</span> les contenus téléchargés ont un badge ✅ "Offline"
<span style="color: #9E9E9E">**Et**</span> je sais immédiatement lesquels sont disponibles sans connexion

---

## 48. Filtre "Téléchargés uniquement"

<span style="color: #2196F3">**Étant donné**</span> que je veux voir uniquement mes contenus offline

<span style="color: #FF9800">**Quand**</span> j'active le filtre "Téléchargés uniquement"

<span style="color: #4CAF50">**Alors**</span> seuls les contenus téléchargés s'affichent
<span style="color: #9E9E9E">**Et**</span> je peux facilement naviguer dans mon catalogue offline

---

## 49. Playlist offline automatique

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé 45 contenus

<span style="color: #FF9800">**Quand**</span> j'accède à "Téléchargements"

<span style="color: #4CAF50">**Alors**</span> je peux lancer une playlist aléatoire de mes 45 contenus
<span style="color: #9E9E9E">**Et**</span> profiter d'une écoute continue offline

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis connecté à l'application RoadWave
> <span style="color: #9E9E9E">**Et**</span> que j'ai des contenus téléchargés
## 1. Validité de 30 jours après téléchargement

<span style="color: #2196F3">**Étant donné**</span> que je télécharge un contenu le 1er juin 2025

<span style="color: #FF9800">**Quand**</span> le téléchargement est terminé

<span style="color: #4CAF50">**Alors**</span> le contenu est valide jusqu'au 1er juillet 2025 (30 jours)
<span style="color: #9E9E9E">**Et**</span> la date d'expiration est stockée en local

---

## 2. Affichage date expiration sur contenu téléchargé

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé un contenu il y a 20 jours

<span style="color: #FF9800">**Quand**</span> je consulte les détails du contenu

<span style="color: #4CAF50">**Alors**</span> je vois "Expire dans 10 jours"
<span style="color: #9E9E9E">**Et**</span> je sais combien de temps il reste avant expiration

---

## 3. Standard industrie aligné (Spotify, YouTube, Deezer)

<span style="color: #2196F3">**Étant donné**</span> que Spotify, YouTube Music et Deezer utilisent 30 jours

<span style="color: #FF9800">**Quand**</span> RoadWave fixe également 30 jours

<span style="color: #4CAF50">**Alors**</span> c'est le standard accepté par les utilisateurs
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de confusion avec les autres plateformes

---

## 4. Justification 30 jours - Force reconnexion régulière

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur ne se connecte jamais

<span style="color: #FF9800">**Quand**</span> ses contenus expirent après 30 jours

<span style="color: #4CAF50">**Alors**</span> il est obligé de se reconnecter pour les renouveler
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a été modéré après téléchargement

<span style="color: #FF9800">**Quand**</span> le contenu expire après 30 jours maximum

<span style="color: #4CAF50">**Alors**</span> le contenu illégal est automatiquement supprimé
<span style="color: #9E9E9E">**Et**</span> ne reste pas indéfiniment sur le device

---

## 6. Détection WiFi et contenus >25 jours

<span style="color: #2196F3">**Étant donné**</span> que j'ai des contenus téléchargés il y a 26 jours

<span style="color: #FF9800">**Quand**</span> l'app détecte une connexion WiFi

<span style="color: #4CAF50">**Alors**</span> une requête GET /offline/contents/refresh est envoyée
<span style="color: #9E9E9E">**Et**</span> le backend vérifie chaque contenu

---

## 7. Vérification abonnement Premium toujours actif

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu téléchargé en Premium est à renouveler

<span style="color: #FF9800">**Quand**</span> le backend vérifie le statut
<span style="color: #9E9E9E">**Et**</span> que l'abonnement Premium est toujours actif

<span style="color: #4CAF50">**Alors**</span> la validité est renouvelée à 30 jours supplémentaires

---

## 8. Abonnement Premium expiré - Contenu non renouvelé

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu Premium téléchargé est à renouveler

<span style="color: #FF9800">**Quand**</span> le backend vérifie le statut
<span style="color: #9E9E9E">**Et**</span> que l'abonnement Premium a expiré

<span style="color: #4CAF50">**Alors**</span> le contenu n'est pas renouvelé
<span style="color: #9E9E9E">**Et**</span> il sera supprimé à l'expiration (J-0)
<span style="color: #9E9E9E">**Et**</span> l'utilisateur voit "Contenu Premium expiré (abonnement inactif)"

---

## 9. Vérification contenu pas modéré/supprimé

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu téléchargé est à renouveler

<span style="color: #FF9800">**Quand**</span> le backend vérifie le statut
<span style="color: #9E9E9E">**Et**</span> que le contenu a été modéré ou supprimé entre temps

<span style="color: #4CAF50">**Alors**</span> le contenu n'est pas renouvelé
<span style="color: #9E9E9E">**Et**</span> sera supprimé immédiatement du device
<span style="color: #9E9E9E">**Et**</span> l'utilisateur voit "1 contenu retiré (violation règles)"

---

## 10. Mise à jour métadonnées lors du renouvellement

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu téléchargé est renouvelé

<span style="color: #FF9800">**Quand**</span> le backend traite le renouvellement

<span style="color: #4CAF50">**Alors**</span> les métadonnées sont mises à jour:

    | métadonnée | mise à jour si changée |
    |---|---|
    | Titre | ✅ |
    | Nom créateur | ✅ |
    | Description | ✅ |
    | Tags | ✅ |
    | Statut Premium | ✅ |

<span style="color: #9E9E9E">**Et**</span> l'utilisateur voit les infos à jour

---

## 11. Pas de re-téléchargement audio si fichier OK

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est renouvelé

<span style="color: #FF9800">**Quand**</span> le fichier audio local est intact

<span style="color: #4CAF50">**Alors**</span> seules les métadonnées sont mises à jour
<span style="color: #9E9E9E">**Et**</span> le fichier audio n'est pas re-téléchargé
<span style="color: #9E9E9E">**Et**</span> cela économise la bande passante

---

## 12. Re-téléchargement audio si fichier corrompu

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est renouvelé

<span style="color: #FF9800">**Quand**</span> le fichier audio local est corrompu (checksum invalide)

<span style="color: #4CAF50">**Alors**</span> le fichier audio est re-téléchargé entièrement
<span style="color: #9E9E9E">**Et**</span> le nouveau fichier remplace le corrompu

---

## 13. Renouvellement silencieux si WiFi régulier

<span style="color: #2196F3">**Étant donné**</span> que je me connecte en WiFi tous les jours

<span style="color: #FF9800">**Quand**</span> mes contenus atteignent 25-30 jours

<span style="color: #4CAF50">**Alors**</span> ils sont automatiquement renouvelés en arrière-plan
<span style="color: #9E9E9E">**Et**</span> je ne vois aucune notification (processus transparent)
<span style="color: #9E9E9E">**Et**</span> mes contenus restent valides indéfiniment

---

## 14. Renouvellement batch de plusieurs contenus

<span style="color: #2196F3">**Étant donné**</span> que j'ai 30 contenus à renouveler

<span style="color: #FF9800">**Quand**</span> le renouvellement automatique se déclenche

<span style="color: #4CAF50">**Alors**</span> une requête batch est envoyée:
<span style="color: #9E9E9E">**Et**</span> le backend traite les 30 contenus en une seule requête
<span style="color: #9E9E9E">**Et**</span> cela économise les requêtes HTTP

---

## 15. Temps de traitement renouvellement

<span style="color: #2196F3">**Étant donné**</span> que 30 contenus sont à renouveler

<span style="color: #FF9800">**Quand**</span> la requête batch est traitée

<span style="color: #4CAF50">**Alors**</span> le backend répond en <2 secondes
<span style="color: #9E9E9E">**Et**</span> les métadonnées sont mises à jour localement
<span style="color: #9E9E9E">**Et**</span> l'utilisateur ne remarque aucun ralentissement

---

## 16. Notification J-3 avant expiration

<span style="color: #2196F3">**Étant donné**</span> que j'ai 15 contenus qui expirent dans 3 jours

<span style="color: #FF9800">**Quand**</span> le système vérifie les expirations

<span style="color: #4CAF50">**Alors**</span> je reçois une notification:
<span style="color: #9E9E9E">**Et**</span> je peux agir avant l'expiration

---

## 17. Pas de notification si connexion WiFi régulière

<span style="color: #2196F3">**Étant donné**</span> que je me connecte en WiFi tous les jours
<span style="color: #9E9E9E">**Et**</span> que mes contenus sont automatiquement renouvelés

<span style="color: #FF9800">**Quand**</span> le système vérifie les expirations

<span style="color: #4CAF50">**Alors**</span> aucune notification J-3 n'est envoyée

---

## 18. Notification uniquement si contenus non renouvelés

<span style="color: #2196F3">**Étant donné**</span> que j'ai 20 contenus dont 15 renouvelés et 5 non renouvelés

<span style="color: #FF9800">**Quand**</span> le J-3 arrive pour les 5 non renouvelés

<span style="color: #4CAF50">**Alors**</span> je reçois "5 contenus expirent dans 3 jours"
<span style="color: #9E9E9E">**Et**</span> seuls les contenus à risque sont mentionnés

---

## 19. Action utilisateur après notification J-3

<span style="color: #2196F3">**Étant donné**</span> que je reçois la notification J-3

<span style="color: #FF9800">**Quand**</span> je clique sur la notification

<span style="color: #4CAF50">**Alors**</span> l'app s'ouvre sur la page Téléchargements
<span style="color: #9E9E9E">**Et**</span> je vois les contenus qui vont expirer en rouge
<span style="color: #9E9E9E">**Et**</span> je peux me connecter en WiFi pour les renouveler

---

## 20. Suppression automatique J-0 (expiration)

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu n'a pas été renouvelé

<span style="color: #FF9800">**Quand**</span> le jour d'expiration arrive (J-0)

<span style="color: #4CAF50">**Alors**</span> le fichier est automatiquement supprimé du device
<span style="color: #9E9E9E">**Et**</span> l'espace disque est libéré
<span style="color: #9E9E9E">**Et**</span> le compteur est décrémenté (ex: 45/50 → 44/50)

---

## 21. Toast après suppression automatique J-0

<span style="color: #2196F3">**Étant donné**</span> que 15 contenus viennent d'expirer

<span style="color: #FF9800">**Quand**</span> l'utilisateur ouvre l'app

<span style="color: #4CAF50">**Alors**</span> il voit un toast:

---

## 22. Liste contenus supprimés après expiration

<span style="color: #2196F3">**Étant donné**</span> que 15 contenus ont expiré

<span style="color: #FF9800">**Quand**</span> je consulte l'historique des suppressions

<span style="color: #4CAF50">**Alors**</span> 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 |
    | ... | ... | ... |

<span style="color: #9E9E9E">**Et**</span> je peux les re-télécharger si je veux

---

## 23. Re-téléchargement après expiration

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a expiré et été supprimé

<span style="color: #FF9800">**Quand**</span> je retrouve ce contenu dans l'app

<span style="color: #4CAF50">**Alors**</span> le badge ✅ "Offline" n'est plus affiché
<span style="color: #9E9E9E">**Et**</span> je peux le re-télécharger normalement
<span style="color: #9E9E9E">**Et**</span> la validité repart à 30 jours

---

## 24. Utilisateur ne se connecte jamais pendant 30 jours

<span style="color: #2196F3">**Étant donné**</span> que je télécharge 50 contenus le 1er juin
<span style="color: #F44336">**Mais**</span> que je ne me connecte jamais en WiFi pendant 30 jours

<span style="color: #FF9800">**Quand**</span> le 1er juillet arrive

<span style="color: #4CAF50">**Alors**</span> tous les 50 contenus expirent
<span style="color: #9E9E9E">**Et**</span> sont automatiquement supprimés
<span style="color: #9E9E9E">**Et**</span> je n'ai plus aucun contenu offline

---

## 25. Utilisateur en zone blanche 30+ jours

<span style="color: #2196F3">**Étant donné**</span> que je télécharge 50 contenus avant de partir en zone sans réseau
<span style="color: #9E9E9E">**Et**</span> que je reste 45 jours sans connexion

<span style="color: #FF9800">**Quand**</span> les contenus expirent après 30 jours

<span style="color: #4CAF50">**Alors**</span> ils sont supprimés même si je ne peux pas me connecter
<span style="color: #9E9E9E">**Et**</span> je perds l'accès à mes contenus offline

---

## 26. Recommandation téléchargement avant zone blanche longue

<span style="color: #2196F3">**Étant donné**</span> que je prépare un road trip de 60 jours

<span style="color: #FF9800">**Quand**</span> je consulte la FAQ

<span style="color: #4CAF50">**Alors**</span> je vois la recommandation:

---

## 27. Changement statut Premium en gratuit pendant validité

<span style="color: #2196F3">**Étant donné**</span> que je suis Premium et j'ai téléchargé 200 contenus

<span style="color: #FF9800">**Quand**</span> mon abonnement Premium expire
<span style="color: #9E9E9E">**Et**</span> que je repasse en gratuit

<span style="color: #4CAF50">**Alors**</span> au prochain renouvellement, seulement 50 contenus sont conservés
<span style="color: #9E9E9E">**Et**</span> les 150 autres sont supprimés (limite gratuit)
<span style="color: #9E9E9E">**Et**</span> je vois "Limite gratuit (50 contenus) appliquée. 150 contenus supprimés."

---

## 28. Sélection automatique 50 meilleurs contenus si passage gratuit

<span style="color: #2196F3">**Étant donné**</span> que je repasse en gratuit avec 200 contenus téléchargés

<span style="color: #FF9800">**Quand**</span> le système applique la limite de 50

<span style="color: #4CAF50">**Alors**</span> les 50 contenus les plus récemment écoutés sont conservés
<span style="color: #9E9E9E">**Et**</span> les 150 autres sont supprimés
<span style="color: #9E9E9E">**Et**</span> cela maximise les chances de garder les contenus que j'aime

---

## 29. Contenus Premium exclusifs supprimés si abonnement expire

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé 20 contenus Premium exclusifs

<span style="color: #FF9800">**Quand**</span> mon abonnement Premium expire

<span style="color: #4CAF50">**Alors**</span> les 20 contenus Premium sont immédiatement supprimés
<span style="color: #9E9E9E">**Et**</span> je vois "20 contenus Premium supprimés (abonnement expiré)"

---

## 30. Affichage temps restant avant expiration

<span style="color: #2196F3">**Étant donné**</span> que j'ai 45 contenus téléchargés

<span style="color: #FF9800">**Quand**</span> je consulte la page Téléchargements

<span style="color: #4CAF50">**Alors**</span> je vois pour chaque contenu:

    | contenu | temps restant |
    |---|---|
    | Mon épisode (récent) | Expire dans 28 jours |
    | Road trip (ancien) | Expire dans 3 jours |

<span style="color: #9E9E9E">**Et**</span> je sais lesquels sont prioritaires pour renouvellement

---

## 31. Tri par date expiration

<span style="color: #2196F3">**Étant donné**</span> que j'ai 45 contenus avec différentes dates d'expiration

<span style="color: #FF9800">**Quand**</span> je trie par "Expiration"

<span style="color: #4CAF50">**Alors**</span> les contenus qui expirent le plus tôt apparaissent en premier
<span style="color: #9E9E9E">**Et**</span> je peux voir rapidement lesquels nécessitent une reconnexion urgente

---

## 32. Badge rouge si expiration <3 jours

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu expire dans 2 jours

<span style="color: #FF9800">**Quand**</span> je consulte la liste des téléchargements

<span style="color: #4CAF50">**Alors**</span> le contenu a un badge rouge "⚠️ Expire bientôt"
<span style="color: #9E9E9E">**Et**</span> il est visuellement mis en avant

---

## 33. Statistiques utilisateur - Taux de renouvellement

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mes statistiques

<span style="color: #FF9800">**Quand**</span> je consulte la section Téléchargements

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un admin consulte les métriques offline

<span style="color: #FF9800">**Quand**</span> il accède au dashboard

<span style="color: #4CAF50">**Alors**</span> 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%

<span style="color: #2196F3">**Étant donné**</span> que le taux d'expiration mensuel dépasse 10%

<span style="color: #FF9800">**Quand**</span> le système détecte cette anomalie

<span style="color: #4CAF50">**Alors**</span> une alerte est envoyée:

---

## 36. Email rappel si pas de connexion WiFi depuis 20 jours

<span style="color: #2196F3">**Étant donné**</span> que je n'ai pas connecté l'app en WiFi depuis 20 jours
<span style="color: #9E9E9E">**Et**</span> que j'ai 45 contenus téléchargés

<span style="color: #FF9800">**Quand**</span> le système détecte cette inactivité WiFi

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 37. Performance renouvellement avec 10 000 utilisateurs simultanés

<span style="color: #2196F3">**Étant donné**</span> que 10 000 utilisateurs se connectent en WiFi simultanément

<span style="color: #FF9800">**Quand**</span> chacun demande le renouvellement de 50 contenus

<span style="color: #4CAF50">**Alors**</span> le serveur traite 500 000 vérifications
<span style="color: #9E9E9E">**Et**</span> grâce au cache Redis et index PostgreSQL, le temps de réponse reste <3s
<span style="color: #9E9E9E">**Et**</span> les serveurs gèrent la charge sans problème

---

## 38. Logs audit renouvellements

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est renouvelé

<span style="color: #FF9800">**Quand**</span> l'opération se termine

<span style="color: #4CAF50">**Alors**</span> 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) |

<span style="color: #9E9E9E">**Et**</span> ces logs aident à débugger les problèmes

---





<div style="page-break-after: always;"></div>


# Modération préventive
**22 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que le système de modération préventive est actif
## 1. Créateur nouvellement inscrit

<span style="color: #2196F3">**Étant donné**</span> que je viens de créer un compte créateur
<span style="color: #9E9E9E">**Et**</span> que je n'ai jamais publié de contenu

<span style="color: #FF9800">**Quand**</span> j'examine mon statut de créateur

<span style="color: #4CAF50">**Alors**</span> mon compte est marqué comme "Nouveau créateur"
<span style="color: #9E9E9E">**Et**</span> mes 3 premiers contenus devront être validés manuellement
<span style="color: #9E9E9E">**Et**</span> je suis informé de ce processus lors de l'onboarding

---

## 2. Publication du premier contenu par un nouveau créateur

<span style="color: #2196F3">**Étant donné**</span> que je suis un nouveau créateur
<span style="color: #9E9E9E">**Et**</span> que je n'ai publié aucun contenu auparavant

<span style="color: #FF9800">**Quand**</span> je publie mon premier contenu

<span style="color: #4CAF50">**Alors**</span> le contenu entre en file d'attente de validation manuelle
<span style="color: #9E9E9E">**Et**</span> le statut du contenu est "En attente de validation"
<span style="color: #9E9E9E">**Et**</span> le contenu n'est pas diffusé sur la plateforme
<span style="color: #9E9E9E">**Et**</span> je reçois une notification:

---

## 3. Validation manuelle par un modérateur

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié mon premier contenu
<span style="color: #9E9E9E">**Et**</span> que le contenu est en attente de validation

<span style="color: #FF9800">**Quand**</span> un modérateur examine mon contenu

<span style="color: #4CAF50">**Alors**</span> le modérateur utilise la transcription automatique Whisper
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> si tout est conforme, le contenu est validé

---

## 4. Délai de validation de 24-48h jours ouvrés

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié mon premier contenu lundi à 10:00

<span style="color: #FF9800">**Quand**</span> le contenu entre en file de validation

<span style="color: #4CAF50">**Alors**</span> le contenu est validé avant mercredi 10:00 (48h jours ouvrés)
<span style="color: #9E9E9E">**Et**</span> dans la plupart des cas, la validation est effectuée sous 24h
<span style="color: #9E9E9E">**Et**</span> je reçois une notification dès que le contenu est validé

---

## 5. Notification de validation réussie

<span style="color: #2196F3">**Étant donné**</span> que mon premier contenu a été validé par un modérateur

<span style="color: #FF9800">**Quand**</span> la validation est approuvée

<span style="color: #4CAF50">**Alors**</span> je reçois une notification:
<span style="color: #9E9E9E">**Et**</span> le statut du contenu passe à "Publié"
<span style="color: #9E9E9E">**Et**</span> le contenu devient visible pour tous les utilisateurs
<span style="color: #9E9E9E">**Et**</span> il entre dans l'algorithme de recommandation

---

## 6. Refus de validation si contenu non conforme

<span style="color: #2196F3">**Étant donné**</span> que mon premier contenu viole les règles de la communauté

<span style="color: #FF9800">**Quand**</span> le modérateur examine le contenu

<span style="color: #4CAF50">**Alors**</span> le contenu est refusé
<span style="color: #9E9E9E">**Et**</span> je reçois une notification détaillée:
<span style="color: #9E9E9E">**Et**</span> le contenu reste en statut "Refusé"
<span style="color: #9E9E9E">**Et**</span> je peux modifier et republier

---

## 7. Les 3 premiers contenus sont validés manuellement

<span style="color: #2196F3">**Étant donné**</span> que je suis un nouveau créateur

<span style="color: #FF9800">**Quand**</span> je publie mes contenus

<span style="color: #4CAF50">**Alors**</span> les contenus suivants nécessitent une validation manuelle:

    | contenu | validation manuelle |
    |---|---|
    | 1er | oui |
    | 2ème | oui |
    | 3ème | oui |
    | 4ème | non (auto) |

<span style="color: #9E9E9E">**Et**</span> après 3 contenus validés, mes futurs contenus sont publiés automatiquement

---

## 8. Passage en mode automatique après 3 validations

<span style="color: #2196F3">**Étant donné**</span> que mes 3 premiers contenus ont été validés avec succès

<span style="color: #FF9800">**Quand**</span> je publie mon 4ème contenu

<span style="color: #4CAF50">**Alors**</span> le contenu est publié automatiquement
<span style="color: #9E9E9E">**Et**</span> aucune validation manuelle n'est requise
<span style="color: #9E9E9E">**Et**</span> le statut passe directement à "Publié"
<span style="color: #9E9E9E">**Et**</span> je reçois une notification:

---

## 9. Évolution du score de confiance

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur établi

<span style="color: #FF9800">**Quand**</span> le système évalue mon historique

<span style="color: #4CAF50">**Alors**</span> 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% |

<span style="color: #9E9E9E">**Et**</span> le score évolue dynamiquement

---

## 10. Créateur fiable - Publication automatique

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur
<span style="color: #9E9E9E">**Et**</span> que j'ai 0 strike depuis 6 mois
<span style="color: #9E9E9E">**Et**</span> que tous mes contenus précédents ont été conformes

<span style="color: #FF9800">**Quand**</span> mon score de confiance est calculé

<span style="color: #4CAF50">**Alors**</span> je suis classé comme "Créateur fiable"
<span style="color: #9E9E9E">**Et**</span> tous mes nouveaux contenus sont publiés automatiquement
<span style="color: #9E9E9E">**Et**</span> aucune validation manuelle n'est nécessaire
<span style="color: #9E9E9E">**Et**</span> je bénéficie d'une publication instantanée

---

## 11. Créateur suspect - Validation manuelle systématique

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu 2 strikes récents (< 3 mois)

<span style="color: #FF9800">**Quand**</span> mon score de confiance est recalculé

<span style="color: #4CAF50">**Alors**</span> je suis classé comme "Créateur suspect"
<span style="color: #9E9E9E">**Et**</span> tous mes nouveaux contenus nécessitent une validation manuelle
<span style="color: #9E9E9E">**Et**</span> chaque contenu est examiné avant publication
<span style="color: #9E9E9E">**Et**</span> je suis notifié de ce changement de statut:

---

## 12. Réhabilitation après période sans incident

<span style="color: #2196F3">**Étant donné**</span> que j'étais un "Créateur suspect"
<span style="color: #9E9E9E">**Et**</span> que je publie 10 contenus conformes sur 6 mois
<span style="color: #9E9E9E">**Et**</span> que je ne reçois aucun nouveau strike

<span style="color: #FF9800">**Quand**</span> le système réévalue mon score de confiance

<span style="color: #4CAF50">**Alors**</span> je passe en "Créateur fiable"
<span style="color: #9E9E9E">**Et**</span> la publication automatique est rétablie
<span style="color: #9E9E9E">**Et**</span> je reçois une notification de réhabilitation:

---

## 13. Toute publicité nécessite validation manuelle

<span style="color: #2196F3">**Étant donné**</span> qu'un annonceur soumet une publicité audio

<span style="color: #FF9800">**Quand**</span> la publicité est créée

<span style="color: #4CAF50">**Alors**</span> elle entre automatiquement en file de validation manuelle
<span style="color: #9E9E9E">**Et**</span> aucune publicité n'est diffusée sans validation préalable
<span style="color: #9E9E9E">**Et**</span> cela est obligatoire pour des raisons de responsabilité juridique

---

## 14. Validation d'une publicité - Processus complet

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité est en attente de validation

<span style="color: #FF9800">**Quand**</span> un modérateur senior examine la publicité

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> si tout est conforme, la publicité est validée

---

## 15. Délai de validation d'une publicité - 24-48h

<span style="color: #2196F3">**Étant donné**</span> qu'un annonceur soumet une publicité lundi à 10:00

<span style="color: #FF9800">**Quand**</span> la publicité entre en file de validation

<span style="color: #4CAF50">**Alors**</span> la publicité est validée avant mercredi 10:00 (48h jours ouvrés)
<span style="color: #9E9E9E">**Et**</span> l'annonceur est notifié dès la validation
<span style="color: #9E9E9E">**Et**</span> la campagne publicitaire peut alors démarrer

---

## 16. Refus de validation d'une publicité

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité contient des éléments non conformes

<span style="color: #FF9800">**Quand**</span> le modérateur examine la publicité

<span style="color: #4CAF50">**Alors**</span> la publicité est refusée
<span style="color: #9E9E9E">**Et**</span> l'annonceur reçoit une notification détaillée:
<span style="color: #9E9E9E">**Et**</span> l'annonceur peut modifier et resoumettre la publicité
<span style="color: #9E9E9E">**Et**</span> aucun remboursement n'est effectué pour une publicité refusée

---

## 17. Économie de modération grâce à la prévention

<span style="color: #2196F3">**Étant donné**</span> que la modération préventive est active

<span style="color: #FF9800">**Quand**</span> on analyse l'efficacité du système

<span style="color: #4CAF50">**Alors**</span> 80% des contenus inappropriés sont détectés avant publication
<span style="color: #9E9E9E">**Et**</span> cela réduit le nombre de signalements de 70%
<span style="color: #9E9E9E">**Et**</span> les ressources de modération sont optimisées
<span style="color: #9E9E9E">**Et**</span> la qualité de la plateforme est préservée dès le début

---

## 18. Qualité de la plateforme maintenue

<span style="color: #2196F3">**Étant donné**</span> que tous les nouveaux créateurs sont vérifiés

<span style="color: #FF9800">**Quand**</span> on analyse la qualité globale des contenus

<span style="color: #4CAF50">**Alors**</span> le taux de contenus inappropriés est <1%
<span style="color: #9E9E9E">**Et**</span> les utilisateurs font confiance à la plateforme
<span style="color: #9E9E9E">**Et**</span> la réputation de RoadWave est préservée
<span style="color: #9E9E9E">**Et**</span> l'expérience utilisateur est optimale

---

## 19. Information claire sur le processus de validation

<span style="color: #2196F3">**Étant donné**</span> que je suis un nouveau créateur

<span style="color: #FF9800">**Quand**</span> je consulte la page d'aide "Validation des contenus"

<span style="color: #4CAF50">**Alors**</span> j'apprends que:
<span style="color: #9E9E9E">**Et**</span> le processus est clair et transparent

---

## 20. Badge "Créateur vérifié" après validation

<span style="color: #2196F3">**Étant donné**</span> que mes 3 premiers contenus ont été validés avec succès

<span style="color: #FF9800">**Quand**</span> je consulte mon profil créateur

<span style="color: #4CAF50">**Alors**</span> un badge discret "✓ Créateur vérifié" s'affiche
<span style="color: #9E9E9E">**Et**</span> ce badge rassure les auditeurs sur la qualité de mes contenus
<span style="color: #9E9E9E">**Et**</span> il améliore ma crédibilité sur la plateforme

---

## 21. Justification de la modération préventive

<span style="color: #2196F3">**Étant donné**</span> que la modération préventive est en place

<span style="color: #FF9800">**Quand**</span> on évalue les bénéfices

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> l'investissement dans la prévention est rentable

---

## 22. Coût de la modération préventive

<span style="color: #2196F3">**Étant donné**</span> que 100 nouveaux créateurs publient 3 contenus chacun
<span style="color: #9E9E9E">**Et**</span> que 50 publicités sont soumises par mois

<span style="color: #FF9800">**Quand**</span> on calcule le coût de modération préventive

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le coût total est d'environ 33h de modération/mois
<span style="color: #9E9E9E">**Et**</span> c'est largement compensé par la réduction des signalements réactifs

---





<div style="page-break-after: always;"></div>


# Sanctions et notifications de modération
**27 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un créateur de contenu
> <span style="color: #9E9E9E">**Et**</span> que j'ai publié un contenu
## 1. Notification multi-canal après sanction

<span style="color: #2196F3">**Étant donné**</span> que mon contenu a été modéré

<span style="color: #FF9800">**Quand**</span> la sanction est appliquée

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> chaque canal contient un lien vers les détails complets

---

## 2. Notification push immédiate

<span style="color: #2196F3">**Étant donné**</span> que mon contenu vient d'être modéré

<span style="color: #FF9800">**Quand**</span> la sanction est appliquée

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push immédiate
<span style="color: #9E9E9E">**Et**</span> le message est court: "⚠️ Votre contenu a été modéré"
<span style="color: #9E9E9E">**Et**</span> je peux cliquer pour voir les détails
<span style="color: #9E9E9E">**Et**</span> la notification utilise Firebase Cloud Messaging (Android) ou APNs (iOS)
<span style="color: #9E9E9E">**Et**</span> le coût est de 0€

---

## 3. Popup in-app au prochain lancement

<span style="color: #2196F3">**Étant donné**</span> que mon contenu a été modéré

<span style="color: #FF9800">**Quand**</span> j'ouvre l'application

<span style="color: #4CAF50">**Alors**</span> une popup détaillée s'affiche automatiquement
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> je ne peux pas fermer la popup sans l'avoir vue

---

## 4. Email de notification complet dans l'heure

<span style="color: #2196F3">**Étant donné**</span> que mon contenu a été modéré à 14:00

<span style="color: #FF9800">**Quand**</span> la sanction est appliquée

<span style="color: #4CAF50">**Alors**</span> je reçois un email avant 15:00 (dans l'heure)
<span style="color: #9E9E9E">**Et**</span> l'objet de l'email est "Modération de votre contenu \"[Titre du contenu]\""
<span style="color: #9E9E9E">**Et**</span> l'email contient toutes les informations détaillées
<span style="color: #9E9E9E">**Et**</span> le coût est d'environ 0.001€ par email (Brevo, Resend)

---

## 5. Email de notification complet et structuré

<span style="color: #2196F3">**Étant donné**</span> que mon contenu "Mon podcast #42" a été modéré

<span style="color: #FF9800">**Quand**</span> je reçois l'email de notification

<span style="color: #4CAF50">**Alors**</span> l'email contient la structure suivante:

---

## 6. Page détaillée de la sanction in-app

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Voir détails" dans la notification

<span style="color: #FF9800">**Quand**</span> la page détaillée s'affiche

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la page détaillée de la sanction est affichée

<span style="color: #FF9800">**Quand**</span> je consulte l'extrait audio concerné

<span style="color: #4CAF50">**Alors**</span> le timestamp exact est affiché: "3:42-4:15"
<span style="color: #9E9E9E">**Et**</span> je peux écouter uniquement cette portion de l'audio
<span style="color: #9E9E9E">**Et**</span> un player audio intégré permet l'écoute du passage
<span style="color: #9E9E9E">**Et**</span> la transcription correspondante est affichée en dessous
<span style="color: #9E9E9E">**Et**</span> les mots/phrases problématiques sont surlignés en rouge

---

## 8. Référence précise aux CGU

<span style="color: #2196F3">**Étant donné**</span> que la sanction fait référence à l'Article 3.2 des CGU

<span style="color: #FF9800">**Quand**</span> je clique sur "Article 3.2"

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers la section correspondante des CGU
<span style="color: #9E9E9E">**Et**</span> la section "Haine & violence" est mise en évidence
<span style="color: #9E9E9E">**Et**</span> je peux lire exactement ce qui est interdit
<span style="color: #9E9E9E">**Et**</span> cela m'aide à comprendre mon erreur

---

## 9. Gravité de la sanction avec système de strikes

<span style="color: #2196F3">**Étant donné**</span> que c'est mon 2ème strike

<span style="color: #FF9800">**Quand**</span> je consulte les détails de la sanction

<span style="color: #4CAF50">**Alors**</span> je vois clairement "Strike 2/4"
<span style="color: #9E9E9E">**Et**</span> les conséquences sont explicitées:
<span style="color: #9E9E9E">**Et**</span> je comprends l'escalade des sanctions

---

## 10. Accès au formulaire d'appel depuis la notification

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu une notification de modération

<span style="color: #FF9800">**Quand**</span> je clique sur "Contester cette décision"

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers le formulaire d'appel
<span style="color: #9E9E9E">**Et**</span> le formulaire est pré-rempli avec les informations de la sanction
<span style="color: #9E9E9E">**Et**</span> je peux commencer à rédiger mon appel

---

## 11. Accès au formulaire d'appel depuis "Mes sanctions"

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu une sanction il y a 2 jours

<span style="color: #FF9800">**Quand**</span> j'ouvre "Profil créateur > Mes sanctions"

<span style="color: #4CAF50">**Alors**</span> je vois la liste de mes sanctions
<span style="color: #9E9E9E">**Et**</span> chaque sanction a un bouton "Faire appel" (si délai <7j)
<span style="color: #9E9E9E">**Et**</span> je peux accéder au formulaire d'appel

---

## 12. Structure du formulaire d'appel

<span style="color: #2196F3">**Étant donné**</span> que j'ouvre le formulaire d'appel

<span style="color: #FF9800">**Quand**</span> le formulaire s'affiche

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> tous les champs obligatoires sont marqués d'un astérisque

---

## 13. Validation du formulaire d'appel

<span style="color: #2196F3">**Étant donné**</span> que je remplis le formulaire d'appel

<span style="color: #FF9800">**Quand**</span> je clique sur "Soumettre l'appel"

<span style="color: #4CAF50">**Alors**</span> le système valide les champs obligatoires
<span style="color: #9E9E9E">**Et**</span> si un champ obligatoire est vide, une erreur s'affiche
<span style="color: #9E9E9E">**Et**</span> si la raison fait moins de 50 caractères, une erreur s'affiche
<span style="color: #9E9E9E">**Et**</span> si tout est valide, l'appel est soumis

---

## 14. Confirmation après soumission de l'appel

<span style="color: #2196F3">**Étant donné**</span> que j'ai soumis un appel valide

<span style="color: #FF9800">**Quand**</span> l'appel est enregistré

<span style="color: #4CAF50">**Alors**</span> un numéro de ticket unique est généré: "#MOD-2026-00142"
<span style="color: #9E9E9E">**Et**</span> un email de confirmation est envoyé:
<span style="color: #9E9E9E">**Et**</span> le statut de l'appel est "En cours d'examen"
<span style="color: #9E9E9E">**Et**</span> je peux suivre le statut dans "Mes sanctions"

---

## 15. Délai de soumission de 7 jours maximum

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu une sanction le 2026-01-15

<span style="color: #FF9800">**Quand**</span> j'essaie de faire appel le 2026-01-25 (10 jours plus tard)

<span style="color: #4CAF50">**Alors**</span> le formulaire d'appel est désactivé
<span style="color: #9E9E9E">**Et**</span> un message s'affiche:
<span style="color: #9E9E9E">**Et**</span> je ne peux plus contester la sanction

---

## 16. Bouton "Faire appel" visible si délai respecté

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu une sanction il y a 3 jours

<span style="color: #FF9800">**Quand**</span> je consulte "Mes sanctions"

<span style="color: #4CAF50">**Alors**</span> le bouton "Faire appel" est actif
<span style="color: #9E9E9E">**Et**</span> un compteur indique "4 jours restants pour faire appel"
<span style="color: #9E9E9E">**Et**</span> je peux cliquer pour soumettre un appel

---

## 17. SLA de 72h garanti pour appel standard

<span style="color: #2196F3">**Étant donné**</span> que j'ai soumis un appel standard le lundi à 10:00

<span style="color: #FF9800">**Quand**</span> l'appel est en cours de traitement

<span style="color: #4CAF50">**Alors**</span> un modérateur senior est assigné
<span style="color: #9E9E9E">**Et**</span> l'appel doit être traité avant jeudi 10:00 (72h - 3 jours ouvrés)
<span style="color: #9E9E9E">**Et**</span> je reçois une réponse dans ce délai

---

## 18. Appel complexe avec notification intermédiaire

<span style="color: #2196F3">**Étant donné**</span> que j'ai soumis un appel complexe
<span style="color: #9E9E9E">**Et**</span> que le traitement nécessite plus de 72h

<span style="color: #FF9800">**Quand**</span> 3 jours se sont écoulés

<span style="color: #4CAF50">**Alors**</span> je reçois un email de notification intermédiaire:
<span style="color: #9E9E9E">**Et**</span> l'appel est traité sous 5 jours ouvrés au total
<span style="color: #9E9E9E">**Et**</span> un modérateur senior + admin modération examinent le cas

---

## 19. Appel CRITIQUE traité en 24h

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu une suspension longue ou un ban
<span style="color: #9E9E9E">**Et**</span> que je soumets un appel

<span style="color: #FF9800">**Quand**</span> l'appel est classé en priorité CRITIQUE

<span style="color: #4CAF50">**Alors**</span> l'admin modération traite l'appel sous 24h
<span style="color: #9E9E9E">**Et**</span> je reçois une réponse rapide
<span style="color: #9E9E9E">**Et**</span> le cas est examiné en priorité absolue

---

## 20. Réponse finale détaillée - Appel accepté

<span style="color: #2196F3">**Étant donné**</span> que mon appel est accepté

<span style="color: #FF9800">**Quand**</span> je reçois la réponse finale

<span style="color: #4CAF50">**Alors**</span> 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" |

<span style="color: #9E9E9E">**Et**</span> le strike est retiré de mon compte
<span style="color: #9E9E9E">**Et**</span> le contenu est rétabli sur la plateforme
<span style="color: #9E9E9E">**Et**</span> je peux continuer normalement

---

## 21. Réponse finale détaillée - Appel rejeté

<span style="color: #2196F3">**Étant donné**</span> que mon appel est rejeté

<span style="color: #FF9800">**Quand**</span> je reçois la réponse finale

<span style="color: #4CAF50">**Alors**</span> 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" |

<span style="color: #9E9E9E">**Et**</span> la sanction reste active
<span style="color: #9E9E9E">**Et**</span> je ne peux pas faire de second appel
<span style="color: #9E9E9E">**Et**</span> je dois respecter la suspension

---

## 22. Réponse finale - Réduction de sanction

<span style="color: #2196F3">**Étant donné**</span> que mon appel est partiellement accepté

<span style="color: #FF9800">**Quand**</span> je reçois la réponse finale

<span style="color: #4CAF50">**Alors**</span> la décision est "Réduction de sanction"
<span style="color: #9E9E9E">**Et**</span> l'email explique:
<span style="color: #9E9E9E">**Et**</span> le strike est réduit
<span style="color: #9E9E9E">**Et**</span> la suspension est raccourcie
<span style="color: #9E9E9E">**Et**</span> je suis notifié de la nouvelle date de fin

---

## 23. Suivi du statut de l'appel in-app

<span style="color: #2196F3">**Étant donné**</span> que j'ai soumis un appel

<span style="color: #FF9800">**Quand**</span> je consulte "Mes sanctions"

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> une notification badge m'alerte quand le statut change

---

## 24. Historique complet des sanctions visible

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur

<span style="color: #FF9800">**Quand**</span> j'ouvre "Profil créateur > Mes sanctions"

<span style="color: #4CAF50">**Alors**</span> 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é |

<span style="color: #9E9E9E">**Et**</span> les sanctions sont triées par date décroissante

---

## 25. Conformité DSA - Transparence obligatoire

<span style="color: #2196F3">**Étant donné**</span> que le système de sanction est en place

<span style="color: #FF9800">**Quand**</span> un audit DSA est effectué

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le système est conforme au Digital Services Act

---

## 26. Décision définitive après premier appel

<span style="color: #2196F3">**Étant donné**</span> que mon premier appel a été rejeté

<span style="color: #FF9800">**Quand**</span> j'essaie de faire un second appel

<span style="color: #4CAF50">**Alors**</span> le bouton "Faire appel" est désactivé
<span style="color: #9E9E9E">**Et**</span> un message s'affiche: "Cette décision est définitive. Aucun second appel n'est possible."
<span style="color: #9E9E9E">**Et**</span> je ne peux plus contester la sanction
<span style="color: #9E9E9E">**Et**</span> je dois respecter la décision finale

---

## 27. Coût des notifications multi-canal

<span style="color: #2196F3">**Étant donné**</span> que 100 sanctions sont appliquées en un mois

<span style="color: #FF9800">**Quand**</span> on calcule le coût des notifications

<span style="color: #4CAF50">**Alors**</span> 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€ |

<span style="color: #9E9E9E">**Et**</span> le coût est négligeable même à grande échelle

---





<div style="page-break-after: always;"></div>


# Signalement de contenu inapproprié
**23 scénarios** (22 standards, 1 plan)

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur connecté
> <span style="color: #9E9E9E">**Et**</span> que je suis en train d'écouter un contenu
## 1. Affichage du formulaire de signalement

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu inapproprié

<span style="color: #FF9800">**Quand**</span> j'ouvre le menu du contenu
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Signaler"

<span style="color: #4CAF50">**Alors**</span> un formulaire de signalement s'affiche
<span style="color: #9E9E9E">**Et**</span> le formulaire contient une liste déroulante "Catégorie du problème"
<span style="color: #9E9E9E">**Et**</span> le formulaire contient un champ texte "Commentaire (optionnel)"
<span style="color: #9E9E9E">**Et**</span> le formulaire contient un bouton "Envoyer le signalement"

---

## 2. Liste des 7 catégories prédéfinies

<span style="color: #2196F3">**Étant donné**</span> que le formulaire de signalement est affiché

<span style="color: #FF9800">**Quand**</span> je clique sur la liste déroulante "Catégorie du problème"

<span style="color: #4CAF50">**Alors**</span> 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é |

<span style="color: #9E9E9E">**Et**</span> chaque catégorie a une description claire

---

## 3. Sélection de la catégorie "Haine & violence"

<span style="color: #2196F3">**Étant donné**</span> que le formulaire de signalement est affiché

<span style="color: #FF9800">**Quand**</span> je sélectionne la catégorie "🚫 Haine & violence"

<span style="color: #4CAF50">**Alors**</span> la catégorie est sélectionnée
<span style="color: #9E9E9E">**Et**</span> la description "Incitation à la haine, discrimination, menaces" s'affiche
<span style="color: #9E9E9E">**Et**</span> je peux passer au champ commentaire

---

## 4. Catégorie "Autre" nécessite un commentaire obligatoire

<span style="color: #2196F3">**Étant donné**</span> que le formulaire de signalement est affiché

<span style="color: #FF9800">**Quand**</span> je sélectionne la catégorie "🔧 Autre"

<span style="color: #4CAF50">**Alors**</span> le champ "Commentaire" devient obligatoire
<span style="color: #9E9E9E">**Et**</span> un message s'affiche: "Veuillez décrire le problème (obligatoire)"
<span style="color: #9E9E9E">**Et**</span> le placeholder change en "Décrivez le problème rencontré"
<span style="color: #9E9E9E">**Et**</span> je ne peux pas envoyer le signalement sans commentaire

---

## 5. Champ commentaire optionnel avec incitation

<span style="color: #2196F3">**Étant donné**</span> que le formulaire de signalement est affiché
<span style="color: #9E9E9E">**Et**</span> que j'ai sélectionné une catégorie autre que "Autre"

<span style="color: #FF9800">**Quand**</span> je consulte le champ "Commentaire"

<span style="color: #4CAF50">**Alors**</span> le champ est optionnel (pas d'astérisque rouge)
<span style="color: #9E9E9E">**Et**</span> le placeholder indique "Décrivez le problème (optionnel mais recommandé)"
<span style="color: #9E9E9E">**Et**</span> la limite de caractères est de 500
<span style="color: #9E9E9E">**Et**</span> un compteur affiche "0/500"

---

## 6. Envoi de signalement sans commentaire

<span style="color: #2196F3">**Étant donné**</span> que j'ai sélectionné la catégorie "📧 Spam"
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas rempli le champ commentaire

<span style="color: #FF9800">**Quand**</span> je clique sur "Envoyer le signalement"

<span style="color: #4CAF50">**Alors**</span> le signalement est envoyé avec succès
<span style="color: #9E9E9E">**Et**</span> aucune erreur de validation ne s'affiche
<span style="color: #9E9E9E">**Et**</span> le commentaire est enregistré comme vide

---

## 7. Envoi de signalement avec commentaire

<span style="color: #2196F3">**Étant donné**</span> que j'ai sélectionné la catégorie "🚫 Haine & violence"
<span style="color: #9E9E9E">**Et**</span> que j'ai saisi le commentaire "Le créateur tient des propos discriminatoires à 2:30"

<span style="color: #FF9800">**Quand**</span> je clique sur "Envoyer le signalement"

<span style="color: #4CAF50">**Alors**</span> le signalement est envoyé avec succès
<span style="color: #9E9E9E">**Et**</span> le commentaire est enregistré avec le signalement
<span style="color: #9E9E9E">**Et**</span> il sera visible par les modérateurs

---

## 8. Limite de 500 caractères pour le commentaire

<span style="color: #2196F3">**Étant donné**</span> que le formulaire de signalement est affiché

<span style="color: #FF9800">**Quand**</span> je saisis un commentaire de 501 caractères

<span style="color: #4CAF50">**Alors**</span> le champ limite automatiquement à 500 caractères
<span style="color: #9E9E9E">**Et**</span> le compteur affiche "500/500"
<span style="color: #9E9E9E">**Et**</span> les caractères supplémentaires ne sont pas acceptés

---

## 9. Toast de confirmation après signalement

<span style="color: #2196F3">**Étant donné**</span> que j'ai envoyé un signalement

<span style="color: #FF9800">**Quand**</span> le signalement est enregistré

<span style="color: #4CAF50">**Alors**</span> un toast notification s'affiche
<span style="color: #9E9E9E">**Et**</span> le toast contient le message "✓ Signalement envoyé. Nous l'examinerons sous 24-48h."
<span style="color: #9E9E9E">**Et**</span> le toast s'affiche pendant 5 secondes
<span style="color: #9E9E9E">**Et**</span> le toast contient un bouton "Voir mes signalements"
<span style="color: #9E9E9E">**Et**</span> je peux fermer le toast manuellement avec un bouton X

---

## 10. Accès à l'historique des signalements via le toast

<span style="color: #2196F3">**Étant donné**</span> que le toast de confirmation est affiché

<span style="color: #FF9800">**Quand**</span> je clique sur "Voir mes signalements"

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers la page "Mes signalements"
<span style="color: #9E9E9E">**Et**</span> je vois la liste de tous mes signalements
<span style="color: #9E9E9E">**Et**</span> le signalement que je viens d'envoyer apparaît en premier

---

## 11. Historique personnel des signalements

<span style="color: #2196F3">**Étant donné**</span> que j'ai envoyé 3 signalements précédemment

<span style="color: #FF9800">**Quand**</span> j'ouvre "Profil > Mes signalements"

<span style="color: #4CAF50">**Alors**</span> je vois la liste de mes 3 signalements
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> les signalements sont triés par date décroissante

---

## 12. 📋 Plan: Statuts possibles d'un signalement

<span style="color: #2196F3">**Étant donné**</span> que j'ai envoyé un signalement

<span style="color: #FF9800">**Quand**</span> le statut du signalement est "<statut>"

<span style="color: #4CAF50">**Alors**</span> le badge affiché est "<badge>"
<span style="color: #9E9E9E">**Et**</span> la couleur du badge est "<couleur>"

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai signalé un contenu il y a 24h

<span style="color: #FF9800">**Quand**</span> le modérateur traite mon signalement
<span style="color: #9E9E9E">**Et**</span> que le contenu est effectivement retiré

<span style="color: #4CAF50">**Alors**</span> je reçois une notification in-app
<span style="color: #9E9E9E">**Et**</span> la notification indique "Votre signalement a été traité. Le contenu a été retiré."
<span style="color: #9E9E9E">**Et**</span> le statut de mon signalement passe à "Traité ✓"
<span style="color: #9E9E9E">**Et**</span> je peux voir les détails de l'action prise

---

## 14. Notification si signalement rejeté

<span style="color: #2196F3">**Étant donné**</span> que j'ai signalé un contenu

<span style="color: #FF9800">**Quand**</span> le modérateur rejette mon signalement

<span style="color: #4CAF50">**Alors**</span> je reçois une notification in-app
<span style="color: #9E9E9E">**Et**</span> la notification indique "Votre signalement a été examiné. Le contenu ne viole pas les règles de la communauté."
<span style="color: #9E9E9E">**Et**</span> le statut de mon signalement passe à "Rejeté ✗"
<span style="color: #9E9E9E">**Et**</span> je peux voir la raison du rejet

---

## 15. Un contenu peut être signalé plusieurs fois

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a déjà été signalé par 5 autres utilisateurs

<span style="color: #FF9800">**Quand**</span> je signale le même contenu

<span style="color: #4CAF50">**Alors**</span> mon signalement est enregistré indépendamment
<span style="color: #9E9E9E">**Et**</span> le compteur de signalements du contenu passe à 6
<span style="color: #9E9E9E">**Et**</span> mon signalement rejoint la file d'attente de modération
<span style="color: #9E9E9E">**Et**</span> les signalements cumulés augmentent la priorité de traitement

---

## 16. Limite de signalements par utilisateur

<span style="color: #2196F3">**Étant donné**</span> que j'ai déjà signalé le même contenu il y a 2 jours

<span style="color: #FF9800">**Quand**</span> j'essaie de signaler à nouveau le même contenu

<span style="color: #4CAF50">**Alors**</span> un message m'informe "Vous avez déjà signalé ce contenu"
<span style="color: #9E9E9E">**Et**</span> le formulaire de signalement n'est pas affiché
<span style="color: #9E9E9E">**Et**</span> je peux consulter le statut de mon signalement précédent

---

## 17. Détection de signalements abusifs répétés

<span style="color: #2196F3">**Étant donné**</span> que j'ai envoyé 10 signalements ce mois-ci
<span style="color: #9E9E9E">**Et**</span> que 8 d'entre eux ont été rejetés comme infondés

<span style="color: #FF9800">**Quand**</span> j'essaie d'envoyer un nouveau signalement

<span style="color: #4CAF50">**Alors**</span> mon compte est marqué comme "signaleur suspect"
<span style="color: #9E9E9E">**Et**</span> un avertissement s'affiche:
<span style="color: #9E9E9E">**Et**</span> je peux toujours envoyer le signalement
<span style="color: #F44336">**Mais**</span> mes futurs signalements auront une priorité réduite

---

## 18. Sanction pour signalements abusifs graves

<span style="color: #2196F3">**Étant donné**</span> que j'ai envoyé 20 signalements abusifs en 1 mois
<span style="color: #9E9E9E">**Et**</span> que tous ont été rejetés comme volontairement faux

<span style="color: #FF9800">**Quand**</span> le modérateur détecte le pattern abusif

<span style="color: #4CAF50">**Alors**</span> mon compte reçoit un avertissement formel
<span style="color: #9E9E9E">**Et**</span> je perds la possibilité de signaler pendant 30 jours
<span style="color: #9E9E9E">**Et**</span> je reçois un email m'expliquant la sanction

---

## 19. Signalement depuis le player audio

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu

<span style="color: #FF9800">**Quand**</span> j'ouvre le menu "⋮" du player

<span style="color: #4CAF50">**Alors**</span> je vois l'option "Signaler"
<span style="color: #9E9E9E">**Et**</span> je peux ouvrir le formulaire de signalement

---

## 20. Signalement depuis la page de détails du contenu

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page de détails d'un contenu

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton "⋮" en haut à droite

<span style="color: #4CAF50">**Alors**</span> je vois l'option "Signaler"
<span style="color: #9E9E9E">**Et**</span> je peux ouvrir le formulaire de signalement

---

## 21. Signalement depuis l'historique d'écoute

<span style="color: #2196F3">**Étant donné**</span> que je consulte mon historique d'écoute

<span style="color: #FF9800">**Quand**</span> je clique sur "⋮" à côté d'un contenu passé

<span style="color: #4CAF50">**Alors**</span> je vois l'option "Signaler"
<span style="color: #9E9E9E">**Et**</span> je peux signaler ce contenu même si je ne l'écoute plus actuellement

---

## 22. Identité du signaleur anonyme pour le créateur

<span style="color: #2196F3">**Étant donné**</span> que j'ai signalé un contenu

<span style="color: #FF9800">**Quand**</span> le créateur est notifié de la modération

<span style="color: #4CAF50">**Alors**</span> mon identité reste anonyme
<span style="color: #9E9E9E">**Et**</span> le créateur ne peut pas savoir qui a signalé
<span style="color: #9E9E9E">**Et**</span> seuls les modérateurs ont accès à l'identité du signaleur

---

## 23. Coût du système de signalement

<span style="color: #2196F3">**Étant donné**</span> que le système de signalement est en place

<span style="color: #FF9800">**Quand**</span> on calcule le coût

<span style="color: #4CAF50">**Alors**</span> le coût est de 0€
<span style="color: #9E9E9E">**Et**</span> le formulaire est développé en interne
<span style="color: #9E9E9E">**Et**</span> aucun service tiers n'est utilisé
<span style="color: #9E9E9E">**Et**</span> les notifications in-app sont gratuites

---





<div style="page-break-after: always;"></div>


# Traitement des signalements par l'IA et les modérateurs
**25 scénarios** (21 standards, 4 plans)

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que le système de modération est actif
## 1. Signalement ajouté à la file d'attente asynchrone

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur envoie un signalement pour un contenu audio

<span style="color: #FF9800">**Quand**</span> le signalement est reçu

<span style="color: #4CAF50">**Alors**</span> le signalement est ajouté à la file d'attente asynchrone
<span style="color: #9E9E9E">**Et**</span> un worker de traitement est déclenché
<span style="color: #9E9E9E">**Et**</span> le traitement se fait en arrière-plan sans bloquer l'utilisateur

---

## 2. Transcription automatique avec Whisper large-v3

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu audio signalé dure 5 minutes

<span style="color: #FF9800">**Quand**</span> le worker de traitement démarre

<span style="color: #4CAF50">**Alors**</span> le système utilise Whisper large-v3 pour transcrire l'audio
<span style="color: #9E9E9E">**Et**</span> la transcription est en self-hosted (pas de service cloud)
<span style="color: #9E9E9E">**Et**</span> le texte transcrit est enregistré en base de données
<span style="color: #9E9E9E">**Et**</span> le délai de transcription est de 1-3 minutes

---

## 3. 📋 Plan: Délai de transcription selon durée audio

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu audio signalé dure <duree> minutes

<span style="color: #FF9800">**Quand**</span> le système transcrit l'audio

<span style="color: #4CAF50">**Alors**</span> la transcription prend environ <delai>

**📊 Exemples de données:**

| duree | delai |
|---|---|
| 2 | 1-3 minutes |
| 10 | 3-10 minutes |
| 45 | 10-20 minutes |

---

## 4. Analyse automatique du contenu transcrit

<span style="color: #2196F3">**Étant donné**</span> que la transcription audio est terminée

<span style="color: #FF9800">**Quand**</span> le système analyse le texte transcrit

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> chaque analyse génère un score de confiance (0-100%)

---

## 5. Génération du score de confiance IA

<span style="color: #2196F3">**Étant donné**</span> que toutes les analyses sont terminées

<span style="color: #FF9800">**Quand**</span> le système calcule le score final

<span style="color: #4CAF50">**Alors**</span> un score de confiance IA entre 0-100% est généré
<span style="color: #9E9E9E">**Et**</span> le score indique la probabilité que le contenu viole les règles
<span style="color: #9E9E9E">**Et**</span> la catégorie la plus probable est identifiée
<span style="color: #9E9E9E">**Et**</span> les timestamps des passages problématiques sont extraits

---

## 6. Détection automatique de contenu clairement inapproprié

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu contient des insultes graves et répétées

<span style="color: #FF9800">**Quand**</span> l'IA analyse la transcription

<span style="color: #4CAF50">**Alors**</span> le score de confiance IA est >95%
<span style="color: #9E9E9E">**Et**</span> la catégorie détectée est "Haine & violence"
<span style="color: #9E9E9E">**Et**</span> les passages problématiques sont identifiés avec timestamps:

    | timestamp | texte problématique |
    |---|---|
    | 02:15 | [insulte discriminatoire] |
    | 03:42 | [propos haineux] |

<span style="color: #9E9E9E">**Et**</span> le signalement est classé en priorité CRITIQUE

---

## 7. 📋 Plan: SLA selon priorité du signalement

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement a une priorité "<priorite>"

<span style="color: #FF9800">**Quand**</span> le signalement entre en file d'attente

<span style="color: #4CAF50">**Alors**</span> le délai de traitement cible est "<delai>"
<span style="color: #9E9E9E">**Et**</span> le responsable du traitement est "<responsable>"

**📊 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%

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement a un score IA de 97%
<span style="color: #9E9E9E">**Et**</span> que la catégorie détectée est "Spam" (évidente)

<span style="color: #FF9800">**Quand**</span> le système évalue le signalement

<span style="color: #4CAF50">**Alors**</span> une action automatique immédiate est déclenchée
<span style="color: #9E9E9E">**Et**</span> le contenu est retiré automatiquement
<span style="color: #9E9E9E">**Et**</span> le créateur est notifié de la modération
<span style="color: #9E9E9E">**Et**</span> le créateur peut faire appel de la décision
<span style="color: #9E9E9E">**Et**</span> un modérateur senior vérifie l'action a posteriori

---

## 9. Signalement CRITIQUE traité en moins de 2h

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement de priorité CRITIQUE est reçu à 14:00
<span style="color: #9E9E9E">**Et**</span> que le contenu concerne une menace de violence

<span style="color: #FF9800">**Quand**</span> le signalement est assigné à un modérateur senior d'astreinte

<span style="color: #4CAF50">**Alors**</span> le modérateur est alerté immédiatement (push + SMS)
<span style="color: #9E9E9E">**Et**</span> le signalement est traité avant 16:00 (2h)
<span style="color: #9E9E9E">**Et**</span> une décision est prise et appliquée
<span style="color: #9E9E9E">**Et**</span> les autorités peuvent être contactées si nécessaire

---

## 10. Astreinte modérateur 24/7 pour signalements CRITIQUES

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement CRITIQUE est reçu un dimanche à 03:00

<span style="color: #FF9800">**Quand**</span> le signalement est classé en priorité CRITIQUE

<span style="color: #4CAF50">**Alors**</span> le modérateur senior d'astreinte est alerté
<span style="color: #9E9E9E">**Et**</span> le signalement est traité dans les 2h (avant 05:00)
<span style="color: #9E9E9E">**Et**</span> le service d'astreinte garantit une disponibilité 24/7

---

## 11. Signalement HAUTE priorité traité en moins de 24h

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement de priorité HAUTE est reçu lundi à 10:00
<span style="color: #9E9E9E">**Et**</span> que le contenu concerne du harcèlement

<span style="color: #FF9800">**Quand**</span> le signalement entre en file d'attente

<span style="color: #4CAF50">**Alors**</span> le signalement est assigné à un modérateur (junior ou senior)
<span style="color: #9E9E9E">**Et**</span> le signalement est traité avant mardi 10:00 (24h jours ouvrés)
<span style="color: #9E9E9E">**Et**</span> une décision est prise et appliquée

---

## 12. Signalement BASSE priorité traité en moins de 72h

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement de priorité BASSE est reçu lundi à 10:00
<span style="color: #9E9E9E">**Et**</span> que le contenu concerne des tags incorrects

<span style="color: #FF9800">**Quand**</span> le signalement entre en file d'attente

<span style="color: #4CAF50">**Alors**</span> le signalement est traité avant jeudi 10:00 (72h jours ouvrés)
<span style="color: #9E9E9E">**Et**</span> un modérateur junior peut traiter ce type de signalement

---

## 13. Calcul du score de priorité

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement a les caractéristiques suivantes:

    | caractéristique | valeur |
    |---|---|
    | Score IA | 85% |
    | Signalements cumulés | 3 |
    | Fiabilité du signaleur | 75% |


<span style="color: #FF9800">**Quand**</span> le système calcule la priorité

<span style="color: #4CAF50">**Alors**</span> la formule appliquée est:
<span style="color: #9E9E9E">**Et**</span> le score de priorité est: (85 × 0.7) + (3 × 0.2) + (75 × 0.1) = 67.5
<span style="color: #9E9E9E">**Et**</span> le signalement est classé en priorité MOYENNE

---

## 14. 📋 Plan: Classification selon score de priorité

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement a un score de priorité de <score>

<span style="color: #FF9800">**Quand**</span> le système classe le signalement

<span style="color: #4CAF50">**Alors**</span> la priorité assignée est "<priorite>"
<span style="color: #9E9E9E">**Et**</span> le signalement entre dans la file "<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

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a été signalé par 1 utilisateur avec un score IA de 60%
<span style="color: #9E9E9E">**Et**</span> que le signalement est classé en priorité MOYENNE (score 42)

<span style="color: #FF9800">**Quand**</span> 5 autres utilisateurs signalent le même contenu

<span style="color: #4CAF50">**Alors**</span> le nombre de signalements cumulés passe à 6
<span style="color: #9E9E9E">**Et**</span> le score de priorité augmente significativement
<span style="color: #9E9E9E">**Et**</span> le signalement peut passer en priorité HAUTE
<span style="color: #9E9E9E">**Et**</span> le traitement est accéléré

---

## 16. Impact de la fiabilité du signaleur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur de confiance (90% fiabilité) envoie un signalement
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur suspect (20% fiabilité) envoie un signalement similaire

<span style="color: #FF9800">**Quand**</span> le système calcule les priorités

<span style="color: #4CAF50">**Alors**</span> le signalement de l'utilisateur de confiance a un score plus élevé
<span style="color: #9E9E9E">**Et**</span> son signalement est traité en priorité
<span style="color: #9E9E9E">**Et**</span> le signalement de l'utilisateur suspect est traité plus tard

---

## 17. Évolution du score de fiabilité du signaleur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a envoyé 10 signalements
<span style="color: #9E9E9E">**Et**</span> que 8 d'entre eux ont été acceptés par les modérateurs

<span style="color: #FF9800">**Quand**</span> le système calcule son score de fiabilité

<span style="color: #4CAF50">**Alors**</span> le score est de 80% (8 acceptés / 10 total)
<span style="color: #9E9E9E">**Et**</span> ses futurs signalements auront plus de poids
<span style="color: #9E9E9E">**Et**</span> il peut devenir "utilisateur de confiance"

---

## 18. Files d'attente séparées par priorité

<span style="color: #2196F3">**Étant donné**</span> que 50 signalements sont en attente

<span style="color: #FF9800">**Quand**</span> le système organise la file d'attente

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> les modérateurs traitent en priorité la file Immédiate

---

## 19. Modérateurs assignés selon compétences

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement complexe de harcèlement est reçu

<span style="color: #FF9800">**Quand**</span> le système assigne un modérateur

<span style="color: #4CAF50">**Alors**</span> un modérateur senior est prioritairement assigné
<span style="color: #9E9E9E">**Et**</span> les modérateurs juniors peuvent traiter les cas simples (spam, tags)
<span style="color: #9E9E9E">**Et**</span> les modérateurs seniors traitent les cas complexes (haine, violence, appels)

---

## 20. Stack technique 100% opensource

<span style="color: #2196F3">**Étant donné**</span> que le système de modération IA est déployé

<span style="color: #FF9800">**Quand**</span> on analyse les technologies utilisées

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> aucune dépendance à Google, AWS, Azure

---

## 21. 📋 Plan: Coût selon phase du projet

<span style="color: #2196F3">**Étant donné**</span> que RoadWave est en phase "<phase>"

<span style="color: #FF9800">**Quand**</span> on calcule le coût de l'infrastructure IA

<span style="color: #4CAF50">**Alors**</span> le coût mensuel est "<cout>"

**📊 Exemples de données:**

| phase | cout |
|---|---|
| MVP | 0-50€ (CPU) |
| Scale | 50-200€ (GPU VPS) |

---

## 22. Processing asynchrone en MVP avec CPU

<span style="color: #2196F3">**Étant donné**</span> que RoadWave est en phase MVP
<span style="color: #9E9E9E">**Et**</span> que le volume est <1000 signalements/mois

<span style="color: #FF9800">**Quand**</span> le système traite les signalements

<span style="color: #4CAF50">**Alors**</span> un serveur CPU standard est suffisant
<span style="color: #9E9E9E">**Et**</span> le coût est de 0€ (serveur existant)
<span style="color: #9E9E9E">**Et**</span> le processing asynchrone absorbe les pics de charge
<span style="color: #9E9E9E">**Et**</span> les délais restent acceptables (1-20 minutes)

---

## 23. Scaling avec GPU pour gros volumes

<span style="color: #2196F3">**Étant donné**</span> que RoadWave reçoit >1000 signalements/jour

<span style="color: #FF9800">**Quand**</span> le système nécessite un scaling

<span style="color: #4CAF50">**Alors**</span> un VPS avec GPU est requis
<span style="color: #9E9E9E">**Et**</span> le coût passe à 50-200€/mois
<span style="color: #9E9E9E">**Et**</span> les délais de transcription sont divisés par 5-10
<span style="color: #9E9E9E">**Et**</span> le système peut gérer 10 000+ signalements/mois

---

## 24. Logs d'audit pour chaque traitement

<span style="color: #2196F3">**Étant donné**</span> qu'un signalement est traité

<span style="color: #FF9800">**Quand**</span> une action est prise (rejet, acceptation, sanction)

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le log est conservé pour conformité DSA
<span style="color: #9E9E9E">**Et**</span> les logs sont anonymisés après 3 ans (RGPD)

---

## 25. Traçabilité complète pour conformité DSA

<span style="color: #2196F3">**Étant donné**</span> que le système de modération est actif

<span style="color: #FF9800">**Quand**</span> un audit DSA est effectué

<span style="color: #4CAF50">**Alors**</span> toutes les actions de modération sont tracées
<span style="color: #9E9E9E">**Et**</span> les délais de traitement sont mesurés et respectés
<span style="color: #9E9E9E">**Et**</span> les décisions sont justifiées et documentées
<span style="color: #9E9E9E">**Et**</span> la transparence vis-à-vis des utilisateurs est garantie
<span style="color: #9E9E9E">**Et**</span> le système est conforme au Digital Services Act

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant que créateur
## 1. Critère 1 - Ancienneté de 3 mois validée

<span style="color: #2196F3">**Étant donné**</span> que mon compte a été créé il y a 91 jours

<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "Ancienneté ≥ 3 mois" est validé ✅

---

## 2. Critère 1 - Ancienneté insuffisante

<span style="color: #2196F3">**Étant donné**</span> que mon compte a été créé il y a 60 jours

<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "Ancienneté ≥ 3 mois" n'est pas validé ❌
<span style="color: #9E9E9E">**Et**</span> je vois "Encore 30 jours avant d'être éligible"

---

## 3. Critère 2 - 500 abonnés atteints

<span style="color: #2196F3">**Étant donné**</span> que j'ai exactement 500 abonnés

<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "≥ 500 abonnés" est validé ✅

---

## 4. Critère 2 - Pas assez d'abonnés

<span style="color: #2196F3">**Étant donné**</span> que j'ai 347 abonnés

<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "≥ 500 abonnés" n'est pas validé ❌
<span style="color: #9E9E9E">**Et**</span> je vois "Encore 153 abonnés nécessaires"

---

## 5. Critère 3 - 10 000 écoutes complètes atteintes

<span style="color: #2196F3">**Étant donné**</span> que mes contenus ont cumulé 10 487 écoutes complètes

<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "≥ 10 000 écoutes complètes" est validé ✅

---

## 6. Critère 3 - Écoutes incomplètes non comptabilisées

<span style="color: #2196F3">**Étant donné**</span> que mes contenus ont:

    | type écoute | nombre |
    |---|---|
    | Écoutes complètes | 8 500 |
    | Écoutes <80% | 3 000 |


<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> seules les 8 500 écoutes complètes comptent
<span style="color: #9E9E9E">**Et**</span> je vois "Encore 1 500 écoutes complètes nécessaires"

---

## 7. Critère 4 - Aucun strike actif

<span style="color: #2196F3">**Étant donné**</span> que je n'ai aucun strike actif
<span style="color: #9E9E9E">**Et**</span> que je n'ai eu aucun contenu modéré dans les 6 derniers mois

<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "Fiabilité" est validé ✅

---

## 8. Critère 4 - Strike actif bloque l'éligibilité

<span style="color: #2196F3">**Étant donné**</span> que j'ai 1 strike actif pour contenu inapproprié

<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "Fiabilité" n'est pas validé ❌
<span style="color: #9E9E9E">**Et**</span> je vois "Vous devez résoudre votre strike avant d'être éligible"

---

## 9. Critère 4 - Contenu modéré dans les 6 derniers mois

<span style="color: #2196F3">**Étant donné**</span> que je n'ai pas de strike actif
<span style="color: #F44336">**Mais**</span> qu'un de mes contenus a été modéré il y a 4 mois

<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "Fiabilité" n'est pas validé ❌
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> le critère "≥ 5 contenus publiés dans les 90 derniers jours" est validé ✅

---

## 11. Critère 5 - Contenus trop anciens ne comptent pas

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> je consulte les critères de monétisation

<span style="color: #4CAF50">**Alors**</span> seuls 2 contenus comptent (dans les 90 jours)
<span style="color: #9E9E9E">**Et**</span> je vois "Encore 3 contenus à publier dans les 90 prochains jours"

---

## 12. Tous les critères validés - Bouton disponible

<span style="color: #2196F3">**Étant donné**</span> 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) | ✅ |


<span style="color: #FF9800">**Quand**</span> j'accède à mon profil créateur

<span style="color: #4CAF50">**Alors**</span> le bouton "Demander la monétisation" est actif
<span style="color: #9E9E9E">**Et**</span> je peux cliquer pour démarrer le KYC

---

## 13. Critères incomplets - Bouton grisé avec progression

<span style="color: #2196F3">**Étant donné**</span> 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% |


<span style="color: #FF9800">**Quand**</span> j'accède à mon profil créateur

<span style="color: #4CAF50">**Alors**</span> le bouton "Demander la monétisation" est grisé
<span style="color: #9E9E9E">**Et**</span> je vois la progression détaillée de chaque critère

---

## 14. Vérification automatique SQL lors de la demande

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Demander la monétisation"

<span style="color: #FF9800">**Quand**</span> le système vérifie mes critères

<span style="color: #4CAF50">**Alors**</span> une requête SQL est exécutée:
<span style="color: #9E9E9E">**Et**</span> si tous les critères sont TRUE, je suis redirigé vers le KYC

---

## 15. Notification par email quand critères atteints

<span style="color: #2196F3">**Étant donné**</span> que je viens d'atteindre 500 abonnés
<span style="color: #9E9E9E">**Et**</span> que c'était mon dernier critère manquant

<span style="color: #FF9800">**Quand**</span> le système détecte l'éligibilité

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 16. Badge "Éligible monétisation" dans profil

<span style="color: #2196F3">**Étant donné**</span> que je remplis tous les critères
<span style="color: #F44336">**Mais**</span> que je n'ai pas encore activé la monétisation

<span style="color: #FF9800">**Quand**</span> un utilisateur consulte mon profil

<span style="color: #4CAF50">**Alors**</span> il voit un badge "Éligible monétisation 💰"
<span style="color: #9E9E9E">**Et**</span> cela renforce ma crédibilité de créateur

---

## 17. Justification anti-fraude - Délai 3 mois

<span style="color: #2196F3">**Étant donné**</span> qu'un compte suspect crée du contenu frauduleux

<span style="color: #FF9800">**Quand**</span> le compte est détecté dans les 2 premiers mois

<span style="color: #4CAF50">**Alors**</span> le compte est banni avant d'atteindre les 3 mois
<span style="color: #9E9E9E">**Et**</span> le créateur n'a jamais été éligible à la monétisation
<span style="color: #9E9E9E">**Et**</span> aucun paiement n'a été effectué

---

## 18. Justification qualité - 10 000 écoutes

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur produit du contenu de mauvaise qualité

<span style="color: #FF9800">**Quand**</span> ses contenus ne génèrent que 2 000 écoutes complètes

<span style="color: #4CAF50">**Alors**</span> il ne peut pas activer la monétisation
<span style="color: #9E9E9E">**Et**</span> seuls les créateurs avec contenu apprécié sont monétisés

---

## 19. Réduction coût administratif plateforme

<span style="color: #2196F3">**Étant donné**</span> que RoadWave a 10 000 créateurs inscrits
<span style="color: #9E9E9E">**Et**</span> que seuls 500 remplissent tous les critères

<span style="color: #FF9800">**Quand**</span> le système calcule le coût administratif

<span style="color: #4CAF50">**Alors**</span> seulement 500 KYC sont à gérer (vs 10 000)
<span style="color: #9E9E9E">**Et**</span> seulement 500 virements mensuels (vs 10 000)
<span style="color: #9E9E9E">**Et**</span> la charge comptable est réduite de 95%

---

## 20. Statistiques publiques pour transparence

<span style="color: #FF9800">**Quand**</span> un utilisateur consulte la page "Devenir créateur"

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> cela permet de fixer des attentes réalistes

---

## 21. Cache Redis pour calcul rapide critères

<span style="color: #2196F3">**Étant donné**</span> que je consulte mes critères de monétisation

<span style="color: #FF9800">**Quand**</span> le système charge la page

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le temps de réponse est <50ms

---

## 22. Mise à jour temps réel des compteurs

<span style="color: #2196F3">**Étant donné**</span> que je viens de publier un nouveau contenu

<span style="color: #FF9800">**Quand**</span> un utilisateur écoute ce contenu en entier

<span style="color: #4CAF50">**Alors**</span> le compteur "complete_listens_total" est incrémenté immédiatement
<span style="color: #9E9E9E">**Et**</span> si je rafraîchis la page critères, je vois la nouvelle valeur
<span style="color: #9E9E9E">**Et**</span> cela encourage les créateurs à continuer de produire

---

## 23. Historique des tentatives d'activation

<span style="color: #2196F3">**Étant donné**</span> que j'ai tenté d'activer la monétisation il y a 2 mois
<span style="color: #F44336">**Mais**</span> que les critères n'étaient pas remplis

<span style="color: #FF9800">**Quand**</span> j'accède à mes logs d'activité

<span style="color: #4CAF50">**Alors**</span> je vois:

    | date | action | résultat | raison |
    |---|---|---|---|
    | 2025-11-15 | Demande monétisation | Refusée | Seulement 300 abonnés |

<span style="color: #9E9E9E">**Et**</span> cela m'aide à suivre ma progression

---

## 24. Performance avec 100 000 créateurs

<span style="color: #2196F3">**Étant donné**</span> que RoadWave a 100 000 créateurs
<span style="color: #9E9E9E">**Et**</span> que chacun consulte ses critères 1 fois par jour

<span style="color: #FF9800">**Quand**</span> le système traite ces requêtes

<span style="color: #4CAF50">**Alors**</span> la table users est indexée sur created_at
<span style="color: #9E9E9E">**Et**</span> la table subscriptions est indexée sur creator_id
<span style="color: #9E9E9E">**Et**</span> la table contents est indexée sur creator_id et published_at
<span style="color: #9E9E9E">**Et**</span> chaque requête reste <50ms grâce aux index

---

## 25. Export des critères pour support client

<span style="color: #2196F3">**Étant donné**</span> que je contacte le support car je pense être éligible

<span style="color: #FF9800">**Quand**</span> l'agent support consulte mon compte

<span style="color: #4CAF50">**Alors**</span> il voit un export JSON complet:
<span style="color: #9E9E9E">**Et**</span> l'agent peut expliquer précisément pourquoi je ne suis pas éligible

---

## 26. Notification 30 jours avant éligibilité probable

<span style="color: #2196F3">**Étant donné**</span> que mes critères sont:

    | critère | statut | progression |
    |---|---|---|
    | Ancienneté ≥ 3 mois | ❌ | 60/90 jours |
    | Tous les autres critères | ✅ | 100% |


<span style="color: #FF9800">**Quand**</span> il reste exactement 30 jours avant les 90 jours

<span style="color: #4CAF50">**Alors**</span> je reçois une notification:

---

## 27. Pas de bypass possible pour amis/influenceurs

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur influent me contacte directement
<span style="color: #9E9E9E">**Et**</span> qu'il demande un bypass des critères

<span style="color: #FF9800">**Quand**</span> je consulte la politique RoadWave

<span style="color: #4CAF50">**Alors**</span> la réponse est "Aucune exception possible, critères automatiques uniquement"
<span style="color: #9E9E9E">**Et**</span> cela garantit l'équité pour tous les créateurs

---

## 28. A/B test futur sur seuils (post-MVP)

<span style="color: #2196F3">**Étant donné**</span> que RoadWave veut tester des seuils différents

<span style="color: #FF9800">**Quand**</span> un A/B test est lancé en 2027

<span style="color: #4CAF50">**Alors**</span> groupe A voit: 500 abonnés, 10 000 écoutes
<span style="color: #9E9E9E">**Et**</span> groupe B voit: 300 abonnés, 5 000 écoutes
<span style="color: #9E9E9E">**Et**</span> les métriques (taux activation, fraude, qualité) sont comparées
<span style="color: #9E9E9E">**Et**</span> le meilleur seuil est déployé définitivement

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec la monétisation activée
## 1. Toggle "Réservé Premium" lors de la création

<span style="color: #2196F3">**Étant donné**</span> que je crée un nouveau contenu

<span style="color: #FF9800">**Quand**</span> j'accède aux options de publication

<span style="color: #4CAF50">**Alors**</span> je vois un toggle "Réservé aux abonnés Premium 👑"
<span style="color: #9E9E9E">**Et**</span> je peux l'activer ou le désactiver

---

## 2. Contenu marqué Premium lors de la création

<span style="color: #2196F3">**Étant donné**</span> que je crée un nouveau contenu

<span style="color: #FF9800">**Quand**</span> j'active le toggle "Réservé Premium"
<span style="color: #9E9E9E">**Et**</span> que je publie le contenu

<span style="color: #4CAF50">**Alors**</span> le champ `is_premium` en base est mis à `true`
<span style="color: #9E9E9E">**Et**</span> le contenu est visible uniquement pour les utilisateurs Premium

---

## 3. Contenu gratuit par défaut

<span style="color: #2196F3">**Étant donné**</span> que je crée un nouveau contenu

<span style="color: #FF9800">**Quand**</span> je ne touche pas au toggle "Réservé Premium"
<span style="color: #9E9E9E">**Et**</span> que je publie le contenu

<span style="color: #4CAF50">**Alors**</span> le champ `is_premium` en base est mis à `false` (défaut)
<span style="color: #9E9E9E">**Et**</span> le contenu est accessible à tous les utilisateurs

---

## 4. Modification d'un contenu existant en Premium

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié un contenu gratuit il y a 2 jours

<span style="color: #FF9800">**Quand**</span> je modifie le contenu et active le toggle "Réservé Premium"
<span style="color: #9E9E9E">**Et**</span> que j'enregistre les modifications

<span style="color: #4CAF50">**Alors**</span> le contenu devient immédiatement Premium
<span style="color: #9E9E9E">**Et**</span> les utilisateurs gratuits ne peuvent plus y accéder

---

## 5. Passage d'un contenu Premium en gratuit

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié un contenu Premium il y a 1 mois

<span style="color: #FF9800">**Quand**</span> je modifie le contenu et désactive le toggle "Réservé Premium"
<span style="color: #9E9E9E">**Et**</span> que j'enregistre les modifications

<span style="color: #4CAF50">**Alors**</span> le contenu devient immédiatement gratuit
<span style="color: #9E9E9E">**Et**</span> tous les utilisateurs peuvent maintenant y accéder

---

## 6. Aucune limite sur pourcentage de contenus Premium

<span style="color: #2196F3">**Étant donné**</span> que je publie 10 nouveaux contenus

<span style="color: #FF9800">**Quand**</span> je décide de rendre les 10 contenus Premium (100%)

<span style="color: #4CAF50">**Alors**</span> le système accepte sans limitation
<span style="color: #9E9E9E">**Et**</span> je peux avoir 100% de mon catalogue en Premium

---

## 7. Stratégie freemium - Mix gratuit/premium

<span style="color: #2196F3">**Étant donné**</span> que je publie 10 nouveaux contenus

<span style="color: #FF9800">**Quand**</span> je décide de rendre 5 contenus Premium et 5 gratuits (50/50)

<span style="color: #4CAF50">**Alors**</span> le système accepte cette stratégie
<span style="color: #9E9E9E">**Et**</span> je peux tester différents mix pour optimiser mes revenus

---

## 8. Stratégie tout gratuit possible

<span style="color: #2196F3">**Étant donné**</span> que je suis monétisé via publicités

<span style="color: #FF9800">**Quand**</span> je décide de ne mettre aucun contenu en Premium (0%)

<span style="color: #4CAF50">**Alors**</span> le système accepte cette stratégie
<span style="color: #9E9E9E">**Et**</span> je génère des revenus uniquement via les publicités

---

## 9. Badge 👑 visible sur l'interface utilisateur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur consulte ma liste de contenus

<span style="color: #FF9800">**Quand**</span> il voit un contenu Premium

<span style="color: #4CAF50">**Alors**</span> un badge 👑 "Premium" est affiché
<span style="color: #9E9E9E">**Et**</span> le contenu est clairement identifiable comme réservé

---

## 10. Utilisateur gratuit voit les contenus Premium dans la liste

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> je consulte les contenus d'un créateur

<span style="color: #4CAF50">**Alors**</span> je vois aussi les contenus Premium dans la liste
<span style="color: #9E9E9E">**Et**</span> ils sont affichés avec un badge 👑
<span style="color: #F44336">**Mais**</span> je ne peux pas les lire

---

## 11. Tentative de lecture Premium par utilisateur gratuit - Overlay bloquant

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> je clique sur un contenu Premium pour le lire

<span style="color: #4CAF50">**Alors**</span> un overlay bloquant apparaît
<span style="color: #9E9E9E">**Et**</span> je vois le message:
<span style="color: #9E9E9E">**Et**</span> un bouton "Passer Premium" est affiché

---

## 12. CTA "Passer Premium" redirige vers abonnement

<span style="color: #2196F3">**Étant donné**</span> que je vois l'overlay de contenu Premium bloqué

<span style="color: #FF9800">**Quand**</span> je clique sur "Passer Premium"

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers la page d'abonnement Premium
<span style="color: #9E9E9E">**Et**</span> je peux m'abonner pour 4.99€/mois

---

## 13. Utilisateur Premium peut lire tous les contenus Premium

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium actif

<span style="color: #FF9800">**Quand**</span> je clique sur un contenu Premium

<span style="color: #4CAF50">**Alors**</span> le contenu se lance immédiatement
<span style="color: #9E9E9E">**Et**</span> je n'ai aucun overlay bloquant
<span style="color: #9E9E9E">**Et**</span> je peux profiter pleinement du contenu exclusif

---

## 14. Contenus Premium inclus dans les recommandations

<span style="color: #2196F3">**Étant donné**</span> que l'algorithme génère ma file de 5 contenus

<span style="color: #FF9800">**Quand**</span> je suis un utilisateur gratuit

<span style="color: #4CAF50">**Alors**</span> les contenus Premium peuvent apparaître dans les recommandations
<span style="color: #9E9E9E">**Et**</span> cela me fait découvrir qu'il existe du contenu exclusif

---

## 15. Contenu Premium skippé automatiquement pour utilisateur gratuit

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit
<span style="color: #9E9E9E">**Et**</span> qu'un contenu Premium apparaît dans ma file de recommandation

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu précédent jusqu'à la fin

<span style="color: #4CAF50">**Alors**</span> le contenu Premium est automatiquement skippé
<span style="color: #9E9E9E">**Et**</span> le contenu suivant (gratuit) est lancé
<span style="color: #9E9E9E">**Et**</span> le slot Premium ne compte pas dans ma file de 5 contenus

---

## 16. Contenu Premium diffusé normalement pour utilisateur Premium

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium
<span style="color: #9E9E9E">**Et**</span> qu'un contenu Premium apparaît dans ma file de recommandation

<span style="color: #FF9800">**Quand**</span> j'écoute le contenu précédent jusqu'à la fin

<span style="color: #4CAF50">**Alors**</span> le contenu Premium est lancé normalement
<span style="color: #9E9E9E">**Et**</span> je profite du contenu exclusif sans interruption

---

## 17. Champ `is_premium` boolean en base PostgreSQL

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est créé

<span style="color: #FF9800">**Quand**</span> il est stocké en base de données

<span style="color: #4CAF50">**Alors**</span> la table `contents` contient un champ `is_premium BOOLEAN DEFAULT FALSE`
<span style="color: #9E9E9E">**Et**</span> ce champ est indexé pour requêtes rapides

---

## 18. Index PostgreSQL sur `is_premium`

<span style="color: #2196F3">**Étant donné**</span> que l'algorithme doit filtrer les contenus selon le statut Premium

<span style="color: #FF9800">**Quand**</span> une requête SQL est exécutée:

<span style="color: #4CAF50">**Alors**</span> l'index sur `is_premium` accélère la requête
<span style="color: #9E9E9E">**Et**</span> le temps de réponse reste <20ms

---

## 19. Cache Redis pour statut Premium

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu Premium est consulté fréquemment

<span style="color: #FF9800">**Quand**</span> l'API vérifie le statut Premium

<span style="color: #4CAF50">**Alors**</span> la valeur est récupérée depuis Redis:
<span style="color: #9E9E9E">**Et**</span> le cache a un TTL de 1 heure
<span style="color: #9E9E9E">**Et**</span> cela évite des requêtes SQL inutiles

---

## 20. Invalidation cache lors de modification statut Premium

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est passé de gratuit à Premium

<span style="color: #FF9800">**Quand**</span> le créateur enregistre la modification

<span style="color: #4CAF50">**Alors**</span> le cache Redis `content:[id]:premium` est invalidé immédiatement
<span style="color: #9E9E9E">**Et**</span> la nouvelle valeur est mise à jour
<span style="color: #9E9E9E">**Et**</span> les utilisateurs voient le changement en temps réel

---

## 21. Justification liberté créateur - Stratégie personnalisée

<span style="color: #2196F3">**Étant donné**</span> que chaque créateur a une audience différente

<span style="color: #FF9800">**Quand**</span> un créateur décide de sa stratégie Premium

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur gratuit voit beaucoup de contenus Premium

<span style="color: #FF9800">**Quand**</span> il consulte les profils de ses créateurs préférés

<span style="color: #4CAF50">**Alors**</span> il voit que 60% de leur contenu est réservé Premium
<span style="color: #9E9E9E">**Et**</span> cela l'incite à s'abonner pour 4.99€/mois
<span style="color: #9E9E9E">**Et**</span> RoadWave augmente son taux de conversion vers Premium

---

## 23. Justification équité - Petit créateur peut tout mettre en Premium

<span style="color: #2196F3">**Étant donné**</span> que je suis un petit créateur avec 600 abonnés
<span style="color: #9E9E9E">**Et**</span> que 50 sont abonnés Premium

<span style="color: #FF9800">**Quand**</span> je mets 100% de mon contenu en Premium

<span style="color: #4CAF50">**Alors**</span> je génère des revenus uniquement via mes 50 abonnés Premium
<span style="color: #9E9E9E">**Et**</span> cela me permet de vivre de mon contenu malgré une petite audience

---

## 24. Justification équité - Gros créateur peut tout offrir gratuitement

<span style="color: #2196F3">**Étant donné**</span> que je suis un gros créateur avec 50 000 abonnés
<span style="color: #9E9E9E">**Et**</span> que je génère déjà beaucoup de revenus publicitaires

<span style="color: #FF9800">**Quand**</span> je laisse 100% de mon contenu gratuit

<span style="color: #4CAF50">**Alors**</span> je maximise mon audience et mes revenus pub
<span style="color: #9E9E9E">**Et**</span> je n'ai pas besoin de mettre du contenu en Premium

---

## 25. Statistiques créateur - Ratio Premium/Gratuit

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mon tableau de bord créateur

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques de contenus

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai des contenus gratuits et Premium

<span style="color: #FF9800">**Quand**</span> je consulte mes revenus détaillés

<span style="color: #4CAF50">**Alors**</span> je vois:

    | source | montant |
    |---|---|
    | Revenus pub (gratuit) | 86.70€ |
    | Revenus Premium (exclusifs) | 34.20€ |
    | Revenus Premium (tout contenu) | 78.90€ |

<span style="color: #9E9E9E">**Et**</span> je peux comparer l'efficacité de chaque stratégie

---

## 27. Notification créateur - Contenu Premium très écouté

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié un contenu Premium il y a 3 jours
<span style="color: #9E9E9E">**Et**</span> qu'il a généré 5 000 écoutes Premium (très élevé)

<span style="color: #FF9800">**Quand**</span> le système détecte cette performance

<span style="color: #4CAF50">**Alors**</span> je reçois une notification:

---

## 28. A/B test utilisateur - Impact badge Premium sur conversion

<span style="color: #2196F3">**Étant donné**</span> que RoadWave veut optimiser le taux de conversion Premium

<span style="color: #FF9800">**Quand**</span> un A/B test est lancé

<span style="color: #4CAF50">**Alors**</span> groupe A voit le badge 👑 "Premium"
<span style="color: #9E9E9E">**Et**</span> groupe B voit le badge 💎 "Exclusif"
<span style="color: #9E9E9E">**Et**</span> les taux de clic et conversion sont mesurés
<span style="color: #9E9E9E">**Et**</span> le badge le plus performant est déployé définitivement

---

## 29. Analytics plateforme - Adoption fonctionnalité Premium

<span style="color: #2196F3">**Étant donné**</span> que RoadWave suit l'adoption de la fonctionnalité

<span style="color: #FF9800">**Quand**</span> un admin consulte les métriques

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium envisage de résilier
<span style="color: #F44336">**Mais**</span> qu'il a accès à 150 contenus Premium de ses créateurs préférés

<span style="color: #FF9800">**Quand**</span> il voit la valeur exclusive qu'il perdrait

<span style="color: #4CAF50">**Alors**</span> il est moins susceptible de résilier (churn réduit de ~30%)
<span style="color: #9E9E9E">**Et**</span> les contenus Premium augmentent la rétention

---

## 31. Transparence - Créateur voit combien de contenus Premium il a

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mon profil créateur

<span style="color: #FF9800">**Quand**</span> je consulte mes contenus

<span style="color: #4CAF50">**Alors**</span> je peux filtrer par statut:

    | filtre | résultats |
    |---|---|
    | Tous | 47 |
    | Gratuits | 32 |
    | Premium 👑 | 15 |

<span style="color: #9E9E9E">**Et**</span> je peux facilement gérer mon catalogue

---

## 32. Export liste contenus avec statut Premium (RGPD)

<span style="color: #2196F3">**Étant donné**</span> que je demande l'export de mes données

<span style="color: #FF9800">**Quand**</span> l'export est généré

<span style="color: #4CAF50">**Alors**</span> la liste de mes contenus inclut le statut Premium:

---

## 33. Suppression compte créateur et contenus Premium

<span style="color: #2196F3">**Étant donné**</span> que je supprime définitivement mon compte créateur

<span style="color: #FF9800">**Quand**</span> la suppression est confirmée

<span style="color: #4CAF50">**Alors**</span> tous mes contenus (gratuits et Premium) sont supprimés
<span style="color: #9E9E9E">**Et**</span> les utilisateurs Premium ne peuvent plus y accéder
<span style="color: #9E9E9E">**Et**</span> les fichiers audio sont supprimés du CDN sous 7 jours

---

## 34. Performance avec 1 million de contenus Premium

<span style="color: #2196F3">**Étant donné**</span> que RoadWave a 1 million de contenus dont 300 000 Premium

<span style="color: #FF9800">**Quand**</span> l'algorithme génère une recommandation

<span style="color: #4CAF50">**Alors**</span> la requête SQL filtre efficacement avec l'index `is_premium`
<span style="color: #9E9E9E">**Et**</span> le temps de réponse reste <50ms
<span style="color: #9E9E9E">**Et**</span> la scalabilité est garantie

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec la monétisation activée
## 1. Désactivation temporaire par le créateur

<span style="color: #2196F3">**Étant donné**</span> que je veux faire une pause dans ma création de contenu

<span style="color: #FF9800">**Quand**</span> j'accède à "Paramètres > Monétisation"
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Désactiver temporairement la monétisation"

<span style="color: #4CAF50">**Alors**</span> ma monétisation est désactivée immédiatement
<span style="color: #9E9E9E">**Et**</span> je ne génère plus de revenus à partir de maintenant

---

## 2. Confirmation avant désactivation

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Désactiver temporairement"

<span style="color: #FF9800">**Quand**</span> une popup de confirmation apparaît

<span style="color: #4CAF50">**Alors**</span> je vois le message:
<span style="color: #9E9E9E">**Et**</span> je dois confirmer pour continuer

---

## 3. Solde conservé pendant désactivation

<span style="color: #2196F3">**Étant donné**</span> que mon solde actuel est 87.45€

<span style="color: #FF9800">**Quand**</span> je désactive ma monétisation le 15 du mois

<span style="color: #4CAF50">**Alors**</span> mon solde de 87.45€ est conservé
<span style="color: #9E9E9E">**Et**</span> il sera reporté au mois suivant
<span style="color: #9E9E9E">**Et**</span> si le total dépasse 50€, il sera versé normalement le 15 du mois prochain

---

## 4. Contenus restent accessibles pendant désactivation

<span style="color: #2196F3">**Étant donné**</span> que j'ai désactivé ma monétisation

<span style="color: #FF9800">**Quand**</span> des utilisateurs écoutent mes contenus

<span style="color: #4CAF50">**Alors**</span> mes contenus restent accessibles normalement
<span style="color: #F44336">**Mais**</span> je ne génère aucun revenu (ni pub ni Premium)

---

## 5. Réactivation sans refaire le KYC si <2 ans

<span style="color: #2196F3">**Étant donné**</span> que j'ai désactivé ma monétisation il y a 8 mois
<span style="color: #9E9E9E">**Et**</span> que mes documents KYC sont toujours valides

<span style="color: #FF9800">**Quand**</span> je clique sur "Réactiver la monétisation"

<span style="color: #4CAF50">**Alors**</span> la réactivation est immédiate
<span style="color: #9E9E9E">**Et**</span> je n'ai pas besoin de refaire le KYC
<span style="color: #9E9E9E">**Et**</span> je recommence à générer des revenus dès maintenant

---

## 6. Nouveau KYC requis si inactivité >2 ans

<span style="color: #2196F3">**Étant donné**</span> que j'ai désactivé ma monétisation il y a 25 mois

<span style="color: #FF9800">**Quand**</span> j'essaie de réactiver

<span style="color: #4CAF50">**Alors**</span> le système demande un nouveau KYC
<span style="color: #9E9E9E">**Et**</span> je vois:
<span style="color: #9E9E9E">**Et**</span> je dois soumettre à nouveau mes documents

---

## 7. Historique des désactivations/réactivations

<span style="color: #2196F3">**Étant donné**</span> que j'ai désactivé et réactivé ma monétisation plusieurs fois

<span style="color: #FF9800">**Quand**</span> j'accède à "Paramètres > Monétisation > Historique"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je reçois un 3ème strike pour violation des règles

<span style="color: #FF9800">**Quand**</span> le strike devient actif

<span style="color: #4CAF50">**Alors**</span> ma monétisation est suspendue automatiquement
<span style="color: #9E9E9E">**Et**</span> je vois:

---

## 9. Réactivation après résolution des strikes

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation est suspendue pour 3 strikes

<span style="color: #FF9800">**Quand**</span> je résous tous mes strikes (après expiration ou contestation)
<span style="color: #9E9E9E">**Et**</span> que mon compteur de strikes passe à 0

<span style="color: #4CAF50">**Alors**</span> ma monétisation est réactivée automatiquement
<span style="color: #9E9E9E">**Et**</span> je reçois un email de confirmation

---

## 10. Suspension si RIB invalide après 3 échecs de virement

<span style="color: #2196F3">**Étant donné**</span> que 3 tentatives de virement ont échoué (15, 18, 22 du mois)

<span style="color: #FF9800">**Quand**</span> le 3ème échec est confirmé

<span style="color: #4CAF50">**Alors**</span> ma monétisation est suspendue automatiquement
<span style="color: #9E9E9E">**Et**</span> je vois:

---

## 11. Réactivation après mise à jour RIB valide

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation est suspendue pour RIB invalide

<span style="color: #FF9800">**Quand**</span> je mets à jour mon RIB avec un compte bancaire valide
<span style="color: #9E9E9E">**Et**</span> que Mangopay valide le nouveau RIB

<span style="color: #4CAF50">**Alors**</span> ma monétisation est réactivée automatiquement
<span style="color: #9E9E9E">**Et**</span> un virement est tenté immédiatement pour le solde en attente

---

## 12. Suspension si documents KYC expirés

<span style="color: #2196F3">**Étant donné**</span> que ma carte d'identité expire dans 30 jours

<span style="color: #FF9800">**Quand**</span> je reçois un email de rappel de mise à jour
<span style="color: #F44336">**Mais**</span> que je ne mets pas à jour mes documents
<span style="color: #9E9E9E">**Et**</span> que ma CNI expire

<span style="color: #4CAF50">**Alors**</span> ma monétisation est suspendue automatiquement après 30 jours de grâce

---

## 13. Préavis 30 jours avant suspension pour docs expirés

<span style="color: #2196F3">**Étant donné**</span> que ma CNI expire le 15 juin 2025

<span style="color: #FF9800">**Quand**</span> le 15 mai 2025 arrive (30 jours avant)

<span style="color: #4CAF50">**Alors**</span> je reçois un email d'alerte:

---

## 14. Réactivation après renouvellement documents KYC

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation est suspendue pour CNI expirée

<span style="color: #FF9800">**Quand**</span> je soumets une nouvelle CNI valide
<span style="color: #9E9E9E">**Et**</span> que Mangopay valide le document sous 24-72h

<span style="color: #4CAF50">**Alors**</span> ma monétisation est réactivée automatiquement
<span style="color: #9E9E9E">**Et**</span> je recommence à générer des revenus

---

## 15. Suspension si fraude détectée

<span style="color: #2196F3">**Étant donné**</span> que le système détecte une activité frauduleuse (bots, écoutes artificielles)

<span style="color: #FF9800">**Quand**</span> l'équipe modération confirme la fraude

<span style="color: #4CAF50">**Alors**</span> ma monétisation est suspendue immédiatement
<span style="color: #9E9E9E">**Et**</span> mon compte est mis sous enquête
<span style="color: #9E9E9E">**Et**</span> je reçois un email m'informant de la suspension

---

## 16. Enquête fraude - Vérification manuelle

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation est suspendue pour suspicion de fraude

<span style="color: #FF9800">**Quand**</span> l'équipe modération enquête

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que mon compte était suspendu pour suspicion de fraude

<span style="color: #FF9800">**Quand**</span> l'enquête conclut qu'il n'y a pas eu de fraude

<span style="color: #4CAF50">**Alors**</span> ma monétisation est réactivée
<span style="color: #9E9E9E">**Et**</span> les revenus suspendus pendant l'enquête sont versés normalement
<span style="color: #9E9E9E">**Et**</span> je reçois un email d'excuses avec explication

---

## 18. Suspension définitive si fraude confirmée

<span style="color: #2196F3">**Étant donné**</span> que l'enquête confirme une fraude avérée

<span style="color: #FF9800">**Quand**</span> l'équipe modération prend la décision

<span style="color: #4CAF50">**Alors**</span> ma monétisation est définitivement désactivée
<span style="color: #9E9E9E">**Et**</span> mon solde en attente est gelé (non versé)
<span style="color: #9E9E9E">**Et**</span> je peux recevoir un strike 4 (ban définitif du compte)

---

## 19. Suppression définitive sur demande créateur

<span style="color: #2196F3">**Étant donné**</span> que je veux arrêter définitivement la monétisation

<span style="color: #FF9800">**Quand**</span> j'accède à "Paramètres > Monétisation > Supprimer définitivement"

<span style="color: #4CAF50">**Alors**</span> une confirmation stricte est demandée
<span style="color: #9E9E9E">**Et**</span> je dois taper "SUPPRIMER" pour confirmer

---

## 20. Solde versé sous 30 jours après suppression

<span style="color: #2196F3">**Étant donné**</span> que je supprime définitivement ma monétisation
<span style="color: #9E9E9E">**Et**</span> que mon solde en attente est 127.45€

<span style="color: #FF9800">**Quand**</span> la suppression est confirmée

<span style="color: #4CAF50">**Alors**</span> mon solde sera versé sous 30 jours
<span style="color: #9E9E9E">**Et**</span> je reçois un dernier virement de clôture
<span style="color: #9E9E9E">**Et**</span> mon e-wallet Mangopay est clôturé

---

## 21. Suppression auto si inactivité 24 mois + solde <50€

<span style="color: #2196F3">**Étant donné**</span> que je n'ai plus publié de contenu depuis 24 mois
<span style="color: #9E9E9E">**Et**</span> que mon solde en attente est 12.30€ (<50€)

<span style="color: #FF9800">**Quand**</span> le processus de purge RGPD s'exécute

<span style="color: #4CAF50">**Alors**</span> ma monétisation est automatiquement supprimée
<span style="color: #9E9E9E">**Et**</span> mon solde de 12.30€ est perdu (trop faible pour virement)
<span style="color: #9E9E9E">**Et**</span> mes données KYC sont archivées puis supprimées selon la législation

---

## 22. Email de préavis 60 jours avant purge RGPD

<span style="color: #2196F3">**Étant donné**</span> que je suis inactif depuis 22 mois

<span style="color: #FF9800">**Quand**</span> le système détecte l'inactivité

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 23. Ban définitif compte - Strike 4

<span style="color: #2196F3">**Étant donné**</span> que je reçois un 4ème strike (violation grave ou répétée)

<span style="color: #FF9800">**Quand**</span> l'équipe modération applique le strike 4

<span style="color: #4CAF50">**Alors**</span> mon compte est banni définitivement
<span style="color: #9E9E9E">**Et**</span> ma monétisation est supprimée définitivement
<span style="color: #9E9E9E">**Et**</span> mon solde en attente est gelé (non versé)
<span style="color: #9E9E9E">**Et**</span> je ne peux plus créer de nouveau compte (blacklist email/SIRET)

---

## 24. Email pour toute suspension

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation est suspendue (quelle qu'en soit la raison)

<span style="color: #FF9800">**Quand**</span> la suspension devient effective

<span style="color: #4CAF50">**Alors**</span> je reçois immédiatement un email:

---

## 25. Notification in-app avec raison explicite

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation est suspendue

<span style="color: #FF9800">**Quand**</span> je me connecte à l'application

<span style="color: #4CAF50">**Alors**</span> je vois une bannière en haut de mon dashboard:

---

## 26. Email de confirmation lors de réactivation

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation était suspendue

<span style="color: #FF9800">**Quand**</span> elle est réactivée (automatiquement ou manuellement)

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 27. Dashboard admin - Suspensions actives

<span style="color: #2196F3">**Étant donné**</span> qu'un admin RoadWave consulte les suspensions

<span style="color: #FF9800">**Quand**</span> il accède au dashboard admin "Monétisation > Suspensions"

<span style="color: #4CAF50">**Alors**</span> 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%

<span style="color: #2196F3">**Étant donné**</span> que le taux de suspension dépasse 5%

<span style="color: #FF9800">**Quand**</span> le système détecte cette anomalie

<span style="color: #4CAF50">**Alors**</span> une alerte est envoyée à l'équipe:

---

## 29. Statistiques personnelles - Temps actif monétisation

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mon dashboard créateur

<span style="color: #FF9800">**Quand**</span> je consulte "Statistiques > Monétisation"

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que je demande l'export de mes données

<span style="color: #FF9800">**Quand**</span> l'export est généré

<span style="color: #4CAF50">**Alors**</span> l'historique des suspensions est inclus:

---

## 31. Suppression compte et données monétisation

<span style="color: #2196F3">**Étant donné**</span> que je supprime définitivement mon compte RoadWave

<span style="color: #FF9800">**Quand**</span> la suppression est confirmée

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je supprime mon compte

<span style="color: #FF9800">**Quand**</span> mes données sont archivées

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> après 10 ans, tout est supprimé définitivement

---

## 33. Suspension temporaire pour maintenance technique

<span style="color: #2196F3">**Étant donné**</span> que Mangopay effectue une maintenance planifiée

<span style="color: #FF9800">**Quand**</span> la maintenance est programmée

<span style="color: #4CAF50">**Alors**</span> tous les créateurs reçoivent un email préventif 7 jours avant:

---

## 34. Réactivation progressive après incident majeur

<span style="color: #2196F3">**Étant donné**</span> qu'un incident technique majeur suspend toutes les monétisations

<span style="color: #FF9800">**Quand**</span> l'incident est résolu

<span style="color: #4CAF50">**Alors**</span> 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% |

<span style="color: #9E9E9E">**Et**</span> cela évite une surcharge système lors de la reprise

---

## 35. Support prioritaire pour créateurs suspendus injustement

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation est suspendue
<span style="color: #9E9E9E">**Et**</span> que je pense que c'est une erreur

<span style="color: #FF9800">**Quand**</span> je contacte le support avec tag "Suspension monétisation"

<span style="color: #4CAF50">**Alors**</span> mon ticket est traité en priorité (SLA 24h)
<span style="color: #9E9E9E">**Et**</span> un agent expert examine mon cas
<span style="color: #9E9E9E">**Et**</span> si suspension injustifiée, je suis réactivé immédiatement avec excuses

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je remplis tous les critères de monétisation
> <span style="color: #9E9E9E">**Et**</span> que j'ai cliqué sur "Demander la monétisation"
## 1. Redirection vers formulaire KYC Mangopay

<span style="color: #FF9800">**Quand**</span> je démarre le processus d'activation

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers un formulaire KYC
<span style="color: #9E9E9E">**Et**</span> le formulaire est fourni par Mangopay (iframe sécurisée)
<span style="color: #9E9E9E">**Et**</span> toutes les données sont chiffrées et hébergées en EU

---

## 2. Statut auto-entrepreneur accepté

<span style="color: #2196F3">**Étant donné**</span> que je suis auto-entrepreneur

<span style="color: #FF9800">**Quand**</span> je renseigne mon statut juridique

<span style="color: #4CAF50">**Alors**</span> l'option "Auto-entrepreneur (micro-BNC)" est disponible
<span style="color: #9E9E9E">**Et**</span> je peux continuer le processus

---

## 3. Statut société SARL/SAS/SASU accepté

<span style="color: #2196F3">**Étant donné**</span> que j'ai créé une société

<span style="color: #FF9800">**Quand**</span> je renseigne mon statut juridique

<span style="color: #4CAF50">**Alors**</span> les options suivantes sont disponibles:

    | statut juridique |
    |---|
    | SARL |
    | SAS |
    | SASU |

<span style="color: #9E9E9E">**Et**</span> je peux continuer le processus

---

## 4. Statut particulier refusé

<span style="color: #2196F3">**Étant donné**</span> que je n'ai pas de statut professionnel

<span style="color: #FF9800">**Quand**</span> j'essaie de m'inscrire en tant que "Particulier"

<span style="color: #4CAF50">**Alors**</span> le formulaire affiche:
<span style="color: #9E9E9E">**Et**</span> je ne peux pas continuer sans statut professionnel

---

## 5. Document SIRET obligatoire et validé

<span style="color: #2196F3">**Étant donné**</span> que je renseigne mon SIRET

<span style="color: #FF9800">**Quand**</span> je saisis "12345678901234" (14 chiffres)

<span style="color: #4CAF50">**Alors**</span> le format est validé
<span style="color: #9E9E9E">**Et**</span> Mangopay vérifie l'existence du SIRET auprès du répertoire SIRENE
<span style="color: #9E9E9E">**Et**</span> si valide, le document est accepté

---

## 6. SIRET invalide ou inexistant

<span style="color: #2196F3">**Étant donné**</span> que je renseigne un SIRET inexistant

<span style="color: #FF9800">**Quand**</span> je saisis "99999999999999"

<span style="color: #4CAF50">**Alors**</span> Mangopay rejette le SIRET
<span style="color: #9E9E9E">**Et**</span> je vois "SIRET non trouvé dans le répertoire SIRENE. Vérifiez le numéro."
<span style="color: #9E9E9E">**Et**</span> je dois corriger avant de continuer

---

## 7. RIB professionnel obligatoire

<span style="color: #2196F3">**Étant donné**</span> que j'upload mon RIB

<span style="color: #FF9800">**Quand**</span> le RIB est scanné par Mangopay

<span style="color: #4CAF50">**Alors**</span> le système vérifie que le titulaire correspond à mon SIRET
<span style="color: #9E9E9E">**Et**</span> que l'IBAN commence par "FR" (compte français)
<span style="color: #9E9E9E">**Et**</span> si valide, le document est accepté

---

## 8. RIB particulier refusé

<span style="color: #2196F3">**Étant donné**</span> que j'upload un RIB de compte particulier

<span style="color: #FF9800">**Quand**</span> Mangopay détecte que le compte n'est pas professionnel

<span style="color: #4CAF50">**Alors**</span> le RIB est rejeté
<span style="color: #9E9E9E">**Et**</span> je vois:

---

## 9. Pièce d'identité CNI en cours de validité

<span style="color: #2196F3">**Étant donné**</span> que j'upload ma carte nationale d'identité

<span style="color: #FF9800">**Quand**</span> Mangopay analyse le document

<span style="color: #4CAF50">**Alors**</span> la date d'expiration est vérifiée
<span style="color: #9E9E9E">**Et**</span> si la CNI est valide, le document est accepté
<span style="color: #9E9E9E">**Et**</span> mon identité est vérifiée par OCR + vérification manuelle

---

## 10. Pièce d'identité expirée refusée

<span style="color: #2196F3">**Étant donné**</span> que j'upload une CNI expirée depuis 2 ans

<span style="color: #FF9800">**Quand**</span> Mangopay analyse le document

<span style="color: #4CAF50">**Alors**</span> le document est rejeté
<span style="color: #9E9E9E">**Et**</span> je vois "Pièce d'identité expirée. Veuillez fournir un document en cours de validité."

---

## 11. Passeport accepté comme alternative

<span style="color: #2196F3">**Étant donné**</span> que je n'ai pas de CNI

<span style="color: #FF9800">**Quand**</span> j'upload mon passeport en cours de validité

<span style="color: #4CAF50">**Alors**</span> Mangopay accepte le passeport
<span style="color: #9E9E9E">**Et**</span> mon identité est vérifiée de la même manière

---

## 12. Numéro TVA intracommunautaire si applicable

<span style="color: #2196F3">**Étant donné**</span> que mon CA dépasse 37 000€/an
<span style="color: #9E9E9E">**Et**</span> que je suis sorti de la franchise en base

<span style="color: #FF9800">**Quand**</span> je renseigne mon numéro TVA intracommunautaire

<span style="color: #4CAF50">**Alors**</span> le format "FR + 11 chiffres" est validé
<span style="color: #9E9E9E">**Et**</span> Mangopay vérifie l'existence auprès de la Commission Européenne (VIES)

---

## 13. TVA non applicable pour micro-BNC sous franchise

<span style="color: #2196F3">**Étant donné**</span> que je suis auto-entrepreneur sous franchise en base
<span style="color: #9E9E9E">**Et**</span> que mon CA est <37 000€/an

<span style="color: #FF9800">**Quand**</span> je remplis le formulaire KYC

<span style="color: #4CAF50">**Alors**</span> le champ "Numéro TVA" est optionnel
<span style="color: #9E9E9E">**Et**</span> je peux continuer sans TVA

---

## 14. Kbis <3 mois pour sociétés

<span style="color: #2196F3">**Étant donné**</span> que je suis gérant d'une SARL

<span style="color: #FF9800">**Quand**</span> j'upload mon extrait Kbis

<span style="color: #4CAF50">**Alors**</span> Mangopay vérifie que le Kbis date de moins de 3 mois
<span style="color: #9E9E9E">**Et**</span> que le SIRET correspond
<span style="color: #9E9E9E">**Et**</span> si valide, le document est accepté

---

## 15. Kbis trop ancien refusé

<span style="color: #2196F3">**Étant donné**</span> que j'upload un Kbis de 5 mois

<span style="color: #FF9800">**Quand**</span> Mangopay analyse le document

<span style="color: #4CAF50">**Alors**</span> le Kbis est rejeté
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que mon compte RoadWave est au nom de "Jean Dupont"
<span style="color: #F44336">**Mais**</span> que ma CNI est au nom de "Pierre Martin"

<span style="color: #FF9800">**Quand**</span> Mangopay compare les identités

<span style="color: #4CAF50">**Alors**</span> le KYC est rejeté
<span style="color: #9E9E9E">**Et**</span> je vois:

---

## 17. Liste noire anti-blanchiment détectée

<span style="color: #2196F3">**Étant donné**</span> que mon identité apparaît sur une liste anti-blanchiment

<span style="color: #FF9800">**Quand**</span> Mangopay effectue la vérification AML (Anti-Money Laundering)

<span style="color: #4CAF50">**Alors**</span> le KYC est automatiquement rejeté
<span style="color: #9E9E9E">**Et**</span> je vois "Votre demande ne peut être acceptée pour des raisons de conformité légale"
<span style="color: #9E9E9E">**Et**</span> mon compte créateur peut être suspendu

---

## 18. Délai de vérification 24-72h si documents conformes

<span style="color: #2196F3">**Étant donné**</span> que j'ai soumis tous les documents valides

<span style="color: #FF9800">**Quand**</span> Mangopay traite ma demande

<span style="color: #4CAF50">**Alors**</span> je reçois un email "KYC en cours de vérification (24-72h)"
<span style="color: #9E9E9E">**Et**</span> mon statut est "En attente de validation"
<span style="color: #9E9E9E">**Et**</span> je peux continuer à publier des contenus en attendant

---

## 19. Validation KYC réussie

<span style="color: #2196F3">**Étant donné**</span> que mes documents sont conformes

<span style="color: #FF9800">**Quand**</span> Mangopay valide mon KYC après 48h

<span style="color: #4CAF50">**Alors**</span> je reçois un email "Monétisation activée !"
<span style="color: #9E9E9E">**Et**</span> mon statut passe à "Monétisé"
<span style="color: #9E9E9E">**Et**</span> je commence à générer des revenus dès maintenant

---

## 20. Rejet KYC pour documents invalides

<span style="color: #2196F3">**Étant donné**</span> que j'ai soumis une CNI floue et illisible

<span style="color: #FF9800">**Quand**</span> Mangopay analyse les documents

<span style="color: #4CAF50">**Alors**</span> le KYC est rejeté après 24h
<span style="color: #9E9E9E">**Et**</span> je reçois un email détaillant les documents à refournir:

---

## 21. E-wallet Mangopay créé automatiquement

<span style="color: #2196F3">**Étant donné**</span> que mon KYC est validé

<span style="color: #FF9800">**Quand**</span> Mangopay finalise mon inscription

<span style="color: #4CAF50">**Alors**</span> un e-wallet Mangopay est créé automatiquement à mon nom
<span style="color: #9E9E9E">**Et**</span> tous mes futurs revenus seront transférés vers ce wallet
<span style="color: #9E9E9E">**Et**</span> les virements SEPA vers mon RIB seront effectués depuis ce wallet

---

## 22. Conformité RGPD - Données hébergées EU

<span style="color: #2196F3">**Étant donné**</span> que je fournis mes documents KYC

<span style="color: #FF9800">**Quand**</span> Mangopay stocke mes données

<span style="color: #4CAF50">**Alors**</span> toutes les données sont hébergées en Union Européenne
<span style="color: #9E9E9E">**Et**</span> Mangopay est régulé par l'ACPR (Autorité de Contrôle Prudentiel)
<span style="color: #9E9E9E">**Et**</span> mes données sont protégées selon le RGPD

---

## 23. KYC gratuit inclus dans Mangopay

<span style="color: #2196F3">**Étant donné**</span> que je complète le KYC

<span style="color: #FF9800">**Quand**</span> le processus se termine

<span style="color: #4CAF50">**Alors**</span> aucun frais ne m'est facturé (0€)
<span style="color: #9E9E9E">**Et**</span> aucun frais n'est facturé à RoadWave (inclus dans l'offre Mangopay)

---

## 24. Base légale - Conformité fiscale française

<span style="color: #2196F3">**Étant donné**</span> que RoadWave est une plateforme française

<span style="color: #FF9800">**Quand**</span> je génère des revenus >1200€/an

<span style="color: #4CAF50">**Alors**</span> RoadWave doit déclarer ces revenus aux impôts (DAS2)
<span style="color: #9E9E9E">**Et**</span> le KYC permet de garantir l'identité réelle du bénéficiaire
<span style="color: #9E9E9E">**Et**</span> cela respecte la réglementation fiscale française

---

## 25. Base légale - Directive anti-blanchiment EU 2018/843

<span style="color: #2196F3">**Étant donné**</span> que RoadWave verse de l'argent aux créateurs

<span style="color: #FF9800">**Quand**</span> le KYC est effectué

<span style="color: #4CAF50">**Alors**</span> RoadWave respecte la 5ème directive anti-blanchiment EU
<span style="color: #9E9E9E">**Et**</span> Mangopay effectue les vérifications requises (identité, liste noire, origine fonds)

---

## 26. Notification de mise à jour documents expirés

<span style="color: #2196F3">**Étant donné**</span> que ma CNI va expirer dans 30 jours

<span style="color: #FF9800">**Quand**</span> le système détecte l'expiration proche

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 27. Suspension monétisation si documents expirés

<span style="color: #2196F3">**Étant donné**</span> que ma CNI est expirée depuis 10 jours
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas mis à jour mes documents

<span style="color: #FF9800">**Quand**</span> le système vérifie mon statut KYC

<span style="color: #4CAF50">**Alors**</span> ma monétisation est suspendue automatiquement
<span style="color: #9E9E9E">**Et**</span> je ne génère plus de revenus jusqu'à mise à jour

---

## 28. Réactivation sans nouveau KYC si données à jour

<span style="color: #2196F3">**Étant donné**</span> que j'ai désactivé temporairement ma monétisation il y a 6 mois
<span style="color: #9E9E9E">**Et**</span> que mes documents KYC sont toujours valides

<span style="color: #FF9800">**Quand**</span> je réactive la monétisation

<span style="color: #4CAF50">**Alors**</span> je n'ai pas besoin de refaire le KYC
<span style="color: #9E9E9E">**Et**</span> la réactivation est immédiate

---

## 29. Nouveau KYC requis après 2 ans d'inactivité

<span style="color: #2196F3">**Étant donné**</span> que j'ai désactivé ma monétisation il y a 25 mois

<span style="color: #FF9800">**Quand**</span> j'essaie de réactiver

<span style="color: #4CAF50">**Alors**</span> le système demande un nouveau KYC
<span style="color: #9E9E9E">**Et**</span> je dois soumettre des documents à jour (CNI peut avoir changé)

---

## 30. Support créateur pour problèmes KYC

<span style="color: #2196F3">**Étant donné**</span> que mon KYC est rejeté et je ne comprends pas pourquoi

<span style="color: #FF9800">**Quand**</span> je contacte le support RoadWave

<span style="color: #4CAF50">**Alors**</span> un agent peut consulter les raisons du rejet Mangopay
<span style="color: #9E9E9E">**Et**</span> m'aider à fournir les bons documents

---

## 31. Export données KYC pour RGPD

<span style="color: #2196F3">**Étant donné**</span> que je demande l'export de mes données personnelles

<span style="color: #FF9800">**Quand**</span> l'export est généré

<span style="color: #4CAF50">**Alors**</span> les informations KYC sont incluses:
<span style="color: #9E9E9E">**Et**</span> les documents scannés (CNI, RIB) sont exclus pour sécurité

---

## 32. Suppression compte et données KYC

<span style="color: #2196F3">**Étant donné**</span> que je supprime définitivement mon compte RoadWave

<span style="color: #FF9800">**Quand**</span> la suppression est confirmée

<span style="color: #4CAF50">**Alors**</span> mes données KYC chez Mangopay sont archivées 10 ans (obligation légale)
<span style="color: #F44336">**Mais**</span> supprimées de la base RoadWave immédiatement
<span style="color: #9E9E9E">**Et**</span> mon e-wallet est clôturé après versement du solde final

---

## 33. Statistiques KYC pour monitoring plateforme

<span style="color: #2196F3">**Étant donné**</span> que RoadWave suit la qualité du processus KYC

<span style="color: #FF9800">**Quand**</span> un admin consulte les métriques

<span style="color: #4CAF50">**Alors**</span> 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% |

<span style="color: #9E9E9E">**Et**</span> cela permet d'optimiser le processus

---

## 34. Vérification SIRET via API INSEE

<span style="color: #2196F3">**Étant donné**</span> que je saisis mon SIRET

<span style="color: #FF9800">**Quand**</span> le système le valide

<span style="color: #4CAF50">**Alors**</span> une requête est faite à l'API SIRENE de l'INSEE
<span style="color: #9E9E9E">**Et**</span> le système vérifie que le SIRET existe et est actif
<span style="color: #9E9E9E">**Et**</span> récupère le nom de l'entreprise pour pré-remplir le formulaire

---

## 35. Détection fraude - Même SIRET utilisé par plusieurs comptes

<span style="color: #2196F3">**Étant donné**</span> qu'un SIRET "12345678901234" est déjà utilisé par un autre créateur

<span style="color: #FF9800">**Quand**</span> j'essaie d'utiliser le même SIRET

<span style="color: #4CAF50">**Alors**</span> le système détecte la duplication
<span style="color: #9E9E9E">**Et**</span> affiche "Ce SIRET est déjà utilisé par un autre compte RoadWave"
<span style="color: #9E9E9E">**Et**</span> je dois contacter le support si c'est une erreur

---

## 36. Protection données sensibles - Logs chiffrés

<span style="color: #2196F3">**Étant donné**</span> que des données KYC sensibles transitent dans le système

<span style="color: #FF9800">**Quand**</span> les logs sont enregistrés

<span style="color: #4CAF50">**Alors**</span> les numéros SIRET, IBAN et données CNI sont masqués:
<span style="color: #9E9E9E">**Et**</span> seule l'équipe sécurité peut accéder aux données complètes

---

## 37. Backup Mangopay des documents KYC

<span style="color: #2196F3">**Étant donné**</span> que mes documents KYC sont stockés chez Mangopay

<span style="color: #FF9800">**Quand**</span> un audit est demandé par les autorités

<span style="color: #4CAF50">**Alors**</span> Mangopay peut fournir les documents originaux
<span style="color: #9E9E9E">**Et**</span> RoadWave n'a pas besoin de stocker ces documents (réduction risque RGPD)

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec la monétisation activée
> <span style="color: #9E9E9E">**Et**</span> que je génère des revenus sur RoadWave
## 1. Génération automatique relevé mensuel PDF

<span style="color: #2196F3">**Étant donné**</span> que le mois de janvier se termine

<span style="color: #FF9800">**Quand**</span> le système calcule mes revenus du mois

<span style="color: #4CAF50">**Alors**</span> un relevé mensuel PDF est généré automatiquement
<span style="color: #9E9E9E">**Et**</span> le PDF est disponible dans mon tableau de bord

---

## 2. Contenu du relevé mensuel PDF

<span style="color: #2196F3">**Étant donné**</span> que mon relevé de janvier est généré

<span style="color: #FF9800">**Quand**</span> je télécharge le PDF

<span style="color: #4CAF50">**Alors**</span> le document contient:

---

## 3. Téléchargement relevé depuis tableau de bord

<span style="color: #2196F3">**Étant donné**</span> que je suis sur mon tableau de bord créateur

<span style="color: #FF9800">**Quand**</span> j'accède à l'onglet "Revenus > Historique"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai commencé la monétisation en janvier 2025

<span style="color: #FF9800">**Quand**</span> je consulte mes relevés en janvier 2035 (10 ans plus tard)

<span style="color: #4CAF50">**Alors**</span> tous les relevés depuis 2025 sont toujours accessibles
<span style="color: #9E9E9E">**Et**</span> je peux télécharger n'importe quel relevé historique
<span style="color: #9E9E9E">**Et**</span> cela respecte l'obligation de conservation comptable de 10 ans

---

## 5. Export CSV à la demande

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Exporter pour comptable"

<span style="color: #FF9800">**Quand**</span> je choisis la période "Année 2025"

<span style="color: #4CAF50">**Alors**</span> un fichier CSV est généré et téléchargé

---

## 6. Contenu export CSV détaillé

<span style="color: #2196F3">**Étant donné**</span> que j'exporte mes données comptables 2025

<span style="color: #FF9800">**Quand**</span> je télécharge le fichier CSV

<span style="color: #4CAF50">**Alors**</span> le fichier contient:

---

## 7. Transmission à l'expert-comptable

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé mon export CSV 2025

<span style="color: #FF9800">**Quand**</span> je l'envoie à mon expert-comptable

<span style="color: #4CAF50">**Alors**</span> il peut importer le fichier dans son logiciel comptable
<span style="color: #9E9E9E">**Et**</span> il saisit rapidement mes revenus RoadWave
<span style="color: #9E9E9E">**Et**</span> cela facilite ma déclaration fiscale annuelle

---

## 8. DAS2 généré automatiquement si revenus >1200€/an

<span style="color: #2196F3">**Étant donné**</span> que mes revenus 2025 totalisent 2,450€

<span style="color: #FF9800">**Quand**</span> l'année 2025 se termine

<span style="color: #4CAF50">**Alors**</span> RoadWave génère automatiquement une DAS2 pour les impôts
<span style="color: #9E9E9E">**Et**</span> la DAS2 est transmise à la DGFIP en janvier 2026

---

## 9. Contenu de la DAS2

<span style="color: #2196F3">**Étant donné**</span> que RoadWave génère ma DAS2 pour 2025

<span style="color: #FF9800">**Quand**</span> la DGFIP reçoit la déclaration

<span style="color: #4CAF50">**Alors**</span> le document contient:

---

## 10. Créateur reçoit une copie de la DAS2

<span style="color: #2196F3">**Étant donné**</span> que RoadWave transmet ma DAS2 aux impôts

<span style="color: #FF9800">**Quand**</span> la transmission est confirmée

<span style="color: #4CAF50">**Alors**</span> je reçois un email avec une copie de la DAS2 en pièce jointe
<span style="color: #9E9E9E">**Et**</span> je peux consulter le document dans mon tableau de bord

---

## 11. Pas de DAS2 si revenus <1200€/an

<span style="color: #2196F3">**Étant donné**</span> que mes revenus 2025 totalisent seulement 890€

<span style="color: #FF9800">**Quand**</span> l'année 2025 se termine

<span style="color: #4CAF50">**Alors**</span> aucune DAS2 n'est générée car le seuil de 1200€ n'est pas atteint
<span style="color: #F44336">**Mais**</span> je dois quand même déclarer mes revenus dans ma déclaration personnelle

---

## 12. Base légale DAS2 - Obligation France

<span style="color: #2196F3">**Étant donné**</span> que RoadWave verse des honoraires à des prestataires

<span style="color: #FF9800">**Quand**</span> les revenus dépassent 1200€/an

<span style="color: #4CAF50">**Alors**</span> la déclaration DAS2 est obligatoire selon l'article 87 du Code Général des Impôts
<span style="color: #9E9E9E">**Et**</span> le non-respect entraîne une amende de 15€ par bénéficiaire non déclaré

---

## 13. Transmission DAS2 via EDI-TDFC

<span style="color: #2196F3">**Étant donné**</span> que RoadWave génère 1,247 DAS2 pour l'année 2025

<span style="color: #FF9800">**Quand**</span> la transmission aux impôts est effectuée

<span style="color: #4CAF50">**Alors**</span> la transmission se fait via le portail EDI-TDFC de la DGFIP
<span style="color: #9E9E9E">**Et**</span> la transmission est automatisée (pas de saisie manuelle)
<span style="color: #9E9E9E">**Et**</span> un accusé de réception est reçu sous 48h

---

## 14. Créateur responsable de déclarer aux impôts

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu 2,450€ de revenus RoadWave en 2025

<span style="color: #FF9800">**Quand**</span> je fais ma déclaration fiscale en mai 2026

<span style="color: #4CAF50">**Alors**</span> je dois déclarer ces 2,450€ dans ma déclaration annuelle
<span style="color: #9E9E9E">**Et**</span> si je suis auto-entrepreneur, je déclare en BNC (Bénéfices Non Commerciaux)

---

## 15. Créateur responsable des cotisations URSSAF

<span style="color: #2196F3">**Étant donné**</span> que je suis auto-entrepreneur
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu 2,450€ de revenus RoadWave en 2025

<span style="color: #FF9800">**Quand**</span> je fais ma déclaration URSSAF trimestrielle

<span style="color: #4CAF50">**Alors**</span> je dois déclarer ces revenus à l'URSSAF
<span style="color: #9E9E9E">**Et**</span> je paie ~22% de cotisations sociales (soit ~539€)

---

## 16. TVA non applicable en franchise en base

<span style="color: #2196F3">**Étant donné**</span> que je suis auto-entrepreneur en micro-BNC
<span style="color: #9E9E9E">**Et**</span> que mon chiffre d'affaires est <37,800€/an

<span style="color: #FF9800">**Quand**</span> je génère des revenus sur RoadWave

<span style="color: #4CAF50">**Alors**</span> je bénéficie de la franchise en base de TVA
<span style="color: #9E9E9E">**Et**</span> je ne facture pas de TVA à RoadWave
<span style="color: #9E9E9E">**Et**</span> je ne récupère pas la TVA sur mes achats

---

## 17. TVA applicable si CA >37,800€/an

<span style="color: #2196F3">**Étant donné**</span> que mon chiffre d'affaires total 2025 est 45,000€

<span style="color: #FF9800">**Quand**</span> je dépasse le seuil de franchise en base (37,800€)

<span style="color: #4CAF50">**Alors**</span> je dois facturer de la TVA (20%) à RoadWave
<span style="color: #9E9E9E">**Et**</span> je dois obtenir un numéro TVA intracommunautaire
<span style="color: #9E9E9E">**Et**</span> je dois déclarer ma TVA mensuellement ou trimestriellement

---

## 18. Conservation justificatifs 10 ans - Obligation légale

<span style="color: #2196F3">**Étant donné**</span> que je génère des revenus sur RoadWave

<span style="color: #FF9800">**Quand**</span> je télécharge mes relevés mensuels et exports CSV

<span style="color: #4CAF50">**Alors**</span> je dois les conserver 10 ans (obligation comptable France)
<span style="color: #9E9E9E">**Et**</span> en cas de contrôle fiscal, je dois pouvoir les fournir

---

## 19. Mangopay transmet automatiquement via DAC7

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur monétisé sur RoadWave

<span style="color: #FF9800">**Quand**</span> l'année se termine

<span style="color: #4CAF50">**Alors**</span> Mangopay transmet automatiquement mes revenus aux autorités fiscales EU
<span style="color: #9E9E9E">**Et**</span> cela respecte la directive DAC7 (2021/514) sur la transparence fiscale des plateformes

---

## 20. Directive DAC7 - Obligations plateforme

<span style="color: #2196F3">**Étant donné**</span> que RoadWave est une plateforme facilitant des transactions

<span style="color: #FF9800">**Quand**</span> Mangopay gère les paiements

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> RoadWave n'a pas besoin de faire cette transmission manuellement

---

## 21. Justificatif virement = Preuve bancaire comptable

<span style="color: #2196F3">**Étant donné**</span> que je reçois un virement de 150.00€ de Mangopay

<span style="color: #FF9800">**Quand**</span> je consulte mon relevé bancaire

<span style="color: #4CAF50">**Alors**</span> je vois le virement avec la référence MANGOPAY-ABC123
<span style="color: #9E9E9E">**Et**</span> ce relevé bancaire sert de justificatif comptable
<span style="color: #9E9E9E">**Et**</span> je peux le fournir à mon expert-comptable ou aux impôts

---

## 22. Notification annuelle rappel déclaration fiscale

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur monétisé

<span style="color: #FF9800">**Quand**</span> le mois d'avril 2026 arrive (période déclaration impôts France)

<span style="color: #4CAF50">**Alors**</span> je reçois un email de rappel:

---

## 23. Page ressources fiscales pour créateurs

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur monétisé

<span style="color: #FF9800">**Quand**</span> j'accède à "Aide > Fiscalité"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je consulte mon dashboard en décembre 2025

<span style="color: #FF9800">**Quand**</span> j'accède à "Revenus > Récapitulatif annuel"

<span style="color: #4CAF50">**Alors**</span> je vois:

---

## 25. Génération automatique minimise erreurs

<span style="color: #2196F3">**Étant donné**</span> que tous les documents fiscaux sont générés automatiquement

<span style="color: #FF9800">**Quand**</span> un créateur télécharge ses documents

<span style="color: #4CAF50">**Alors**</span> les montants sont garantis corrects (issus de la base de données)
<span style="color: #9E9E9E">**Et**</span> il n'y a pas d'erreur de saisie manuelle
<span style="color: #9E9E9E">**Et**</span> cela réduit les risques de contrôle fiscal

---

## 26. Conformité RGPD - Données fiscales chiffrées

<span style="color: #2196F3">**Étant donné**</span> que les documents fiscaux contiennent des données sensibles (SIRET, revenus)

<span style="color: #FF9800">**Quand**</span> les documents sont stockés

<span style="color: #4CAF50">**Alors**</span> ils sont chiffrés au repos (encryption AES-256)
<span style="color: #9E9E9E">**Et**</span> seul le créateur et les admins autorisés peuvent y accéder
<span style="color: #9E9E9E">**Et**</span> les logs d'accès sont conservés pour audit

---

## 27. Backup documents fiscaux 10 ans

<span style="color: #2196F3">**Étant donné**</span> qu'un document fiscal est généré

<span style="color: #FF9800">**Quand**</span> il est stocké dans la base de données

<span style="color: #4CAF50">**Alors**</span> une copie est sauvegardée sur S3 (stockage durable)
<span style="color: #9E9E9E">**Et**</span> les backups sont répliqués sur 3 zones de disponibilité
<span style="color: #9E9E9E">**Et**</span> la conservation est garantie 10 ans minimum

---

## 28. Audit trail génération DAS2

<span style="color: #2196F3">**Étant donné**</span> que 1,247 DAS2 sont générées en janvier 2026

<span style="color: #FF9800">**Quand**</span> un audit est demandé

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un admin RoadWave consulte les métriques fiscales

<span style="color: #FF9800">**Quand**</span> il accède au dashboard admin

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai une question sur ma déclaration fiscale

<span style="color: #FF9800">**Quand**</span> je contacte le support RoadWave

<span style="color: #4CAF50">**Alors**</span> l'agent peut consulter mes documents fiscaux
<span style="color: #9E9E9E">**Et**</span> m'aider à comprendre ce que je dois déclarer
<span style="color: #F44336">**Mais**</span> il ne peut pas me conseiller fiscalement (pas expert-comptable)
<span style="color: #9E9E9E">**Et**</span> il me recommande de consulter un expert-comptable si nécessaire

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec la monétisation activée
> <span style="color: #9E9E9E">**Et**</span> que mon KYC est validé
## 1. Seuil minimum de 50€ atteint - Paiement effectué

<span style="color: #2196F3">**Étant donné**</span> que mes revenus du mois sont 73.45€

<span style="color: #FF9800">**Quand**</span> le dernier jour du mois arrive

<span style="color: #4CAF50">**Alors**</span> mon solde de 73.45€ est transféré vers "en attente de paiement"
<span style="color: #9E9E9E">**Et**</span> le paiement sera effectué le 15 du mois prochain

---

## 2. Seuil minimum de 50€ non atteint - Report mois suivant

<span style="color: #2196F3">**Étant donné**</span> que mes revenus du mois sont 32.17€

<span style="color: #FF9800">**Quand**</span> le dernier jour du mois arrive

<span style="color: #4CAF50">**Alors**</span> mon solde de 32.17€ est reporté au mois suivant
<span style="color: #9E9E9E">**Et**</span> je vois "Solde insuffisant pour paiement (<50€). Report mois prochain."

---

## 3. Cumul sur plusieurs mois jusqu'à atteindre 50€

<span style="color: #2196F3">**Étant donné**</span> que mes revenus sont:

    | mois | revenus | solde cumulé |
    |---|---|---|
    | Janvier | 18.50€ | 18.50€ |
    | Février | 22.30€ | 40.80€ |
    | Mars | 15.70€ | 56.50€ |


<span style="color: #FF9800">**Quand**</span> la fin du mois de mars arrive

<span style="color: #4CAF50">**Alors**</span> le solde cumulé de 56.50€ dépasse les 50€
<span style="color: #9E9E9E">**Et**</span> un paiement de 56.50€ est effectué le 15 avril

---

## 4. Calcul des revenus le dernier jour du mois

<span style="color: #2196F3">**Étant donné**</span> que nous sommes le 31 janvier à 23h59

<span style="color: #FF9800">**Quand**</span> le système calcule les revenus du mois

<span style="color: #4CAF50">**Alors**</span> une requête SQL agrège tous les revenus pub et premium
<span style="color: #9E9E9E">**Et**</span> le solde final du mois est figé dans monthly_revenues
<span style="color: #9E9E9E">**Et**</span> le compteur du mois en cours repart à 0€ le 1er février

---

## 5. Période de traitement contestations 1-14 du mois

<span style="color: #2196F3">**Étant donné**</span> que mes revenus de janvier sont calculés à 150.00€

<span style="color: #FF9800">**Quand**</span> la période du 1-14 février arrive

<span style="color: #4CAF50">**Alors**</span> RoadWave analyse les éventuelles fraudes ou contestations
<span style="color: #9E9E9E">**Et**</span> si une fraude est détectée, les revenus concernés sont retirés du solde
<span style="color: #9E9E9E">**Et**</span> le solde final est validé le 14 février

---

## 6. Virement SEPA le 15 du mois suivant

<span style="color: #2196F3">**Étant donné**</span> que mes revenus de janvier validés sont 150.00€

<span style="color: #FF9800">**Quand**</span> le 15 février arrive

<span style="color: #4CAF50">**Alors**</span> Mangopay initie un virement SEPA depuis mon e-wallet vers mon RIB
<span style="color: #9E9E9E">**Et**</span> le statut du paiement passe à "En cours"

---

## 7. Réception virement 16-18 du mois (1-3 jours SEPA)

<span style="color: #2196F3">**Étant donné**</span> qu'un virement SEPA a été initié le 15 février

<span style="color: #FF9800">**Quand**</span> 1-3 jours ouvrés s'écoulent

<span style="color: #4CAF50">**Alors**</span> je reçois le virement sur mon compte bancaire entre le 16 et 18 février
<span style="color: #9E9E9E">**Et**</span> je peux consulter l'historique des paiements dans mon dashboard

---

## 8. Virement SEPA gratuit pour comptes EU

<span style="color: #2196F3">**Étant donné**</span> que mon RIB est français (IBAN FR)

<span style="color: #FF9800">**Quand**</span> Mangopay effectue le virement

<span style="color: #4CAF50">**Alors**</span> aucun frais n'est prélevé (virement SEPA gratuit)
<span style="color: #9E9E9E">**Et**</span> je reçois 100% du montant annoncé

---

## 9. Virement international hors EU avec frais variables

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur expatrié avec RIB hors Union Européenne

<span style="color: #FF9800">**Quand**</span> Mangopay effectue le virement international

<span style="color: #4CAF50">**Alors**</span> des frais variables s'appliquent selon le pays
<span style="color: #9E9E9E">**Et**</span> les frais sont déduits du montant final
<span style="color: #9E9E9E">**Et**</span> je vois le détail des frais dans mon historique

---

## 10. E-wallet Mangopay automatique

<span style="color: #2196F3">**Étant donné**</span> que mon KYC est validé

<span style="color: #FF9800">**Quand**</span> mes revenus sont calculés

<span style="color: #4CAF50">**Alors**</span> les revenus sont automatiquement transférés vers mon e-wallet Mangopay
<span style="color: #9E9E9E">**Et**</span> l'e-wallet est débité lors du virement SEPA vers mon RIB
<span style="color: #9E9E9E">**Et**</span> je n'ai aucune action manuelle à faire

---

## 11. Tableau de bord - Revenus pub temps réel

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mon tableau de bord créateur

<span style="color: #FF9800">**Quand**</span> je consulte l'onglet "Revenus"

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ces valeurs sont mises à jour en temps réel (cache Redis, refresh 10 min)

---

## 12. Tableau de bord - Solde en attente de paiement

<span style="color: #2196F3">**Étant donné**</span> que mes revenus de janvier sont calculés et validés
<span style="color: #9E9E9E">**Et**</span> que nous sommes le 10 février

<span style="color: #FF9800">**Quand**</span> je consulte mon tableau de bord

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis monétisé depuis 6 mois

<span style="color: #FF9800">**Quand**</span> je consulte l'historique des paiements

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Télécharger export comptable"

<span style="color: #FF9800">**Quand**</span> le fichier CSV est généré

<span style="color: #4CAF50">**Alors**</span> je télécharge un fichier contenant:
<span style="color: #9E9E9E">**Et**</span> je peux transmettre ce fichier à mon expert-comptable

---

## 15. Échec virement - Tentative 1 échouée

<span style="color: #2196F3">**Étant donné**</span> qu'un virement est initié le 15 février
<span style="color: #F44336">**Mais**</span> que mon RIB est invalide ou le compte est fermé

<span style="color: #FF9800">**Quand**</span> Mangopay détecte l'échec

<span style="color: #4CAF50">**Alors**</span> le statut passe à "Échec - Retry programmé le 18 février"
<span style="color: #9E9E9E">**Et**</span> je reçois un email m'alertant du problème

---

## 16. Échec virement - Retry automatique J+3

<span style="color: #2196F3">**Étant donné**</span> que le virement du 15 février a échoué

<span style="color: #FF9800">**Quand**</span> le 18 février arrive (J+3)

<span style="color: #4CAF50">**Alors**</span> Mangopay tente automatiquement un nouveau virement
<span style="color: #9E9E9E">**Et**</span> si le RIB est toujours invalide, le virement échoue à nouveau

---

## 17. Échec virement - Retry automatique J+7

<span style="color: #2196F3">**Étant donné**</span> que les 2 premières tentatives ont échoué

<span style="color: #FF9800">**Quand**</span> le 22 février arrive (J+7)

<span style="color: #4CAF50">**Alors**</span> Mangopay tente une 3ème et dernière fois
<span style="color: #9E9E9E">**Et**</span> si le virement échoue encore, la monétisation est suspendue

---

## 18. Échec virement - Suspension monétisation après 3 échecs

<span style="color: #2196F3">**Étant donné**</span> que les 3 tentatives de virement ont échoué

<span style="color: #FF9800">**Quand**</span> le système détecte le 3ème échec

<span style="color: #4CAF50">**Alors**</span> ma monétisation est suspendue automatiquement
<span style="color: #9E9E9E">**Et**</span> je reçois un email:

---

## 19. Mise à jour RIB et réactivation paiement

<span style="color: #2196F3">**Étant donné**</span> que ma monétisation est suspendue pour RIB invalide
<span style="color: #9E9E9E">**Et**</span> que mon solde en attente est 150.00€

<span style="color: #FF9800">**Quand**</span> je mets à jour mon RIB avec un compte valide

<span style="color: #4CAF50">**Alors**</span> Mangopay tente immédiatement un nouveau virement
<span style="color: #9E9E9E">**Et**</span> si le virement réussit, ma monétisation est réactivée automatiquement

---

## 20. Notification email lors de chaque paiement

<span style="color: #2196F3">**Étant donné**</span> qu'un virement de 150.00€ est effectué le 15 février

<span style="color: #FF9800">**Quand**</span> le virement est confirmé par Mangopay

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 21. Justification seuil 50€ - Éviter frais bancaires micro-sommes

<span style="color: #2196F3">**Étant donné**</span> que Mangopay facture des frais fixes par virement
<span style="color: #9E9E9E">**Et**</span> que les banques peuvent facturer des frais de réception

<span style="color: #FF9800">**Quand**</span> un créateur génère seulement 5€/mois

<span style="color: #4CAF50">**Alors**</span> un virement mensuel coûterait proportionnellement trop cher
<span style="color: #9E9E9E">**Et**</span> le seuil de 50€ garantit des frais proportionnels raisonnables

---

## 22. Comparaison avec YouTube (seuil 100$)

<span style="color: #2196F3">**Étant donné**</span> que YouTube fixe le seuil à 100$ (~90€)

<span style="color: #FF9800">**Quand**</span> RoadWave fixe le seuil à 50€

<span style="color: #4CAF50">**Alors**</span> RoadWave est plus accessible pour petits créateurs
<span style="color: #9E9E9E">**Et**</span> les paiements arrivent plus rapidement

---

## 23. Comparaison avec Twitch (seuil 50$)

<span style="color: #2196F3">**Étant donné**</span> que Twitch fixe le seuil à 50$ (~45€)

<span style="color: #FF9800">**Quand**</span> RoadWave fixe le seuil à 50€

<span style="color: #4CAF50">**Alors**</span> le seuil est aligné sur Twitch
<span style="color: #9E9E9E">**Et**</span> les créateurs comprennent facilement le système

---

## 24. Comparaison avec Spotify (seuil 10€ mais délais longs)

<span style="color: #2196F3">**Étant donné**</span> que Spotify a un seuil bas de 10€ mais verse tous les 3 mois

<span style="color: #FF9800">**Quand**</span> RoadWave a un seuil de 50€ mais verse chaque mois

<span style="color: #4CAF50">**Alors**</span> les créateurs reçoivent leurs paiements plus régulièrement
<span style="color: #9E9E9E">**Et**</span> la trésorerie est plus prévisible

---

## 25. Relevé mensuel PDF automatique

<span style="color: #2196F3">**Étant donné**</span> que mes revenus de janvier sont calculés

<span style="color: #FF9800">**Quand**</span> le 1er février arrive

<span style="color: #4CAF50">**Alors**</span> un relevé mensuel PDF est généré automatiquement:
<span style="color: #9E9E9E">**Et**</span> le PDF est téléchargeable depuis mon tableau de bord

---

## 26. Conservation relevés 10 ans (obligation comptable)

<span style="color: #2196F3">**Étant donné**</span> que je génère des revenus sur RoadWave

<span style="color: #FF9800">**Quand**</span> je télécharge mes relevés mensuels

<span style="color: #4CAF50">**Alors**</span> je dois les conserver 10 ans (obligation légale France)
<span style="color: #9E9E9E">**Et**</span> RoadWave conserve également une copie pendant 10 ans pour audit

---

## 27. Dashboard admin - Monitoring paiements

<span style="color: #2196F3">**Étant donné**</span> qu'un admin RoadWave consulte les paiements du mois

<span style="color: #FF9800">**Quand**</span> il accède au dashboard admin

<span style="color: #4CAF50">**Alors**</span> 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%

<span style="color: #2196F3">**Étant donné**</span> que 8% des virements du mois ont échoué

<span style="color: #FF9800">**Quand**</span> le système détecte le taux d'échec élevé

<span style="color: #4CAF50">**Alors**</span> une alerte est envoyée à l'équipe technique:

---

## 29. Statistiques personnelles - Moyenne revenus sur 6 mois

<span style="color: #2196F3">**Étant donné**</span> que je suis monétisé depuis 6 mois

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> je vois:

    | métrique | valeur |
    |---|---|
    | Revenus moyens/mois | 134.50€ |
    | Meilleur mois | 189.00€ |
    | Mois le plus bas | 87.30€ |
    | Tendance | +12% ↗ |

<span style="color: #9E9E9E">**Et**</span> cela m'aide à suivre ma progression

---

## 30. Projection revenus annuels

<span style="color: #2196F3">**Étant donné**</span> que mes revenus moyens sont 134.50€/mois

<span style="color: #FF9800">**Quand**</span> je consulte les projections

<span style="color: #4CAF50">**Alors**</span> le système estime mes revenus annuels à ~1,614€
<span style="color: #9E9E9E">**Et**</span> je peux anticiper mes déclarations fiscales

---

## 31. Notification seuil symbolique 1000€ cumulés

<span style="color: #2196F3">**Étant donné**</span> que mes revenus cumulés depuis inscription atteignent 1000€

<span style="color: #FF9800">**Quand**</span> le paiement qui franchit ce seuil est effectué

<span style="color: #4CAF50">**Alors**</span> je reçois une notification:

---

## 32. Performance calcul avec 100 000 créateurs monétisés

<span style="color: #2196F3">**Étant donné**</span> que RoadWave a 100 000 créateurs monétisés

<span style="color: #FF9800">**Quand**</span> le calcul des paiements du 15 du mois est lancé

<span style="color: #4CAF50">**Alors**</span> un job asynchrone traite les paiements par batch de 1000
<span style="color: #9E9E9E">**Et**</span> tous les virements sont initiés en 2-4 heures
<span style="color: #9E9E9E">**Et**</span> les serveurs Mangopay gèrent la charge sans problème

---

## 33. Backup des données de paiement

<span style="color: #2196F3">**Étant donné**</span> que les paiements sont critiques pour les créateurs

<span style="color: #FF9800">**Quand**</span> un paiement est effectué

<span style="color: #4CAF50">**Alors**</span> les données sont sauvegardées dans PostgreSQL (principal)
<span style="color: #9E9E9E">**Et**</span> répliquées vers une base de backup (replica)
<span style="color: #9E9E9E">**Et**</span> une copie d'archive est stockée sur S3 (conservation 10 ans)

---

## 34. Audit trail complet des paiements

<span style="color: #2196F3">**Étant donné**</span> qu'un paiement est initié, traité et complété

<span style="color: #FF9800">**Quand**</span> un audit est demandé

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ces logs sont conservés 10 ans pour conformité

---

## 35. Protection fraude - Détection pattern suspect

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur génère subitement 10 000€ de revenus en 1 mois

<span style="color: #4CAF50">**Alors**</span> que sa moyenne est de 50€/mois

<span style="color: #FF9800">**Quand**</span> le système détecte cette anomalie

<span style="color: #4CAF50">**Alors**</span> le paiement est mis en attente pour vérification manuelle
<span style="color: #9E9E9E">**Et**</span> l'équipe modération analyse le compte avant validation

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec la monétisation activée
> <span style="color: #9E9E9E">**Et**</span> que mon KYC est validé
## 1. CPM créateur de 3€ / 1000 écoutes complètes

<span style="color: #2196F3">**Étant donné**</span> que mes contenus ont généré 1000 écoutes complètes par des utilisateurs gratuits

<span style="color: #FF9800">**Quand**</span> le calcul des revenus du mois est effectué

<span style="color: #4CAF50">**Alors**</span> je touche 3.00€ pour ces 1000 écoutes
<span style="color: #9E9E9E">**Et**</span> ce montant est ajouté à mon solde disponible

---

## 2. 10 000 écoutes gratuits → 30€ de revenus pub

<span style="color: #2196F3">**Étant donné**</span> que mes contenus ont généré 10 000 écoutes complètes (utilisateurs gratuits)

<span style="color: #FF9800">**Quand**</span> le mois se termine

<span style="color: #4CAF50">**Alors**</span> je touche 30.00€ de revenus publicitaires
<span style="color: #9E9E9E">**Et**</span> ces revenus sont visibles en temps réel dans mon tableau de bord

---

## 3. 50 000 écoutes gratuits → 150€ de revenus pub

<span style="color: #2196F3">**Étant donné**</span> que mes contenus ont généré 50 000 écoutes complètes (utilisateurs gratuits)

<span style="color: #FF9800">**Quand**</span> le mois se termine

<span style="color: #4CAF50">**Alors**</span> je touche 150.00€ de revenus publicitaires

---

## 4. 100 000 écoutes gratuits → 300€ de revenus pub

<span style="color: #2196F3">**Étant donné**</span> que mes contenus ont généré 100 000 écoutes complètes (utilisateurs gratuits)

<span style="color: #FF9800">**Quand**</span> le mois se termine

<span style="color: #4CAF50">**Alors**</span> je touche 300.00€ de revenus publicitaires

---

## 5. Répartition économique - Plateforme garde 94%

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité facturée 0.05€/écoute génère 50€ CPM

<span style="color: #FF9800">**Quand**</span> la plateforme calcule la répartition

<span style="color: #4CAF50">**Alors**</span> le créateur touche 3€ (6% du CA pub)
<span style="color: #9E9E9E">**Et**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur gratuit écoute mon contenu de 10 minutes

<span style="color: #FF9800">**Quand**</span> il écoute 8 minutes (80%)

<span style="color: #4CAF50">**Alors**</span> l'écoute compte comme "complète"
<span style="color: #9E9E9E">**Et**</span> je génère 0.003€ de revenus pub (3€/1000)

---

## 7. Écoute incomplète <80% ne compte pas

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur gratuit écoute mon contenu de 10 minutes
<span style="color: #F44336">**Mais**</span> il skip après 5 minutes (50%)

<span style="color: #FF9800">**Quand**</span> le calcul des revenus est effectué

<span style="color: #4CAF50">**Alors**</span> cette écoute ne compte pas comme "complète"
<span style="color: #9E9E9E">**Et**</span> je ne génère aucun revenu publicitaire pour cette écoute

---

## 8. Écoutes Premium ne comptent pas pour les revenus pub

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium écoute 100% de mon contenu

<span style="color: #FF9800">**Quand**</span> le calcul des revenus publicitaires est effectué

<span style="color: #4CAF50">**Alors**</span> cette écoute ne compte pas dans les revenus pub
<span style="color: #F44336">**Mais**</span> elle compte dans les revenus Premium (système séparé)

---

## 9. Détection bots - Écoutes exclues

<span style="color: #2196F3">**Étant donné**</span> qu'un bot génère 10 000 écoutes artificielles sur mes contenus

<span style="color: #FF9800">**Quand**</span> le système détecte le pattern suspect (rate limiting, IP unique, etc.)

<span style="color: #4CAF50">**Alors**</span> ces écoutes sont marquées comme frauduleuses
<span style="color: #9E9E9E">**Et**</span> elles sont exclues du calcul des revenus publicitaires

---

## 10. Comparaison avec YouTube (3-5€/1000 vues)

<span style="color: #2196F3">**Étant donné**</span> que YouTube paie 3-5€/1000 vues

<span style="color: #FF9800">**Quand**</span> RoadWave fixe le CPM créateur à 3€/1000 écoutes

<span style="color: #4CAF50">**Alors**</span> le tarif est aligné sur le bas de la fourchette YouTube
<span style="color: #9E9E9E">**Et**</span> cela est compétitif pour un MVP sans marché publicitaire mature

---

## 11. Comparaison avec Spotify (3-4€/1000 écoutes)

<span style="color: #2196F3">**Étant donné**</span> que Spotify paie ~3-4€/1000 écoutes

<span style="color: #FF9800">**Quand**</span> RoadWave fixe le CPM créateur à 3€/1000 écoutes

<span style="color: #4CAF50">**Alors**</span> le tarif est aligné sur l'industrie musicale
<span style="color: #9E9E9E">**Et**</span> les créateurs audio peuvent anticiper des revenus similaires

---

## 12. Tableau de bord - Revenus pub temps réel

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mon tableau de bord créateur

<span style="color: #FF9800">**Quand**</span> je consulte mes revenus publicitaires

<span style="color: #4CAF50">**Alors**</span> je vois:

    | métrique | valeur exemple |
    |---|---|
    | Écoutes complètes ce mois (gratuit) | 23 456 |
    | Revenus pub ce mois | 70.37€ |
    | CPM effectif | 3.00€ |

<span style="color: #9E9E9E">**Et**</span> ces valeurs sont mises à jour toutes les 10 minutes

---

## 13. Répartition 70/30 - Créateur touche 70%

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium paie 4.99€/mois

<span style="color: #FF9800">**Quand**</span> la répartition est calculée

<span style="color: #4CAF50">**Alors**</span> 3.49€ sont reversés aux créateurs écoutés (70%)
<span style="color: #9E9E9E">**Et**</span> 1.50€ sont gardés par la plateforme (30%)

---

## 14. Utilisateur écoute 3 créateurs - Répartition proportionnelle

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium paie 4.99€/mois
<span style="color: #9E9E9E">**Et**</span> 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% |


<span style="color: #FF9800">**Quand**</span> le calcul des revenus Premium est effectué

<span style="color: #4CAF50">**Alors**</span> la répartition est:

    | créateur | revenus |
    |---|---|
    | Créateur A | 1.75€ |
    | Créateur B | 1.05€ |
    | Créateur C | 0.70€ |

<span style="color: #9E9E9E">**Et**</span> la somme totale versée aux créateurs est 3.50€ (70% de 4.99€)

---

## 15. Calcul SQL proportionnel au temps d'écoute

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium a écouté plusieurs créateurs

<span style="color: #FF9800">**Quand**</span> le système calcule les revenus du mois

<span style="color: #4CAF50">**Alors**</span> la requête SQL suivante est exécutée:

---

## 16. Utilisateur écoute un seul créateur - 100% à ce créateur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium paie 4.99€/mois
<span style="color: #9E9E9E">**Et**</span> qu'il n'écoute qu'un seul créateur (moi)

<span style="color: #FF9800">**Quand**</span> le mois se termine

<span style="color: #4CAF50">**Alors**</span> je touche 3.49€ (70% de 4.99€)
<span style="color: #9E9E9E">**Et**</span> je reçois 100% de la part créateurs

---

## 17. Utilisateur Premium inactif - Aucun revenu généré

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium paie 4.99€/mois
<span style="color: #F44336">**Mais**</span> qu'il n'écoute aucun contenu ce mois

<span style="color: #FF9800">**Quand**</span> le calcul des revenus Premium est effectué

<span style="color: #4CAF50">**Alors**</span> aucun créateur ne reçoit de revenus de cet utilisateur
<span style="color: #9E9E9E">**Et**</span> les 3.49€ de la part créateurs restent à la plateforme
<span style="color: #9E9E9E">**Et**</span> cela couvre les coûts d'infrastructure

---

## 18. Comparaison avec YouTube Premium (70/30)

<span style="color: #2196F3">**Étant donné**</span> que YouTube Premium reverse 70% aux créateurs

<span style="color: #FF9800">**Quand**</span> RoadWave fixe également 70/30

<span style="color: #4CAF50">**Alors**</span> le modèle est aligné sur le standard industrie
<span style="color: #9E9E9E">**Et**</span> les créateurs ont confiance dans l'équité du système

---

## 19. Comparaison avec Spotify (70/30)

<span style="color: #2196F3">**Étant donné**</span> que Spotify reverse 70% aux artistes

<span style="color: #FF9800">**Quand**</span> RoadWave fixe également 70/30

<span style="color: #4CAF50">**Alors**</span> le modèle est identique à Spotify
<span style="color: #9E9E9E">**Et**</span> les créateurs audio comprennent facilement le système

---

## 20. Apple Music moins avantageux (52/48)

<span style="color: #2196F3">**Étant donné**</span> qu'Apple Music ne reverse que 52% aux artistes

<span style="color: #FF9800">**Quand**</span> RoadWave offre 70% aux créateurs

<span style="color: #4CAF50">**Alors**</span> RoadWave est plus avantageux de 18 points
<span style="color: #9E9E9E">**Et**</span> cela devient un argument marketing fort

---

## 21. Justification équité - Créateurs les plus écoutés gagnent plus

<span style="color: #2196F3">**Étant donné**</span> que 2 créateurs ont le même nombre d'abonnés Premium
<span style="color: #F44336">**Mais**</span> que le Créateur A est écouté 20h/mois et le Créateur B seulement 2h/mois

<span style="color: #FF9800">**Quand**</span> les revenus Premium sont calculés

<span style="color: #4CAF50">**Alors**</span> le Créateur A gagne 10× plus que le Créateur B
<span style="color: #9E9E9E">**Et**</span> cela récompense la qualité et l'engagement (pas juste l'abonnement)

---

## 22. Pas de "winner takes all" - Équité totale

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium écoute 10 créateurs différents

<span style="color: #FF9800">**Quand**</span> les revenus sont calculés

<span style="color: #4CAF50">**Alors**</span> chacun des 10 créateurs reçoit sa part proportionnelle
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de système où un seul créateur prend tout

---

## 23. Marge plateforme 30% couvre absence revenus pub Premium

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium ne voit aucune publicité

<span style="color: #FF9800">**Quand**</span> la plateforme calcule ses revenus

<span style="color: #4CAF50">**Alors**</span> elle ne touche que les 30% de l'abonnement Premium (1.50€)
<span style="color: #9E9E9E">**Et**</span> cette marge compense la perte des revenus publicitaires (qui auraient été ~47€/1000 écoutes)

---

## 24. Tableau de bord - Revenus Premium temps réel

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mon tableau de bord créateur

<span style="color: #FF9800">**Quand**</span> je consulte mes revenus Premium

<span style="color: #4CAF50">**Alors**</span> 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€ |

<span style="color: #9E9E9E">**Et**</span> ces valeurs sont mises à jour toutes les 10 minutes

---

## 25. Revenus cumulés pub + premium

<span style="color: #2196F3">**Étant donné**</span> que j'ai généré ce mois:

    | source | montant |
    |---|---|
    | Revenus pub | 150.00€ |
    | Revenus Premium | 89.23€ |


<span style="color: #FF9800">**Quand**</span> je consulte mon solde disponible

<span style="color: #4CAF50">**Alors**</span> le total est 239.23€
<span style="color: #9E9E9E">**Et**</span> ce solde sera versé le 15 du mois prochain (si ≥50€)

---

## 26. Dashboard créateur - Vue d'ensemble

<span style="color: #2196F3">**Étant donné**</span> que j'accède à mon tableau de bord créateur

<span style="color: #FF9800">**Quand**</span> je consulte la page revenus

<span style="color: #4CAF50">**Alors**</span> je vois:

---

## 27. Export comptable CSV pour expert-comptable

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Exporter pour comptable"

<span style="color: #FF9800">**Quand**</span> l'export est généré

<span style="color: #4CAF50">**Alors**</span> je télécharge un fichier CSV:
<span style="color: #9E9E9E">**Et**</span> je peux transmettre ce fichier à mon expert-comptable

---

## 28. Notification hebdomadaire progression revenus

<span style="color: #2196F3">**Étant donné**</span> que je suis créateur monétisé

<span style="color: #FF9800">**Quand**</span> chaque lundi matin arrive

<span style="color: #4CAF50">**Alors**</span> je reçois un email récapitulatif:

---

## 29. Graphique évolution revenus sur 12 mois

<span style="color: #2196F3">**Étant donné**</span> que je suis monétisé depuis 12 mois

<span style="color: #FF9800">**Quand**</span> j'accède à mes statistiques

<span style="color: #4CAF50">**Alors**</span> 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€ |
    | ... | ... | ... | ... |

<span style="color: #9E9E9E">**Et**</span> cela m'aide à suivre ma progression

---

## 30. Top 3 contenus les plus rentables du mois

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié 20 contenus ce mois

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques détaillées

<span style="color: #4CAF50">**Alors**</span> 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€ |

<span style="color: #9E9E9E">**Et**</span> cela m'aide à comprendre quel type de contenu plaît le plus

---

## 31. Alertes seuils de revenus

<span style="color: #2196F3">**Étant donné**</span> que j'ai activé les notifications de seuils

<span style="color: #FF9800">**Quand**</span> mes revenus du mois dépassent 100€ pour la première fois

<span style="color: #4CAF50">**Alors**</span> je reçois une notification:

---

## 32. Performance calcul avec 100 000 créateurs

<span style="color: #2196F3">**Étant donné**</span> que RoadWave a 100 000 créateurs monétisés

<span style="color: #FF9800">**Quand**</span> le calcul des revenus mensuels est lancé le dernier jour du mois

<span style="color: #4CAF50">**Alors**</span> un job asynchrone traite tous les créateurs
<span style="color: #9E9E9E">**Et**</span> le calcul prend environ 2-4 heures pour tous les créateurs
<span style="color: #9E9E9E">**Et**</span> les résultats sont stockés dans la table monthly_revenues

---

## 33. Cache Redis pour métriques temps réel

<span style="color: #2196F3">**Étant donné**</span> que je consulte mon dashboard plusieurs fois par jour

<span style="color: #FF9800">**Quand**</span> la page se charge

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le temps de réponse est <30ms

---

## 34. Prévision revenus fin de mois

<span style="color: #2196F3">**Étant donné**</span> que nous sommes le 20 du mois
<span style="color: #9E9E9E">**Et**</span> que mes revenus actuels sont 160€

<span style="color: #FF9800">**Quand**</span> le système calcule la projection

<span style="color: #4CAF50">**Alors**</span> il estime les revenus fin de mois à ~240€ (extrapolation linéaire)
<span style="color: #9E9E9E">**Et**</span> affiche "Projection fin de mois: ~240€"
<span style="color: #9E9E9E">**Et**</span> cela m'aide à anticiper mes revenus

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est connecté
> <span style="color: #9E9E9E">**Et**</span> que le véhicule est à l'arrêt (vitesse GPS = 0 km/h)
## 1. Like explicite avec bouton cœur

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu tagué "Automobile"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Automobile" est à 60%

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton cœur "Like"

<span style="color: #4CAF50">**Alors**</span> ma jauge "Automobile" augmente de 2%
<span style="color: #9E9E9E">**Et**</span> une animation de cœur rouge s'affiche
<span style="color: #9E9E9E">**Et**</span> une vibration courte est déclenchée
<span style="color: #9E9E9E">**Et**</span> ma jauge "Automobile" est maintenant à 62%

---

## 2. Like explicite cumulable avec like automatique

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté un contenu "Voyage" à 85%
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu un like automatique renforcé (+2%)
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Voyage" est à 52%

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton cœur "Like"

<span style="color: #4CAF50">**Alors**</span> ma jauge "Voyage" augmente encore de 2%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Voyage" passe à 54%
<span style="color: #9E9E9E">**Et**</span> les deux likes sont cumulés

---

## 3. Unlike retire le like manuel uniquement

<span style="color: #2196F3">**Étant donné**</span> que j'ai liké manuellement un contenu "Sport"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Sport" est à 57%

<span style="color: #FF9800">**Quand**</span> je clique à nouveau sur le bouton cœur (toggle)

<span style="color: #4CAF50">**Alors**</span> le cœur redevient vide (unlike)
<span style="color: #9E9E9E">**Et**</span> ma jauge "Sport" diminue de 2%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Sport" revient à 55%

---

## 4. Unlike ne retire pas le like automatique

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté un contenu "Musique" à 90%
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu un like automatique renforcé (+2%)
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Musique" est à 52%
<span style="color: #9E9E9E">**Et**</span> que je n'ai PAS liké manuellement

<span style="color: #FF9800">**Quand**</span> je consulte l'interface

<span style="color: #4CAF50">**Alors**</span> le bouton "Unlike" n'est pas disponible
<span style="color: #9E9E9E">**Et**</span> le cœur reste grisé (aucun like manuel)
<span style="color: #9E9E9E">**Et**</span> ma jauge reste à 52%

---

## 5. Abonnement à un créateur

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur publie des contenus tagués "Automobile" et "Technologie"
<span style="color: #9E9E9E">**Et**</span> que mes jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 50% |
    | Technologie | 45% |


<span style="color: #FF9800">**Quand**</span> je clique sur "S'abonner" sur le profil du créateur

<span style="color: #4CAF50">**Alors**</span> ma jauge "Automobile" augmente de 5%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Technologie" augmente de 5%
<span style="color: #9E9E9E">**Et**</span> une animation d'étoile dorée s'affiche
<span style="color: #9E9E9E">**Et**</span> un badge "Abonné ✓" apparaît sur le profil
<span style="color: #9E9E9E">**Et**</span> mes nouvelles jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 55% |
    | Technologie | 50% |


---

## 6. Désabonnement d'un créateur

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à un créateur
<span style="color: #9E9E9E">**Et**</span> que mes jauges "Automobile" et "Technologie" sont à 55% et 50%

<span style="color: #FF9800">**Quand**</span> je clique sur "Se désabonner"

<span style="color: #4CAF50">**Alors**</span> ma jauge "Automobile" diminue de 5%
<span style="color: #9E9E9E">**Et**</span> ma jauge "Technologie" diminue de 5%
<span style="color: #9E9E9E">**Et**</span> le badge "Abonné ✓" disparaît
<span style="color: #9E9E9E">**Et**</span> mes nouvelles jauges sont:

    | catégorie | niveau |
    |---|---|
    | Automobile | 50% |
    | Technologie | 45% |


---

## 7. Signalement d'un contenu inapproprié

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu

<span style="color: #FF9800">**Quand**</span> je clique sur le menu contextuel "⋮"
<span style="color: #9E9E9E">**Et**</span> que je sélectionne "Signaler"

<span style="color: #4CAF50">**Alors**</span> un formulaire de signalement s'ouvre
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> je peux ajouter un commentaire optionnel
<span style="color: #9E9E9E">**Et**</span> le signalement est envoyé au flux de modération

---

## 8. Feedback visuel pour like explicite

<span style="color: #2196F3">**Étant donné**</span> que je clique sur le bouton cœur

<span style="color: #FF9800">**Quand**</span> le like est enregistré

<span style="color: #4CAF50">**Alors**</span> une animation de cœur rouge se lance (0.5s)
<span style="color: #9E9E9E">**Et**</span> le cœur reste rouge plein
<span style="color: #9E9E9E">**Et**</span> une vibration haptique courte est déclenchée (iOS: .light, Android: 50ms)
<span style="color: #9E9E9E">**Et**</span> un badge "♥ Ajouté à vos favoris" s'affiche 2 secondes

---

## 9. Feedback visuel pour abonnement

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "S'abonner"

<span style="color: #FF9800">**Quand**</span> l'abonnement est enregistré

<span style="color: #4CAF50">**Alors**</span> une animation d'étoile dorée se lance (0.8s)
<span style="color: #9E9E9E">**Et**</span> le bouton devient "Abonné ✓" avec badge doré
<span style="color: #9E9E9E">**Et**</span> une notification "Abonné à [Créateur]" s'affiche
<span style="color: #9E9E9E">**Et**</span> les contenus du créateur seront boostés +30% dans l'algo

---

## 10. Bouton like désactivé si vitesse >10 km/h

<span style="color: #2196F3">**Étant donné**</span> que je conduis à 50 km/h

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder au bouton cœur dans l'app mobile

<span style="color: #4CAF50">**Alors**</span> le bouton est grisé et non cliquable
<span style="color: #9E9E9E">**Et**</span> un message "Arrêtez-vous pour liker" s'affiche si clic tenté
<span style="color: #9E9E9E">**Et**</span> seules les commandes au volant physiques fonctionnent

---

## 11. Bouton abonnement désactivé en conduite

<span style="color: #2196F3">**Étant donné**</span> que je conduis à 40 km/h

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder au profil créateur dans l'app

<span style="color: #4CAF50">**Alors**</span> le bouton "S'abonner" est désactivé
<span style="color: #9E9E9E">**Et**</span> un message "Arrêtez-vous pour vous abonner" s'affiche
<span style="color: #9E9E9E">**Et**</span> la navigation dans l'app est limitée aux fonctions lecture

---

## 12. Signalement possible en conduite via vocal

<span style="color: #2196F3">**Étant donné**</span> que je conduis à 60 km/h
<span style="color: #9E9E9E">**Et**</span> que j'utilise CarPlay avec Siri

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, signale ce contenu"

<span style="color: #4CAF50">**Alors**</span> Siri demande "Quelle catégorie ?"
<span style="color: #9E9E9E">**Et**</span> je peux répondre vocalement "Spam" ou autre catégorie
<span style="color: #9E9E9E">**Et**</span> le signalement est enregistré sans toucher l'écran

---

## 13. Actions vocales disponibles avec CarPlay/Android Auto

<span style="color: #2196F3">**Étant donné**</span> que je conduis avec CarPlay activé

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, like ce podcast"

<span style="color: #4CAF50">**Alors**</span> un like explicite (+2%) est enregistré
<span style="color: #9E9E9E">**Et**</span> Siri confirme "J'ai ajouté ce contenu à vos favoris"

<span style="color: #FF9800">**Quand**</span> je dis "OK Google, abonne-moi à ce créateur"

<span style="color: #4CAF50">**Alors**</span> l'abonnement est enregistré (+5% toutes jauges)
<span style="color: #9E9E9E">**Et**</span> Google Assistant confirme "Vous êtes maintenant abonné"

---

## 14. Menu contextuel accessible à l'arrêt uniquement

<span style="color: #2196F3">**Étant donné**</span> que le véhicule est à l'arrêt

<span style="color: #FF9800">**Quand**</span> je clique sur le menu "⋮" (3 points verticaux)

<span style="color: #4CAF50">**Alors**</span> 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) |

<span style="color: #9E9E9E">**Et**</span> toutes les options sont cliquables

---

## 15. Menu contextuel limité en conduite

<span style="color: #2196F3">**Étant donné**</span> que je conduis à 30 km/h

<span style="color: #FF9800">**Quand**</span> j'essaie d'ouvrir le menu "⋮"

<span style="color: #4CAF50">**Alors**</span> seules 2 options sont disponibles:

    | Option |
    |---|
    | Signaler (vocal possible) |
    | Suivant |

<span style="color: #9E9E9E">**Et**</span> les actions complexes sont désactivées

---

## 16. Persistance des likes manuels en base de données

<span style="color: #2196F3">**Étant donné**</span> que je like manuellement 5 contenus

<span style="color: #FF9800">**Quand**</span> je ferme l'application
<span style="color: #9E9E9E">**Et**</span> que je me reconnecte plus tard

<span style="color: #4CAF50">**Alors**</span> tous mes likes manuels sont toujours présents
<span style="color: #9E9E9E">**Et**</span> les cœurs rouges sont affichés sur les contenus likés
<span style="color: #9E9E9E">**Et**</span> mes jauges reflètent toujours l'impact (+2% × 5 likes)

---

## 17. Liste "Mes contenus likés" accessible dans profil

<span style="color: #2196F3">**Étant donné**</span> que j'ai liké manuellement 10 contenus

<span style="color: #FF9800">**Quand**</span> j'accède à mon profil utilisateur

<span style="color: #4CAF50">**Alors**</span> je vois une section "❤️ Mes favoris"
<span style="color: #9E9E9E">**Et**</span> la liste affiche les 10 contenus likés
<span style="color: #9E9E9E">**Et**</span> je peux cliquer pour réécouter
<span style="color: #9E9E9E">**Et**</span> je peux retirer un like (unlike) depuis cette liste

---

## 18. Liste "Mes abonnements" accessible dans profil

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 5 créateurs

<span style="color: #FF9800">**Quand**</span> j'accède à mon profil utilisateur

<span style="color: #4CAF50">**Alors**</span> je vois une section "⭐ Mes abonnements"
<span style="color: #9E9E9E">**Et**</span> la liste affiche les 5 créateurs avec leurs avatars
<span style="color: #9E9E9E">**Et**</span> je peux accéder au profil de chaque créateur
<span style="color: #9E9E9E">**Et**</span> je peux me désabonner depuis cette liste

---

## 19. Impact abonnement sur tous les tags du créateur

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur a publié des contenus avec ces tags:

    | Contenu | Tags |
    |---|---|
    | C1 | Automobile, Voyage |
    | C2 | Automobile, Technologie |
    | C3 | Voyage, Famille |

<span style="color: #9E9E9E">**Et**</span> que mes jauges sont toutes à 50%

<span style="color: #FF9800">**Quand**</span> je m'abonne à ce créateur

<span style="color: #4CAF50">**Alors**</span> les jauges impactées sont:

    | Tag | Impact |
    |---|---|
    | Automobile | +5% |
    | Voyage | +5% |
    | Technologie | +5% |
    | Famille | +5% |

<span style="color: #9E9E9E">**Et**</span> toutes les autres jauges restent à 50%

---

## 20. Limite d'abonnements (200 maximum)

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à 200 créateurs

<span style="color: #FF9800">**Quand**</span> j'essaie de m'abonner à un 201ème créateur

<span style="color: #4CAF50">**Alors**</span> un message "Limite de 200 abonnements atteinte" s'affiche
<span style="color: #9E9E9E">**Et**</span> je dois me désabonner d'un créateur existant pour en ajouter un nouveau

---

## 21. Confirmation avant désabonnement

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné à un créateur

<span style="color: #FF9800">**Quand**</span> je clique sur "Se désabonner"

<span style="color: #4CAF50">**Alors**</span> une popup de confirmation s'affiche:
<span style="color: #9E9E9E">**Et**</span> je dois confirmer pour valider
<span style="color: #9E9E9E">**Et**</span> je peux annuler pour conserver l'abonnement

---

## 22. 📋 Plan: Cumul like automatique + like manuel

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est tagué "Sport"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Sport" est à 50%

<span style="color: #FF9800">**Quand**</span> j'écoute à <pourcentage>% (like auto <auto>)
<span style="color: #9E9E9E">**Et**</span> que je like manuellement (+2%)

<span style="color: #4CAF50">**Alors**</span> l'impact total est <total>
<span style="color: #9E9E9E">**Et**</span> ma nouvelle jauge est <nouveau_niveau>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que je roule à <vitesse> km/h

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder à <action>

<span style="color: #4CAF50">**Alors**</span> l'action est <disponibilite>

**📊 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 |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est connecté
## 1. Précédent après <10s revient au contenu précédent

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté le contenu "A" pendant 2 minutes
<span style="color: #9E9E9E">**Et**</span> que j'écoute maintenant le contenu "B" depuis 5 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> la lecture revient au contenu "A"
<span style="color: #9E9E9E">**Et**</span> la position de lecture est à 2 minutes (position exacte sauvegardée)
<span style="color: #9E9E9E">**Et**</span> le contenu "B" reste en historique

---

## 2. Précédent après ≥10s rejoue le contenu actuel

<span style="color: #2196F3">**Étant donné**</span> que j'écoute le contenu "C" depuis 15 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> le contenu "C" rejoue depuis le début (position 0:00)
<span style="color: #9E9E9E">**Et**</span> la lecture ne revient pas au contenu précédent
<span style="color: #9E9E9E">**Et**</span> la progress bar revient à 0%

---

## 3. Précédent exactement à 10s rejoue le contenu actuel

<span style="color: #2196F3">**Étant donné**</span> que j'écoute le contenu "D" depuis exactement 10 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> le contenu "D" rejoue depuis le début
<span style="color: #9E9E9E">**Et**</span> la lecture ne revient pas au contenu précédent

---

## 4. Précédent sur le premier contenu de session

<span style="color: #2196F3">**Étant donné**</span> que je viens de démarrer l'application
<span style="color: #9E9E9E">**Et**</span> que j'écoute le contenu "Premier" depuis 3 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> le contenu "Premier" rejoue depuis le début
<span style="color: #9E9E9E">**Et**</span> aucun contenu précédent n'existe

---

## 5. Historique de navigation limité à 10 contenus

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté 10 contenus [C1, C2, ..., C10]
<span style="color: #9E9E9E">**Et**</span> que l'historique Redis contient 10 entrées

<span style="color: #FF9800">**Quand**</span> je passe au contenu C11

<span style="color: #4CAF50">**Alors**</span> le contenu C1 est supprimé de l'historique (FIFO)
<span style="color: #9E9E9E">**Et**</span> l'historique contient [C2, C3, ..., C10, C11]
<span style="color: #9E9E9E">**Et**</span> la taille reste à 10 contenus maximum

---

## 6. Position exacte sauvegardée dans l'historique

<span style="color: #2196F3">**Étant donné**</span> que j'écoute le contenu "A" (durée 5 minutes)

<span style="color: #FF9800">**Quand**</span> j'atteins 2 minutes 30 secondes
<span style="color: #9E9E9E">**Et**</span> que j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> l'historique enregistre:

    | content_id | position_seconds | listened_at |
    |---|---|---|
    | A | 150 | 2026-01-21T10:30:00 |


<span style="color: #FF9800">**Quand**</span> je reviens au contenu "A" via "Précédent"

<span style="color: #4CAF50">**Alors**</span> la lecture reprend exactement à 2 minutes 30 secondes

---

## 7. Navigation arrière sur plusieurs contenus

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté dans l'ordre: A (2min), B (30s), C (3min)
<span style="color: #9E9E9E">**Et**</span> que j'écoute maintenant D depuis 1 seconde

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent" (1ère fois)

<span style="color: #4CAF50">**Alors**</span> je reviens au contenu C à la position 3 minutes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent" (<10s sur C)

<span style="color: #4CAF50">**Alors**</span> je reviens au contenu B à la position 30 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent" (<10s sur B)

<span style="color: #4CAF50">**Alors**</span> je reviens au contenu A à la position 2 minutes

---

## 8. Précédent après milieu du contenu rejoue depuis début

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu de 5 minutes

<span style="color: #FF9800">**Quand**</span> j'atteins 2 minutes 30 secondes (milieu)
<span style="color: #9E9E9E">**Et**</span> que j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> le contenu actuel rejoue depuis 0:00
<span style="color: #9E9E9E">**Et**</span> je ne reviens pas au contenu précédent

---

## 9. Enchaînement Suivant puis Précédent rapide

<span style="color: #2196F3">**Étant donné**</span> que j'écoute le contenu "A" depuis 1 minute

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> le contenu "B" démarre

<span style="color: #FF9800">**Quand**</span> j'appuie immédiatement sur "Précédent" (2s après)

<span style="color: #4CAF50">**Alors**</span> je reviens au contenu "A" à la position 1 minute
<span style="color: #9E9E9E">**Et**</span> le contenu "B" reste dans l'historique

---

## 10. Transition fluide avec animation 0.3s

<span style="color: #2196F3">**Étant donné**</span> que j'appuie sur "Précédent"

<span style="color: #FF9800">**Quand**</span> le changement de contenu se produit

<span style="color: #4CAF50">**Alors**</span> la transition audio utilise un fade out/in de 0.3 secondes
<span style="color: #9E9E9E">**Et**</span> la progress bar revient avec une animation fluide
<span style="color: #9E9E9E">**Et**</span> l'interface ne montre aucun message de confirmation

---

## 11. Historique survit au changement de réseau

<span style="color: #2196F3">**Étant donné**</span> que j'ai un historique de 5 contenus en cache Redis

<span style="color: #FF9800">**Quand**</span> je perds la connexion réseau temporairement
<span style="color: #9E9E9E">**Et**</span> que je reviens en ligne

<span style="color: #4CAF50">**Alors**</span> l'historique de navigation est toujours disponible
<span style="color: #9E9E9E">**Et**</span> je peux toujours utiliser "Précédent"

---

## 12. Historique stocké en Redis avec structure complète

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté 3 contenus

<span style="color: #FF9800">**Quand**</span> je consulte le cache Redis

<span style="color: #4CAF50">**Alors**</span> la structure est:
<span style="color: #9E9E9E">**Et**</span> l'ordre est du plus récent au plus ancien

---

## 13. Précédent sur contenu en cours au début (<10s) du premier

<span style="color: #2196F3">**Étant donné**</span> que je démarre une session avec le contenu "Initial"
<span style="color: #9E9E9E">**Et**</span> que j'écoute depuis 3 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> le contenu "Initial" rejoue depuis le début
<span style="color: #9E9E9E">**Et**</span> aucune erreur n'est générée
<span style="color: #9E9E9E">**Et**</span> l'historique reste vide

---

## 14. Compteur de temps respecte les seuils exacts

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu

<span style="color: #FF9800">**Quand**</span> le temps écoulé est de 9.9 secondes
<span style="color: #9E9E9E">**Et**</span> que j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> je reviens au contenu précédent

<span style="color: #FF9800">**Quand**</span> le temps écoulé est de 10.0 secondes
<span style="color: #9E9E9E">**Et**</span> que j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> le contenu actuel rejoue depuis le début

---

## 15. Progress bar visuelle reflète le retour exact

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté le contenu "A" jusqu'à 75% (3min45 sur 5min)
<span style="color: #9E9E9E">**Et**</span> que je suis passé au contenu "B"

<span style="color: #FF9800">**Quand**</span> je reviens au contenu "A" via "Précédent"

<span style="color: #4CAF50">**Alors**</span> la progress bar affiche 75%
<span style="color: #9E9E9E">**Et**</span> l'indicateur de temps affiche "3:45 / 5:00"
<span style="color: #9E9E9E">**Et**</span> la lecture reprend exactement à cet endroit

---

## 16. Métadonnées d'historique incluent timestamp précis

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu "X" pendant 45 secondes à 10:30:15

<span style="color: #FF9800">**Quand**</span> je passe au contenu suivant

<span style="color: #4CAF50">**Alors**</span> l'historique enregistre:

    | content_id | position_seconds | listened_at |
    |---|---|---|
    | X | 45 | 2026-01-21T10:30:15Z |

<span style="color: #9E9E9E">**Et**</span> le timestamp précis permet l'analyse d'usage

---

## 17. Suppression FIFO respecte l'ordre chronologique

<span style="color: #2196F3">**Étant donné**</span> un historique de [C1@10:00, C2@10:02, ..., C10@10:20]

<span style="color: #FF9800">**Quand**</span> j'ajoute C11 à 10:22

<span style="color: #4CAF50">**Alors**</span> C1 (le plus ancien) est supprimé
<span style="color: #9E9E9E">**Et**</span> l'historique contient [C2@10:02, ..., C11@10:22]
<span style="color: #9E9E9E">**Et**</span> la taille reste exactement 10 entrées

---

## 18. 📋 Plan: Comportement selon temps écouté

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu depuis <temps> secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> l'action est <comportement>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu de 10 minutes

<span style="color: #FF9800">**Quand**</span> j'atteins <position> et passe au suivant
<span style="color: #9E9E9E">**Et**</span> que je reviens via "Précédent"

<span style="color: #4CAF50">**Alors**</span> la lecture reprend exactement à <position>

**📊 Exemples de données:**

| position |
|---|
| 0:15 |
| 1:30 |
| 3:45 |
| 5:00 |
| 7:23 |
| 9:50 |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est connecté
> <span style="color: #9E9E9E">**Et**</span> que CarPlay ou Android Auto est activé
## 1. Disponibilité des commandes vocales uniquement avec CarPlay/Android Auto

<span style="color: #2196F3">**Étant donné**</span> que je conduis avec CarPlay activé

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri"

<span style="color: #4CAF50">**Alors**</span> Siri est disponible pour les commandes RoadWave

<span style="color: #2196F3">**Étant donné**</span> que je conduis avec Android Auto activé

<span style="color: #FF9800">**Quand**</span> je dis "OK Google"

<span style="color: #4CAF50">**Alors**</span> Google Assistant est disponible pour les commandes RoadWave

---

## 2. Parc automobile compatible avec vocal (30-40% en 2026)

<span style="color: #2196F3">**Étant donné**</span> que nous sommes en 2026

<span style="color: #FF9800">**Quand**</span> je consulte les statistiques du parc automobile EU

<span style="color: #4CAF50">**Alors**</span> environ 30-40% des véhicules ont CarPlay ou Android Auto
<span style="color: #9E9E9E">**Et**</span> ces utilisateurs peuvent utiliser les commandes vocales
<span style="color: #9E9E9E">**Et**</span> les 60-70% restants utilisent les commandes au volant uniquement

---

## 3. Commande vocale "Like ce podcast" avec Siri

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu tagué "Automobile"
<span style="color: #9E9E9E">**Et**</span> que ma jauge "Automobile" est à 60%

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, like ce podcast"

<span style="color: #4CAF50">**Alors**</span> un like explicite (+2%) est enregistré
<span style="color: #9E9E9E">**Et**</span> ma jauge "Automobile" passe à 62%
<span style="color: #9E9E9E">**Et**</span> Siri confirme vocalement "J'ai ajouté ce contenu à vos favoris"
<span style="color: #9E9E9E">**Et**</span> aucune interaction écran n'est requise

---

## 4. Commande vocale "Like ce contenu" avec Google Assistant

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu tagué "Voyage"

<span style="color: #FF9800">**Quand**</span> je dis "OK Google, like ce contenu"

<span style="color: #4CAF50">**Alors**</span> un like explicite est enregistré (+2%)
<span style="color: #9E9E9E">**Et**</span> Google Assistant confirme "J'ai liké ce contenu pour vous"
<span style="color: #9E9E9E">**Et**</span> la commande fonctionne sans toucher l'écran

---

## 5. Commande vocale "Abonne-moi à ce créateur"

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu d'un créateur tagué "Automobile" et "Technologie"
<span style="color: #9E9E9E">**Et**</span> que mes jauges sont à 50% et 45%

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, abonne-moi à ce créateur"

<span style="color: #4CAF50">**Alors**</span> l'abonnement est enregistré
<span style="color: #9E9E9E">**Et**</span> mes jauges augmentent de 5% chacune (55% et 50%)
<span style="color: #9E9E9E">**Et**</span> Siri confirme "Vous êtes maintenant abonné à [Nom du créateur]"

---

## 6. Commande vocale "Passe au contenu suivant"

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu "A"

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, passe au contenu suivant"

<span style="color: #4CAF50">**Alors**</span> le contenu "B" démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> la commande a le même effet que le bouton physique "Suivant"

---

## 7. Commande vocale "Signale ce contenu"

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu inapproprié

<span style="color: #FF9800">**Quand**</span> je dis "OK Google, signale ce contenu"

<span style="color: #4CAF50">**Alors**</span> Google Assistant demande "Quelle catégorie ?"
<span style="color: #9E9E9E">**Et**</span> je réponds vocalement "Spam"
<span style="color: #4CAF50">**Alors**</span> le signalement est enregistré avec la catégorie "Spam"
<span style="color: #9E9E9E">**Et**</span> Google Assistant confirme "J'ai signalé ce contenu"

---

## 8. Commande vocale avec catégorie de signalement

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, signale ce contenu pour haine"

<span style="color: #4CAF50">**Alors**</span> le signalement est enregistré avec la catégorie "Haine et violence"
<span style="color: #9E9E9E">**Et**</span> Siri confirme "J'ai signalé ce contenu pour haine et violence"
<span style="color: #9E9E9E">**Et**</span> le flux de modération reçoit le signalement

---

## 9. Liste des catégories de signalement vocales supportées

<span style="color: #2196F3">**Étant donné**</span> que je dis "signale ce contenu pour [catégorie]"

<span style="color: #FF9800">**Quand**</span> 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 |


<span style="color: #4CAF50">**Alors**</span> le signalement est enregistré avec la bonne catégorie

---

## 10. Commande vocale non reconnue - fallback

<span style="color: #2196F3">**Étant donné**</span> que je dis "Hey Siri, super ce podcast"

<span style="color: #FF9800">**Quand**</span> Siri ne reconnaît pas l'intent RoadWave

<span style="color: #4CAF50">**Alors**</span> Siri répond "Je ne comprends pas cette commande RoadWave"
<span style="color: #9E9E9E">**Et**</span> elle suggère "Dites 'like ce podcast' ou 'passe au suivant'"

---

## 11. Commandes vocales disponibles en conduite uniquement

<span style="color: #2196F3">**Étant donné**</span> que je roule à 50 km/h

<span style="color: #FF9800">**Quand**</span> j'utilise les commandes vocales

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que l'app iOS implémente les Intents

<span style="color: #FF9800">**Quand**</span> je configure les Shortcuts iOS

<span style="color: #4CAF50">**Alors**</span> les intents suivants sont disponibles:

    | Intent Name | Action |
    |---|---|
    | LikeCurrentContentIntent | Like explicite |
    | SubscribeToCreatorIntent | Abonnement |
    | ReportContentIntent | Signalement |
    | SkipToNextContentIntent | Suivant |

<span style="color: #9E9E9E">**Et**</span> Siri les reconnaît automatiquement

---

## 13. Intent Android personnalisé pour RoadWave

<span style="color: #2196F3">**Étant donné**</span> que l'app Android implémente les Voice Actions

<span style="color: #FF9800">**Quand**</span> je configure les actions Google Assistant

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> Google Assistant les reconnaît

---

## 14. Confirmation vocale après action réussie

<span style="color: #2196F3">**Étant donné**</span> que je dis "Hey Siri, like ce podcast"

<span style="color: #FF9800">**Quand**</span> l'action est enregistrée avec succès

<span style="color: #4CAF50">**Alors**</span> Siri répond immédiatement avec confirmation:
<span style="color: #9E9E9E">**Et**</span> la réponse est naturelle et concise
<span style="color: #9E9E9E">**Et**</span> elle ne distrait pas de la conduite

---

## 15. Gestion d'erreur vocale si action échoue

<span style="color: #2196F3">**Étant donné**</span> que je dis "Hey Siri, abonne-moi à ce créateur"
<span style="color: #9E9E9E">**Et**</span> que j'ai atteint la limite de 200 abonnements

<span style="color: #FF9800">**Quand**</span> Siri essaie d'enregistrer l'abonnement

<span style="color: #4CAF50">**Alors**</span> l'action échoue
<span style="color: #9E9E9E">**Et**</span> Siri répond "Impossible de s'abonner, limite de 200 abonnements atteinte"
<span style="color: #9E9E9E">**Et**</span> elle suggère "Désabonnez-vous d'un créateur pour continuer"

---

## 16. Commandes vocales multilingues (français)

<span style="color: #2196F3">**Étant donné**</span> que mon Siri est configuré en français

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, j'aime ce podcast"

<span style="color: #4CAF50">**Alors**</span> la commande est reconnue (variante de "like ce podcast")

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, mets une étoile"

<span style="color: #4CAF50">**Alors**</span> la commande est reconnue (variante de "like")

---

## 17. Implémentation post-MVP (Sprint 5)

<span style="color: #2196F3">**Étant donné**</span> que les commandes vocales sont une feature Sprint 5

<span style="color: #FF9800">**Quand**</span> le MVP est lancé

<span style="color: #4CAF50">**Alors**</span> seules les commandes au volant physiques sont disponibles

<span style="color: #FF9800">**Quand**</span> le Sprint 5 est déployé

<span style="color: #4CAF50">**Alors**</span> les intents iOS/Android sont activés
<span style="color: #9E9E9E">**Et**</span> les commandes vocales deviennent disponibles

---

## 18. Priorisation commandes vocales vs boutons physiques

<span style="color: #2196F3">**Étant donné**</span> que je conduis avec CarPlay
<span style="color: #9E9E9E">**Et**</span> que j'ai accès aux boutons physiques ET aux commandes vocales

<span style="color: #FF9800">**Quand**</span> je veux liker un contenu

<span style="color: #4CAF50">**Alors**</span> je peux soit:
<span style="color: #9E9E9E">**Et**</span> les 3 méthodes sont valides

---

## 19. Statistiques d'usage des commandes vocales

<span style="color: #2196F3">**Étant donné**</span> que 100 utilisateurs avec CarPlay utilisent RoadWave

<span style="color: #FF9800">**Quand**</span> je consulte les analytics

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je like un contenu via commande vocale

<span style="color: #FF9800">**Quand**</span> l'action est enregistrée

<span style="color: #4CAF50">**Alors**</span> aucune vibration haptique n'est déclenchée
<span style="color: #9E9E9E">**Et**</span> seule la confirmation vocale est donnée

---

## 21. Badge visuel mis à jour après commande vocale

<span style="color: #2196F3">**Étant donné**</span> que je dis "Hey Siri, like ce podcast"

<span style="color: #FF9800">**Quand**</span> l'action est enregistrée

<span style="color: #4CAF50">**Alors**</span> le badge "♥ Ajouté à vos favoris" s'affiche sur l'écran CarPlay
<span style="color: #9E9E9E">**Et**</span> le cœur devient rouge plein dans l'interface
<span style="color: #9E9E9E">**Et**</span> la mise à jour est visible même sans toucher l'écran

---

## 22. Commandes vocales avec contenu sans créateur

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu anonyme (créateur supprimé)

<span style="color: #FF9800">**Quand**</span> je dis "Hey Siri, abonne-moi à ce créateur"

<span style="color: #4CAF50">**Alors**</span> Siri répond "Ce créateur n'est plus disponible"
<span style="color: #9E9E9E">**Et**</span> aucun abonnement n'est enregistré

---

## 23. Limitation temporelle des commandes vocales

<span style="color: #2196F3">**Étant donné**</span> que je dis "Hey Siri, like ce podcast"
<span style="color: #9E9E9E">**Et**</span> que le contenu change 1 seconde après

<span style="color: #FF9800">**Quand**</span> Siri traite la commande 2 secondes plus tard

<span style="color: #4CAF50">**Alors**</span> la commande s'applique au contenu qui était en lecture au moment de la commande
<span style="color: #9E9E9E">**Et**</span> non au contenu actuel (système de timestamp)

---

## 24. 📋 Plan: Commandes vocales avec différents assistants

<span style="color: #2196F3">**Étant donné**</span> que j'utilise <assistant>

<span style="color: #FF9800">**Quand**</span> je dis <commande>

<span style="color: #4CAF50">**Alors**</span> l'action <action> est exécutée
<span style="color: #9E9E9E">**Et**</span> la confirmation est <confirmation>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que je dis "signale ce contenu pour <mot_cle>"

<span style="color: #FF9800">**Quand**</span> <mot_cle> est reconnu

<span style="color: #4CAF50">**Alors**</span> la catégorie mappée est <categorie>

**📊 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 |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est connecté
> <span style="color: #9E9E9E">**Et**</span> que l'application est connectée via CarPlay ou Android Auto
## 1. Trois commandes disponibles au volant uniquement

<span style="color: #2196F3">**Étant donné**</span> que je conduis à 50 km/h

<span style="color: #FF9800">**Quand**</span> je consulte les commandes physiques disponibles

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> aucune commande complexe n'est proposée

---

## 2. Commande "Suivant" au volant

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu "A"

<span style="color: #FF9800">**Quand**</span> j'appuie sur le bouton physique "Suivant" au volant

<span style="color: #4CAF50">**Alors**</span> le contenu "B" démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> aucune action supplémentaire n'est requise
<span style="color: #9E9E9E">**Et**</span> l'interface ne demande aucune confirmation

---

## 3. Commande "Précédent" au volant respecte règle 10s

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu depuis 5 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent" au volant

<span style="color: #4CAF50">**Alors**</span> je reviens au contenu précédent (règle <10s)

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu depuis 15 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent" au volant

<span style="color: #4CAF50">**Alors**</span> le contenu actuel rejoue depuis le début (règle ≥10s)

---

## 4. Commande "Play/Pause" avec fade audio

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu est en lecture

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Pause" au volant

<span style="color: #4CAF50">**Alors**</span> la lecture se met en pause avec un fade out de 0.3 secondes
<span style="color: #9E9E9E">**Et**</span> la position de lecture est sauvegardée

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Play" au volant

<span style="color: #4CAF50">**Alors**</span> la lecture reprend avec un fade in de 0.3 secondes
<span style="color: #9E9E9E">**Et**</span> la reprise se fait à la position exacte

---

## 5. Aucune commande complexe supportée

<span style="color: #2196F3">**Étant donné**</span> que je conduis

<span style="color: #FF9800">**Quand**</span> j'essaie un appui long sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> l'action n'est pas détectée (non supporté iOS/Android)

<span style="color: #FF9800">**Quand**</span> j'essaie un double-appui sur "Pause"

<span style="color: #4CAF50">**Alors**</span> l'action n'est pas détectée
<span style="color: #9E9E9E">**Et**</span> seules les actions simples (clic simple) fonctionnent

---

## 6. Compatibilité 100% tous véhicules

<span style="color: #2196F3">**Étant donné**</span> que je conduis une voiture avec commandes basiques
<span style="color: #9E9E9E">**Et**</span> que mon véhicule a seulement Suivant/Précédent/Pause

<span style="color: #FF9800">**Quand**</span> j'utilise RoadWave

<span style="color: #4CAF50">**Alors**</span> toutes les fonctions essentielles sont accessibles
<span style="color: #9E9E9E">**Et**</span> je n'ai pas besoin de boutons supplémentaires

---

## 7. Feedback visuel discret après action

<span style="color: #2196F3">**Étant donné**</span> que j'appuie sur "Suivant"

<span style="color: #FF9800">**Quand**</span> le contenu change

<span style="color: #4CAF50">**Alors**</span> l'interface CarPlay/Android Auto affiche le nouveau titre
<span style="color: #9E9E9E">**Et**</span> aucune popup ne bloque la vue
<span style="color: #9E9E9E">**Et**</span> le changement est fluide et immédiat

---

## 8. Like automatique renforcé après écoute ≥80%

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu de 5 minutes tagué "Automobile"

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 4 minutes 30 secondes (90%)

<span style="color: #4CAF50">**Alors**</span> un like automatique renforcé (+2 points) est enregistré
<span style="color: #9E9E9E">**Et**</span> un badge discret "♥ Ajouté à vos favoris" s'affiche 2 secondes
<span style="color: #9E9E9E">**Et**</span> aucune action manuelle n'est requise

---

## 9. Like automatique standard après écoute 30-79%

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu de 5 minutes tagué "Voyage"

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 2 minutes (40%)
<span style="color: #9E9E9E">**Et**</span> que j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> un like automatique standard (+1 point) est enregistré
<span style="color: #9E9E9E">**Et**</span> un badge discret s'affiche brièvement
<span style="color: #9E9E9E">**Et**</span> je peux continuer à conduire sans interruption

---

## 10. Signal négatif après skip rapide <10s

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu tagué "Politique"

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant" après seulement 5 secondes

<span style="color: #4CAF50">**Alors**</span> un signal négatif (-0.5 point) est enregistré
<span style="color: #9E9E9E">**Et**</span> la jauge "Politique" diminue légèrement
<span style="color: #9E9E9E">**Et**</span> aucun message n'est affiché (transparence)

---

## 11. Pas de like si écoute <30%

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu de 10 minutes

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 2 minutes (20%)
<span style="color: #9E9E9E">**Et**</span> que j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> aucun like n'est enregistré
<span style="color: #9E9E9E">**Et**</span> les jauges ne changent pas
<span style="color: #9E9E9E">**Et**</span> le système considère l'écoute comme neutre

---

## 12. Badge de feedback visuel disparaît après 2 secondes

<span style="color: #2196F3">**Étant donné**</span> que je reçois un like automatique

<span style="color: #FF9800">**Quand**</span> le badge "♥ Ajouté à vos favoris" apparaît

<span style="color: #4CAF50">**Alors**</span> il reste visible 2 secondes en bas de l'écran
<span style="color: #9E9E9E">**Et**</span> il disparaît automatiquement sans action
<span style="color: #9E9E9E">**Et**</span> il ne bloque pas la vue de la route

---

## 13. Tracking du temps d'écoute précis côté client

<span style="color: #2196F3">**Étant donné**</span> que je démarre la lecture d'un contenu

<span style="color: #FF9800">**Quand**</span> le player audio iOS/Android enregistre le temps

<span style="color: #4CAF50">**Alors**</span> le startTime est enregistré à la milliseconde

<span style="color: #FF9800">**Quand**</span> j'arrête la lecture (Suivant, Pause, ou fin)

<span style="color: #4CAF50">**Alors**</span> la durée exacte écoutée est calculée
<span style="color: #9E9E9E">**Et**</span> le pourcentage (durée / durée_totale * 100) est envoyé à l'API

---

## 14. API reçoit les événements d'écoute pour calcul

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu de 5 minutes à 80%

<span style="color: #FF9800">**Quand**</span> l'événement est envoyé à l'API

<span style="color: #4CAF50">**Alors**</span> le backend reçoit:
<span style="color: #9E9E9E">**Et**</span> le backend calcule le like automatique (+2 points)
<span style="color: #9E9E9E">**Et**</span> les jauges sont mises à jour immédiatement (Redis + PostgreSQL)

---

## 15. Actions différentes selon arrêt du contenu

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> l'action envoyée est "skipped"

<span style="color: #FF9800">**Quand**</span> le contenu se termine naturellement

<span style="color: #4CAF50">**Alors**</span> l'action envoyée est "completed"

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Pause"

<span style="color: #4CAF50">**Alors**</span> l'action envoyée est "paused"
<span style="color: #9E9E9E">**Et**</span> le backend traite chaque action différemment

---

## 16. Calcul immédiat côté backend sans délai

<span style="color: #2196F3">**Étant donné**</span> que l'API reçoit un événement d'écoute

<span style="color: #FF9800">**Quand**</span> le backend traite l'événement

<span style="color: #4CAF50">**Alors**</span> les jauges sont mises à jour immédiatement (< 100ms)
<span style="color: #9E9E9E">**Et**</span> les nouvelles recommandations utilisent les valeurs actualisées
<span style="color: #9E9E9E">**Et**</span> il n'y a aucun batch différé

---

## 17. Compatibilité iOS avec AVPlayer

<span style="color: #2196F3">**Étant donné**</span> que l'app iOS utilise AVPlayer

<span style="color: #FF9800">**Quand**</span> les commandes physiques sont interceptées

<span style="color: #4CAF50">**Alors**</span> les événements MPRemoteCommandCenter sont capturés:

    | Commande | Événement iOS |
    |---|---|
    | Suivant | nextTrackCommand |
    | Précédent | previousTrackCommand |
    | Play/Pause | playCommand / pauseCommand |

<span style="color: #9E9E9E">**Et**</span> le tracking du temps utilise CMTime

---

## 18. Compatibilité Android avec MediaSession

<span style="color: #2196F3">**Étant donné**</span> que l'app Android utilise MediaPlayer

<span style="color: #FF9800">**Quand**</span> les commandes physiques sont interceptées

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le tracking du temps utilise SystemClock.elapsedRealtime()

---

## 19. Sécurité maximale - pas de distraction

<span style="color: #2196F3">**Étant donné**</span> que je conduis à 80 km/h

<span style="color: #FF9800">**Quand**</span> j'utilise RoadWave avec les commandes au volant

<span style="color: #4CAF50">**Alors**</span> je n'ai jamais besoin de regarder mon téléphone
<span style="color: #9E9E9E">**Et**</span> je n'ai jamais besoin de toucher l'écran CarPlay/Android Auto
<span style="color: #9E9E9E">**Et**</span> toutes les actions sont accessibles via boutons physiques
<span style="color: #9E9E9E">**Et**</span> les likes sont enregistrés automatiquement

---

## 20. 📋 Plan: Calcul du like automatique selon pourcentage

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu tagué "Sport"

<span style="color: #FF9800">**Quand**</span> j'écoute pendant <pourcentage>%

<span style="color: #4CAF50">**Alors**</span> le like automatique est <type>
<span style="color: #9E9E9E">**Et**</span> l'impact sur la jauge est <points>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu

<span style="color: #FF9800">**Quand**</span> je skip après <secondes> secondes

<span style="color: #4CAF50">**Alors**</span> le signal est <type>
<span style="color: #9E9E9E">**Et**</span> l'impact est <points>

**📊 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 |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est connecté
> <span style="color: #9E9E9E">**Et**</span> que la géolocalisation est activée
## 1. Pré-calcul initial de 5 contenus en cache

<span style="color: #2196F3">**Étant donné**</span> que je viens de démarrer l'application
<span style="color: #9E9E9E">**Et**</span> que je suis situé à Paris (48.8566, 2.3522)
<span style="color: #9E9E9E">**Et**</span> que je suis en mode voiture (vitesse ≥ 5 km/h)

<span style="color: #FF9800">**Quand**</span> l'application initialise la lecture

<span style="color: #4CAF50">**Alors**</span> une file d'attente de 5 contenus est pré-calculée
<span style="color: #9E9E9E">**Et**</span> la file est stockée en cache Redis avec la clé "user:{user_id}:queue"
<span style="color: #9E9E9E">**Et**</span> les métadonnées incluent ma position, le timestamp de calcul et le mode
<span style="color: #9E9E9E">**Et**</span> le cache a un TTL de 15 minutes

---

## 2. Commande "Suivant" sans latence

<span style="color: #2196F3">**Étant donné**</span> qu'une file d'attente de 5 contenus est en cache
<span style="color: #9E9E9E">**Et**</span> que j'écoute actuellement le contenu "A"

<span style="color: #FF9800">**Quand**</span> j'appuie sur le bouton "Suivant"

<span style="color: #4CAF50">**Alors**</span> le contenu suivant démarre immédiatement (< 100ms)
<span style="color: #9E9E9E">**Et**</span> le contenu est retiré de la file d'attente
<span style="color: #9E9E9E">**Et**</span> il reste 4 contenus dans la file

---

## 3. Recalcul automatique après déplacement >10km

<span style="color: #2196F3">**Étant donné**</span> que la file a été calculée à Paris (48.8566, 2.3522)
<span style="color: #9E9E9E">**Et**</span> que j'ai 5 contenus en cache

<span style="color: #FF9800">**Quand**</span> je me déplace à Versailles (48.8049, 2.1204) soit 12km

<span style="color: #4CAF50">**Alors**</span> la file d'attente est invalidée automatiquement
<span style="color: #9E9E9E">**Et**</span> une nouvelle file de 5 contenus est recalculée
<span style="color: #9E9E9E">**Et**</span> elle est basée sur ma nouvelle position

---

## 4. Recalcul automatique toutes les 10 minutes

<span style="color: #2196F3">**Étant donné**</span> qu'une file a été calculée il y a 10 minutes
<span style="color: #9E9E9E">**Et**</span> que ma position n'a pas changé

<span style="color: #FF9800">**Quand**</span> le timer de rafraîchissement expire

<span style="color: #4CAF50">**Alors**</span> une nouvelle file de 5 contenus est recalculée
<span style="color: #9E9E9E">**Et**</span> les anciens contenus non écoutés sont remplacés
<span style="color: #9E9E9E">**Et**</span> les nouveaux contenus publiés depuis sont inclus

---

## 5. Recalcul quand il reste moins de 3 contenus

<span style="color: #2196F3">**Étant donné**</span> qu'il reste 3 contenus dans ma file d'attente

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> il reste 2 contenus
<span style="color: #9E9E9E">**Et**</span> un recalcul asynchrone est déclenché en arrière-plan
<span style="color: #9E9E9E">**Et**</span> 3 nouveaux contenus sont ajoutés à la file
<span style="color: #9E9E9E">**Et**</span> la file contient maintenant 5 contenus

---

## 6. Insertion prioritaire d'un contenu géolocalisé en mode voiture

<span style="color: #2196F3">**Étant donné**</span> que j'ai une file de 5 contenus pré-calculée
<span style="color: #9E9E9E">**Et**</span> que je suis en mode voiture
<span style="color: #9E9E9E">**Et**</span> que je me déplace à 50 km/h vers un point avec contenu géolocalisé

<span style="color: #FF9800">**Quand**</span> je suis à 98m du point (ETA = 7 secondes)

<span style="color: #4CAF50">**Alors**</span> une notification est envoyée (icône + compteur 7→1 + son)
<span style="color: #9E9E9E">**Et**</span> je dois appuyer sur "Suivant" dans les 7 secondes pour valider

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> un décompte de 5 secondes démarre
<span style="color: #9E9E9E">**Et**</span> après 5 secondes, le contenu géolocalisé s'insère et démarre
<span style="color: #9E9E9E">**Et**</span> il remplace le contenu actuel dans la lecture

---

## 7. Contenu géolocalisé ignoré est perdu (cooldown activé)

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée est affichée (compteur 7→1)

<span style="color: #FF9800">**Quand**</span> je ne clique pas sur "Suivant" pendant les 7 secondes

<span style="color: #4CAF50">**Alors**</span> la notification disparaît
<span style="color: #9E9E9E">**Et**</span> le contenu géolocalisé est perdu (pas d'insertion dans la file)
<span style="color: #9E9E9E">**Et**</span> un cooldown de 10 minutes est activé
<span style="color: #9E9E9E">**Et**</span> aucune nouvelle notification géolocalisée ne sera envoyée pendant 10 minutes

---

## 8. Validation d'une notification géolocalisée

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée est affichée (compteur à 5)
<span style="color: #9E9E9E">**Et**</span> que j'écoute un podcast

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> le compteur bascule à "5" (décompte final)
<span style="color: #9E9E9E">**Et**</span> le podcast actuel continue de jouer
<span style="color: #9E9E9E">**Et**</span> après 5 secondes, le contenu géolocalisé démarre
<span style="color: #9E9E9E">**Et**</span> le podcast est mis en pause et sauvegardé dans l'historique

---

## 9. Invalidation immédiate après modification des préférences

<span style="color: #2196F3">**Étant donné**</span> que j'ai une file de 5 contenus en cache
<span style="color: #9E9E9E">**Et**</span> que ma vitesse GPS est de 5 km/h (piéton)

<span style="color: #FF9800">**Quand**</span> je modifie mes curseurs de préférences (géo/découverte/politique)

<span style="color: #4CAF50">**Alors**</span> la file d'attente est invalidée immédiatement
<span style="color: #9E9E9E">**Et**</span> une nouvelle file est recalculée avec les nouvelles préférences
<span style="color: #9E9E9E">**Et**</span> les anciens contenus en cache sont supprimés

---

## 10. Blocage modification préférences en conduite (>10 km/h)

<span style="color: #2196F3">**Étant donné**</span> que ma vitesse GPS est de 50 km/h (en voiture)

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder aux réglages de préférences

<span style="color: #4CAF50">**Alors**</span> l'interface affiche "Paramètres verrouillés en conduite"
<span style="color: #9E9E9E">**Et**</span> je ne peux pas modifier les curseurs géo/découverte/politique
<span style="color: #9E9E9E">**Et**</span> un message "Arrêtez-vous pour modifier vos préférences" s'affiche

---

## 11. Invalidation lors du démarrage d'un live suivi

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "RadioVoyage"
<span style="color: #9E9E9E">**Et**</span> que j'ai une file de 5 contenus en cache
<span style="color: #9E9E9E">**Et**</span> que je suis dans la zone géographique du créateur

<span style="color: #FF9800">**Quand**</span> le créateur "RadioVoyage" démarre une radio live

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push
<span style="color: #9E9E9E">**Et**</span> le contenu live s'insère en tête de la file d'attente
<span style="color: #9E9E9E">**Et**</span> la file d'attente est recalculée

---

## 12. Métadonnées de cache Redis

<span style="color: #2196F3">**Étant donné**</span> qu'une file d'attente est calculée

<span style="color: #FF9800">**Quand**</span> elle est stockée dans Redis

<span style="color: #4CAF50">**Alors**</span> la clé est "user:{user_id}:queue"
<span style="color: #9E9E9E">**Et**</span> les métadonnées incluent:

    | champ | valeur |
    |---|---|
    | last_lat | 48.8566 |
    | last_lon | 2.3522 |
    | computed_at | 2026-01-21T10:30:00Z |
    | mode | voiture |

<span style="color: #9E9E9E">**Et**</span> le TTL est de 15 minutes (900 secondes)

---

## 13. Contenu géolocalisé remplace le contenu actuel (pas d'insertion en file)

<span style="color: #2196F3">**Étant donné**</span> que j'écoute le contenu C2 de ma file [C1, C2, C3, C4, C5]
<span style="color: #9E9E9E">**Et**</span> qu'une notification géolocalisée "Tour Eiffel" est déclenchée

<span style="color: #FF9800">**Quand**</span> je valide la notification
<span style="color: #9E9E9E">**Et**</span> que le décompte de 5s se termine

<span style="color: #4CAF50">**Alors**</span> le contenu "Tour Eiffel" remplace C2 et démarre
<span style="color: #9E9E9E">**Et**</span> C2 est sauvegardé dans l'historique de navigation
<span style="color: #9E9E9E">**Et**</span> la file reste [C3, C4, C5] (pas de contenu retiré)
<span style="color: #9E9E9E">**Et**</span> quand "Tour Eiffel" se termine, C3 démarre

---

## 14. Invalidation après déplacement exactement 10km

<span style="color: #2196F3">**Étant donné**</span> que la file a été calculée à une position donnée

<span style="color: #FF9800">**Quand**</span> je me déplace d'exactement 10.0 km

<span style="color: #4CAF50">**Alors**</span> la file d'attente n'est PAS invalidée (seuil strict >10km)
<span style="color: #9E9E9E">**Et**</span> les contenus en cache restent valides

<span style="color: #FF9800">**Quand**</span> je me déplace de 10.1 km supplémentaires (total 10.1km)

<span style="color: #4CAF50">**Alors**</span> la file d'attente est invalidée
<span style="color: #9E9E9E">**Et**</span> une nouvelle file est calculée

---

## 15. Rafraîchissement exactement après 10 minutes

<span style="color: #2196F3">**Étant donné**</span> qu'une file a été calculée à 10:00:00

<span style="color: #FF9800">**Quand**</span> l'heure actuelle est 10:10:00

<span style="color: #4CAF50">**Alors**</span> le timer de rafraîchissement expire
<span style="color: #9E9E9E">**Et**</span> une nouvelle file de 5 contenus est recalculée
<span style="color: #9E9E9E">**Et**</span> le timestamp "computed_at" est mis à jour

---

## 16. Recalcul asynchrone non-bloquant

<span style="color: #2196F3">**Étant donné**</span> qu'il reste 2 contenus dans la file
<span style="color: #9E9E9E">**Et**</span> que j'appuie sur "Suivant"

<span style="color: #FF9800">**Quand**</span> le recalcul asynchrone démarre

<span style="color: #4CAF50">**Alors**</span> la lecture du contenu actuel n'est pas interrompue
<span style="color: #9E9E9E">**Et**</span> le recalcul se fait en arrière-plan
<span style="color: #9E9E9E">**Et**</span> les nouveaux contenus sont ajoutés dès disponibles (< 500ms)
<span style="color: #9E9E9E">**Et**</span> l'utilisateur ne perçoit aucune latence

---

## 17. Notification basée sur ETA (pas distance fixe)

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu géolocalisé existe à un point GPS
<span style="color: #9E9E9E">**Et**</span> que je roule à 130 km/h

<span style="color: #FF9800">**Quand**</span> je suis à 252m du point (ETA = 7 secondes)

<span style="color: #4CAF50">**Alors**</span> une notification est envoyée

<span style="color: #FF9800">**Quand**</span> je suis à 300m du point (ETA = 8 secondes)

<span style="color: #4CAF50">**Alors**</span> aucune notification n'est envoyée (ETA >7s)

---

## 18. 📋 Plan: Différentes distances de déplacement et invalidation

<span style="color: #2196F3">**Étant donné**</span> qu'une file a été calculée à une position donnée

<span style="color: #FF9800">**Quand**</span> je me déplace de <distance> km

<span style="color: #4CAF50">**Alors**</span> la file est <action>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai validé 6 notifications géolocalisées dans la dernière heure

<span style="color: #FF9800">**Quand**</span> un 7ème contenu géolocalisé est détecté (ETA 7s)

<span style="color: #4CAF50">**Alors**</span> aucune notification n'est envoyée
<span style="color: #9E9E9E">**Et**</span> le quota horaire est respecté

---

## 20. Mode piéton - pas de notification avec compteur 7s

<span style="color: #2196F3">**Étant donné**</span> que je suis en mode piéton (vitesse <5 km/h)
<span style="color: #9E9E9E">**Et**</span> qu'un audio-guide géolocalisé existe à 150m

<span style="color: #FF9800">**Quand**</span> je passe dans le rayon de 200m

<span style="color: #4CAF50">**Alors**</span> une notification push système est envoyée
<span style="color: #9E9E9E">**Et**</span> aucun compteur 7s n'est affiché
<span style="color: #9E9E9E">**Et**</span> je peux ouvrir l'app en tapant sur la notification

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est connecté
## 1. Passage automatique après 2 secondes (mode standard)

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu "A" en mode standard

<span style="color: #FF9800">**Quand**</span> la lecture se termine naturellement

<span style="color: #4CAF50">**Alors**</span> un timer de 2 secondes démarre
<span style="color: #9E9E9E">**Et**</span> un overlay s'affiche: "Contenu suivant dans 2s..."
<span style="color: #9E9E9E">**Et**</span> une barre de décompte visuelle s'affiche

<span style="color: #FF9800">**Quand**</span> le timer atteint 0

<span style="color: #4CAF50">**Alors**</span> le contenu "B" démarre automatiquement
<span style="color: #9E9E9E">**Et**</span> l'overlay disparaît

---

## 2. Passage automatique après 1 seconde (mode Kids)

<span style="color: #2196F3">**Étant donné**</span> que je suis en mode Kids
<span style="color: #9E9E9E">**Et**</span> que j'écoute un contenu pour enfants

<span style="color: #FF9800">**Quand**</span> la lecture se termine

<span style="color: #4CAF50">**Alors**</span> un timer de 1 seconde démarre
<span style="color: #9E9E9E">**Et**</span> le message "Contenu suivant dans 1s..." s'affiche

<span style="color: #FF9800">**Quand**</span> le timer expire

<span style="color: #4CAF50">**Alors**</span> le contenu suivant démarre automatiquement

---

## 3. Passage immédiat après une radio live (0 seconde)

<span style="color: #2196F3">**Étant donné**</span> que j'écoute une radio live

<span style="color: #FF9800">**Quand**</span> le créateur arrête la diffusion

<span style="color: #4CAF50">**Alors**</span> le passage au contenu suivant est immédiat (0s de délai)
<span style="color: #9E9E9E">**Et**</span> aucun overlay de décompte n'est affiché
<span style="color: #9E9E9E">**Et**</span> la transition est fluide

---

## 4. Annulation du passage automatique

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu se termine
<span style="color: #9E9E9E">**Et**</span> que le timer de 2 secondes démarre

<span style="color: #FF9800">**Quand**</span> je clique sur "Rester sur ce contenu" pendant le décompte

<span style="color: #4CAF50">**Alors**</span> le timer est annulé
<span style="color: #9E9E9E">**Et**</span> le contenu actuel reste en pause à la fin
<span style="color: #9E9E9E">**Et**</span> le contenu suivant n'est pas lancé

---

## 5. Insertion de publicité pendant le délai de transition

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté 4 contenus sans publicité
<span style="color: #9E9E9E">**Et**</span> que le 5ème contenu se termine

<span style="color: #FF9800">**Quand**</span> le délai de 2 secondes démarre

<span style="color: #4CAF50">**Alors**</span> une publicité s'insère dans la file d'attente
<span style="color: #9E9E9E">**Et**</span> le message devient "Publicité (15s)"
<span style="color: #9E9E9E">**Et**</span> la publicité démarre après les 2 secondes
<span style="color: #9E9E9E">**Et**</span> elle ne coupe jamais un contenu en cours

---

## 6. Fréquence de publicité paramétrable admin

<span style="color: #2196F3">**Étant donné**</span> que la fréquence pub est configurée à "1/5 contenus"

<span style="color: #FF9800">**Quand**</span> j'écoute 10 contenus

<span style="color: #4CAF50">**Alors**</span> 2 publicités sont insérées (après les contenus 5 et 10)

<span style="color: #2196F3">**Étant donné**</span> que l'admin change la fréquence à "1/3 contenus"

<span style="color: #FF9800">**Quand**</span> j'écoute 9 contenus

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30 secondes démarre
<span style="color: #9E9E9E">**Et**</span> que le délai minimal de visionnage est configuré à 5 secondes

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 3 secondes

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" n'est pas encore visible

<span style="color: #FF9800">**Quand**</span> j'atteins 5 secondes d'écoute

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" apparaît
<span style="color: #9E9E9E">**Et**</span> je peux cliquer pour passer au contenu suivant

---

## 8. Délai minimal de publicité paramétrable admin

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité démarre
<span style="color: #9E9E9E">**Et**</span> que l'admin a configuré le délai à 10 secondes

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 9 secondes

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" n'est pas visible

<span style="color: #FF9800">**Quand**</span> j'atteins 10 secondes

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" apparaît
<span style="color: #9E9E9E">**Et**</span> je peux skipper la publicité

---

## 9. Like et abonnement autorisés sur une publicité

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité est en lecture

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton cœur (véhicule arrêté)

<span style="color: #4CAF50">**Alors**</span> la publicité reçoit un like (+2% jauges tags pub)

<span style="color: #FF9800">**Quand**</span> je clique sur "S'abonner" au créateur de la pub

<span style="color: #4CAF50">**Alors**</span> je suis abonné (+5% jauges tags créateur)
<span style="color: #9E9E9E">**Et**</span> le créateur de pub bénéficie de l'engagement

---

## 10. Métriques d'engagement publicité trackées

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30s est diffusée à 100 auditeurs

<span style="color: #FF9800">**Quand**</span> 40 auditeurs écoutent entièrement (30s)
<span style="color: #9E9E9E">**Et**</span> que 50 auditeurs skippent après 10s
<span style="color: #9E9E9E">**Et**</span> que 10 auditeurs skippent avant 5s

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que la file d'attente est vide
<span style="color: #9E9E9E">**Et**</span> qu'aucun contenu n'est disponible dans ma zone

<span style="color: #FF9800">**Quand**</span> le contenu actuel se termine

<span style="color: #4CAF50">**Alors**</span> un message s'affiche: "Aucun contenu disponible dans cette zone"
<span style="color: #9E9E9E">**Et**</span> une proposition apparaît: "Élargir la zone de recherche ?"
<span style="color: #9E9E9E">**Et**</span> un bouton "Élargir" est disponible
<span style="color: #9E9E9E">**Et**</span> la lecture se met en pause automatiquement

---

## 12. Élargissement automatique de la zone de recherche

<span style="color: #2196F3">**Étant donné**</span> que le message "Aucun contenu disponible" s'affiche

<span style="color: #FF9800">**Quand**</span> je clique sur "Élargir la zone"

<span style="color: #4CAF50">**Alors**</span> l'algorithme relance une recherche avec rayon +50km
<span style="color: #9E9E9E">**Et**</span> une notification "Recherche élargie à 50km" s'affiche
<span style="color: #9E9E9E">**Et**</span> la file d'attente est recalculée
<span style="color: #9E9E9E">**Et**</span> la lecture reprend automatiquement

---

## 13. Refus d'élargissement laisse en pause

<span style="color: #2196F3">**Étant donné**</span> que le message "Aucun contenu disponible" s'affiche

<span style="color: #FF9800">**Quand**</span> je clique sur "Annuler"

<span style="color: #4CAF50">**Alors**</span> la lecture reste en pause
<span style="color: #9E9E9E">**Et**</span> l'interface affiche "En attente de contenu"
<span style="color: #9E9E9E">**Et**</span> je peux manuellement naviguer ou chercher du contenu

---

## 14. Retry avec backoff exponentiel en cas d'échec réseau

<span style="color: #2196F3">**Étant donné**</span> que le contenu suivant échoue au chargement

<span style="color: #FF9800">**Quand**</span> la première tentative échoue

<span style="color: #4CAF50">**Alors**</span> le système retente après 1 seconde (backoff 1s)

<span style="color: #FF9800">**Quand**</span> la 2ème tentative échoue

<span style="color: #4CAF50">**Alors**</span> le système retente après 2 secondes (backoff 2s)

<span style="color: #FF9800">**Quand**</span> la 3ème tentative échoue

<span style="color: #4CAF50">**Alors**</span> le système retente après 4 secondes (backoff 4s)
<span style="color: #9E9E9E">**Et**</span> après 3 échecs totaux, le système bascule en mode offline

---

## 15. Basculement mode offline après 3 échecs réseau

<span style="color: #2196F3">**Étant donné**</span> que j'ai eu 3 échecs de chargement consécutifs

<span style="color: #FF9800">**Quand**</span> le 3ème échec se produit

<span style="color: #4CAF50">**Alors**</span> un message "Connexion instable, basculement mode offline" s'affiche
<span style="color: #9E9E9E">**Et**</span> la lecture continue avec les contenus téléchargés uniquement
<span style="color: #9E9E9E">**Et**</span> les contenus en ligne sont temporairement désactivés

<span style="color: #FF9800">**Quand**</span> la connexion revient

<span style="color: #4CAF50">**Alors**</span> le mode en ligne est automatiquement rétabli

---

## 16. Overlay de décompte avec barre visuelle

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu se termine

<span style="color: #FF9800">**Quand**</span> le timer de 2 secondes démarre

<span style="color: #4CAF50">**Alors**</span> un overlay semi-transparent s'affiche en bas de l'écran
<span style="color: #9E9E9E">**Et**</span> le texte "Contenu suivant dans 2s..." est visible
<span style="color: #9E9E9E">**Et**</span> une barre de progression décroît de 100% à 0% en 2 secondes
<span style="color: #9E9E9E">**Et**</span> la couleur de la barre passe de vert à orange
<span style="color: #9E9E9E">**Et**</span> l'overlay disparaît automatiquement après le décompte

---

## 17. Bouton "Rester sur ce contenu" pendant décompte

<span style="color: #2196F3">**Étant donné**</span> que le décompte de 2 secondes est actif

<span style="color: #FF9800">**Quand**</span> l'overlay s'affiche

<span style="color: #4CAF50">**Alors**</span> un bouton "Rester sur ce contenu" est visible
<span style="color: #9E9E9E">**Et**</span> il est cliquable pendant les 2 secondes

<span style="color: #FF9800">**Quand**</span> je clique dessus

<span style="color: #4CAF50">**Alors**</span> le timer est annulé immédiatement
<span style="color: #9E9E9E">**Et**</span> l'overlay disparaît
<span style="color: #9E9E9E">**Et**</span> le contenu actuel reste affiché en pause

---

## 18. Pas d'interruption d'un contenu en cours

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu de 10 minutes
<span style="color: #9E9E9E">**Et**</span> que je suis à 5 minutes de lecture

<span style="color: #FF9800">**Quand**</span> une publicité devrait s'insérer (fréquence 1/5)

<span style="color: #4CAF50">**Alors**</span> la publicité n'interrompt jamais le contenu en cours
<span style="color: #9E9E9E">**Et**</span> elle attend la fin du contenu actuel
<span style="color: #9E9E9E">**Et**</span> elle s'insère pendant le délai de transition (2s)

---

## 19. Publicités uniquement pour utilisateurs gratuits

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> j'écoute 5 contenus

<span style="color: #4CAF50">**Alors**</span> une publicité est insérée après le 5ème contenu

<span style="color: #2196F3">**Étant donné**</span> que je passe en compte Premium

<span style="color: #FF9800">**Quand**</span> j'écoute 100 contenus

<span style="color: #4CAF50">**Alors**</span> aucune publicité n'est insérée
<span style="color: #9E9E9E">**Et**</span> l'enchaînement est direct (2s de transition seulement)

---

## 20. Message clair pour l'utilisateur lors de la publicité

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité va démarrer

<span style="color: #FF9800">**Quand**</span> le délai de transition démarre

<span style="color: #4CAF50">**Alors**</span> le message affiché est: "Publicité (15s)"
<span style="color: #9E9E9E">**Et**</span> la durée totale de la pub est indiquée
<span style="color: #9E9E9E">**Et**</span> l'utilisateur sait qu'il s'agit d'une pub
<span style="color: #9E9E9E">**Et**</span> la transparence est maximale

---

## 21. Transition fluide entre contenus sans coupure

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu se termine
<span style="color: #9E9E9E">**Et**</span> que le suivant est pré-chargé en cache

<span style="color: #FF9800">**Quand**</span> le timer de 2s expire

<span style="color: #4CAF50">**Alors**</span> la transition audio utilise un crossfade de 0.3s
<span style="color: #9E9E9E">**Et**</span> il n'y a aucun blanc ou coupure
<span style="color: #9E9E9E">**Et**</span> l'expérience est fluide

---

## 22. Gestion des erreurs de chargement avec retry

<span style="color: #2196F3">**Étant donné**</span> que le contenu suivant échoue au chargement

<span style="color: #FF9800">**Quand**</span> la 1ère tentative échoue

<span style="color: #4CAF50">**Alors**</span> une notification "Chargement..." s'affiche
<span style="color: #9E9E9E">**Et**</span> le système retente automatiquement

<span style="color: #FF9800">**Quand**</span> la 2ème tentative réussit

<span style="color: #4CAF50">**Alors**</span> la lecture démarre normalement
<span style="color: #9E9E9E">**Et**</span> aucune action utilisateur n'est requise

---

## 23. Mode offline après échecs multiples

<span style="color: #2196F3">**Étant donné**</span> que j'ai 50 contenus téléchargés en mode offline
<span style="color: #9E9E9E">**Et**</span> que j'ai eu 3 échecs réseau consécutifs

<span style="color: #FF9800">**Quand**</span> le mode offline s'active

<span style="color: #4CAF50">**Alors**</span> seuls les contenus téléchargés sont disponibles
<span style="color: #9E9E9E">**Et**</span> un badge "Mode offline" s'affiche en haut de l'écran
<span style="color: #9E9E9E">**Et**</span> la lecture continue sans interruption

---

## 24. Compteur de contenus avant prochaine publicité

<span style="color: #2196F3">**Étant donné**</span> que la fréquence pub est 1/5 contenus
<span style="color: #9E9E9E">**Et**</span> que j'ai écouté 3 contenus depuis la dernière pub

<span style="color: #FF9800">**Quand**</span> je consulte l'interface

<span style="color: #4CAF50">**Alors**</span> un indicateur discret affiche "2 contenus avant pub"
<span style="color: #9E9E9E">**Et**</span> l'utilisateur sait quand attendre la prochaine publicité

---

## 25. 📋 Plan: Délai de transition selon mode

<span style="color: #2196F3">**Étant donné**</span> que je suis en mode <mode>

<span style="color: #FF9800">**Quand**</span> un contenu se termine

<span style="color: #4CAF50">**Alors**</span> le délai de transition est <delai> secondes
<span style="color: #9E9E9E">**Et**</span> le message affiché est <message>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que la fréquence pub est configurée à <frequence>

<span style="color: #FF9800">**Quand**</span> j'écoute <contenus> contenus

<span style="color: #4CAF50">**Alors**</span> <pubs> 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

<span style="color: #2196F3">**Étant donné**</span> que le chargement échoue

<span style="color: #FF9800">**Quand**</span> je suis à la tentative <tentative>

<span style="color: #4CAF50">**Alors**</span> le délai de retry est <delai> secondes

**📊 Exemples de données:**

| tentative | delai |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 4 |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que l'utilisateur "jean@example.com" est connecté
## 1. Bouton partager disponible dans le player en lecture

<span style="color: #2196F3">**Étant donné**</span> que le contenu "Balade à Paris" est en cours de lecture

<span style="color: #FF9800">**Quand**</span> l'utilisateur consulte les contrôles du player

<span style="color: #4CAF50">**Alors**</span> le bouton "Partager" ⬆️ est visible

---

## 2. Bouton partager disponible sur la page profil créateur

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur consulte le profil de "@paris_stories"

<span style="color: #FF9800">**Quand**</span> l'utilisateur consulte un contenu dans la liste

<span style="color: #4CAF50">**Alors**</span> le bouton "Partager" est disponible pour chaque contenu

---

## 3. Bouton partager dans la liste de recherche

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur effectue une recherche "voyage paris"

<span style="color: #FF9800">**Quand**</span> l'utilisateur ouvre le menu contextuel d'un résultat

<span style="color: #4CAF50">**Alors**</span> l'option "Partager" est disponible

---

## 4. Bouton partager dans l'historique personnel

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur consulte son historique d'écoute

<span style="color: #FF9800">**Quand**</span> l'utilisateur sélectionne un contenu de l'historique

<span style="color: #4CAF50">**Alors**</span> le bouton "Partager" est accessible

---

## 5. 📋 Plan: Menu de partage avec options multiples

<span style="color: #2196F3">**Étant donné**</span> que le contenu "<contenu>" est disponible

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur le bouton "Partager"

<span style="color: #4CAF50">**Alors**</span> le menu natif OS s'ouvre
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un contenu avec l'ID "content_12345"

<span style="color: #FF9800">**Quand**</span> l'utilisateur copie le lien de partage

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est installée sur l'appareil
<span style="color: #9E9E9E">**Et**</span> qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur le lien

<span style="color: #4CAF50">**Alors**</span> l'application RoadWave s'ouvre automatiquement
<span style="color: #9E9E9E">**Et**</span> le contenu "content_12345" commence à jouer

---

## 8. Ouverture du lien partagé sans l'application installée (Web player)

<span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave n'est pas installée
<span style="color: #9E9E9E">**Et**</span> qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur le lien

<span style="color: #4CAF50">**Alors**</span> une page web responsive s'affiche
<span style="color: #9E9E9E">**Et**</span> le web player HTML5 est visible
<span style="color: #9E9E9E">**Et**</span> les boutons de téléchargement App Store et Google Play sont affichés

---

## 9. Contenu de la page web de partage

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> la page de partage est affichée

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un contenu "Balade à Paris" par "@paris_stories"

<span style="color: #FF9800">**Quand**</span> la page de partage est générée

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> l'aperçu s'affiche correctement sur WhatsApp
<span style="color: #9E9E9E">**Et**</span> l'aperçu s'affiche correctement sur Facebook
<span style="color: #9E9E9E">**Et**</span> l'aperçu s'affiche correctement sur Twitter

---

## 11. 📋 Plan: Deep linking par plateforme

<span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est installée sur <plateforme>
<span style="color: #9E9E9E">**Et**</span> qu'un lien de partage est ouvert

<span style="color: #FF9800">**Quand**</span> le système détecte l'application

<span style="color: #4CAF50">**Alors**</span> l'application s'ouvre via <mécanisme>

**📊 Exemples de données:**

| plateforme | mécanisme |
|---|---|
| iOS | Universal Links |
| Android | App Links |

---

## 12. Fallback URL scheme pour deep linking

<span style="color: #2196F3">**Étant donné**</span> que les App Links ne fonctionnent pas

<span style="color: #FF9800">**Quand**</span> le système tente d'ouvrir le contenu

<span style="color: #4CAF50">**Alors**</span> l'URL scheme "roadwave://content/content_12345" est utilisé

---

## 13. Badge Premium visible sur le lien partagé

<span style="color: #2196F3">**Étant donné**</span> un contenu Premium "Visite VIP Louvre"

<span style="color: #FF9800">**Quand**</span> l'utilisateur non-premium clique sur le lien partagé

<span style="color: #4CAF50">**Alors**</span> la page web affiche le badge "👑 Contenu Premium"

---

## 14. Preview 30 secondes d'un contenu Premium partagé

<span style="color: #2196F3">**Étant donné**</span> un contenu Premium "Visite VIP Louvre" de 15 minutes
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur non-premium ouvre le lien partagé

<span style="color: #FF9800">**Quand**</span> le player démarre automatiquement

<span style="color: #4CAF50">**Alors**</span> l'audio joue pendant 30 secondes exactement
<span style="color: #9E9E9E">**Et**</span> un fade out de 2 secondes est appliqué
<span style="color: #9E9E9E">**Et**</span> un overlay "Contenu réservé Premium" s'affiche après 32 secondes

---

## 15. Contenu de l'overlay paywall Premium

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu Premium a atteint la limite de 30 secondes

<span style="color: #FF9800">**Quand**</span> l'overlay paywall s'affiche

<span style="color: #4CAF50">**Alors**</span> le texte suivant est visible:

---

## 16. Actions disponibles sur l'overlay Premium

<span style="color: #2196F3">**Étant donné**</span> que l'overlay paywall Premium est affiché

<span style="color: #FF9800">**Quand**</span> l'utilisateur consulte les options

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un contenu Premium partagé
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur a écouté les 30 premières secondes

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "Rejouer"

<span style="color: #4CAF50">**Alors**</span> les 30 premières secondes sont rejouées
<span style="color: #9E9E9E">**Et**</span> cette action est possible de manière illimitée

---

## 18. Tracking des partages Premium

<span style="color: #2196F3">**Étant donné**</span> un créateur "@guide_louvre" avec un contenu Premium

<span style="color: #FF9800">**Quand**</span> son contenu est partagé

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un contenu Premium partagé par "@guide_louvre"

<span style="color: #FF9800">**Quand**</span> un utilisateur s'abonne via le lien partagé

<span style="color: #4CAF50">**Alors**</span> le créateur reçoit 70% des revenus de cet abonnement
<span style="color: #9E9E9E">**Et**</span> la conversion est trackée dans son dashboard

---

## 20. Partage d'un contenu supprimé

<span style="color: #2196F3">**Étant donné**</span> qu'un lien de partage "https://roadwave.fr/share/c/deleted_content" est ouvert
<span style="color: #9E9E9E">**Et**</span> que le contenu n'existe plus

<span style="color: #FF9800">**Quand**</span> la page web se charge

<span style="color: #4CAF50">**Alors**</span> un message "Ce contenu n'est plus disponible" s'affiche
<span style="color: #9E9E9E">**Et**</span> les boutons de téléchargement de l'app sont affichés

---

## 21. Partage d'un contenu en attente de modération

<span style="color: #2196F3">**Étant donné**</span> un contenu en cours de validation modération

<span style="color: #FF9800">**Quand**</span> un lien de partage est ouvert

<span style="color: #4CAF50">**Alors**</span> le message "Ce contenu est en cours de validation" s'affiche

---

## 22. Génération du lien hors connexion

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur n'a pas de connexion réseau

<span style="color: #FF9800">**Quand**</span> l'utilisateur tente de partager un contenu

<span style="color: #4CAF50">**Alors**</span> le lien est copié dans le presse-papiers
<span style="color: #9E9E9E">**Et**</span> un message "Lien copié (nécessite connexion pour ouvrir)" s'affiche

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis connecté à l'application RoadWave
## 1. Utilisateur gratuit voit 1 publicité tous les 5 contenus

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> j'écoute ma file de contenus

<span style="color: #4CAF50">**Alors**</span> je vois une publicité tous les 5 contenus
<span style="color: #9E9E9E">**Et**</span> la publicité dure 30 secondes en moyenne
<span style="color: #9E9E9E">**Et**</span> je ne peux pas la skip

---

## 2. Utilisateur Premium ne voit aucune publicité

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium

<span style="color: #FF9800">**Quand**</span> j'écoute mes contenus

<span style="color: #4CAF50">**Alors**</span> aucune publicité n'est diffusée
<span style="color: #9E9E9E">**Et**</span> je passe directement d'un contenu à l'autre
<span style="color: #9E9E9E">**Et**</span> l'expérience d'écoute est fluide et ininterrompue

---

## 3. Badge "0 publicité" sur page Premium

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page des avantages Premium

<span style="color: #FF9800">**Quand**</span> je lis la liste des avantages

<span style="color: #4CAF50">**Alors**</span> je vois en premier:
<span style="color: #9E9E9E">**Et**</span> c'est l'argument principal mis en avant

---

## 4. Utilisateur gratuit voit contenus Premium bloqués

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> je consulte les contenus d'un créateur

<span style="color: #4CAF50">**Alors**</span> je vois les contenus marqués Premium avec badge 👑
<span style="color: #F44336">**Mais**</span> je ne peux pas les lire (overlay bloquant)

---

## 5. Utilisateur Premium accède à tous les contenus exclusifs

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium

<span style="color: #FF9800">**Quand**</span> je consulte les contenus d'un créateur

<span style="color: #4CAF50">**Alors**</span> tous les contenus Premium sont accessibles
<span style="color: #9E9E9E">**Et**</span> je peux les lire sans restriction
<span style="color: #9E9E9E">**Et**</span> j'ai accès à 100% du catalogue (gratuit + Premium)

---

## 6. Nombre de contenus Premium disponibles

<span style="color: #2196F3">**Étant donné**</span> que je suis Premium

<span style="color: #FF9800">**Quand**</span> je consulte les statistiques

<span style="color: #4CAF50">**Alors**</span> je vois combien de contenus Premium sont disponibles sur la plateforme
<span style="color: #9E9E9E">**Et**</span> par exemple: "8,547 contenus Premium exclusifs disponibles"
<span style="color: #9E9E9E">**Et**</span> cela justifie la valeur de l'abonnement

---

## 7. Utilisateur gratuit écoute en 48 kbps Opus

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> je lance un contenu

<span style="color: #4CAF50">**Alors**</span> l'audio est streamé en 48 kbps Opus
<span style="color: #9E9E9E">**Et**</span> cela consomme environ 20 MB/heure
<span style="color: #9E9E9E">**Et**</span> la qualité est très correcte pour de la voix

---

## 8. Utilisateur Premium écoute en 64 kbps Opus

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium

<span style="color: #FF9800">**Quand**</span> je lance un contenu

<span style="color: #4CAF50">**Alors**</span> l'audio est streamé en 64 kbps Opus
<span style="color: #9E9E9E">**Et**</span> cela consomme environ 30 MB/heure
<span style="color: #9E9E9E">**Et**</span> la qualité est excellente (détails audio supérieurs)

---

## 9. Comparaison qualité 48 kbps vs 64 kbps

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page Premium

<span style="color: #FF9800">**Quand**</span> je lis la section qualité audio

<span style="color: #4CAF50">**Alors**</span> je vois l'explication:

---

## 10. Justification 48 kbps suffisant pour gratuit

<span style="color: #2196F3">**Étant donné**</span> que le contenu RoadWave est principalement de la voix

<span style="color: #FF9800">**Quand**</span> la qualité est fixée à 48 kbps pour gratuit

<span style="color: #4CAF50">**Alors**</span> c'est largement suffisant pour comprendre clairement
<span style="color: #9E9E9E">**Et**</span> équivalent à la qualité radio FM
<span style="color: #9E9E9E">**Et**</span> les utilisateurs gratuits ne sont pas frustrés

---

## 11. Justification 64 kbps avantage tangible Premium

<span style="color: #2196F3">**Étant donné**</span> que les audiophiles et créateurs audio sont exigeants

<span style="color: #FF9800">**Quand**</span> la qualité Premium est à 64 kbps

<span style="color: #4CAF50">**Alors**</span> la différence est perceptible à l'oreille
<span style="color: #9E9E9E">**Et**</span> les ambiances, musiques de fond, nuances de voix sont mieux rendues
<span style="color: #9E9E9E">**Et**</span> cela justifie l'abonnement Premium

---

## 12. Switch automatique qualité selon abonnement

<span style="color: #2196F3">**Étant donné**</span> que je suis gratuit et j'écoute en 48 kbps

<span style="color: #FF9800">**Quand**</span> je souscris à Premium

<span style="color: #4CAF50">**Alors**</span> dès le contenu suivant, je passe automatiquement en 64 kbps
<span style="color: #9E9E9E">**Et**</span> je peux entendre la différence de qualité immédiatement

---

## 13. Consommation data Premium vs Gratuit

<span style="color: #2196F3">**Étant donné**</span> que je roule 1 heure par jour

<span style="color: #FF9800">**Quand**</span> je calcule la consommation mensuelle

<span style="color: #4CAF50">**Alors**</span> en gratuit: 20 MB/h × 1h × 22 jours = 440 MB/mois
<span style="color: #9E9E9E">**Et**</span> en Premium: 30 MB/h × 1h × 22 jours = 660 MB/mois
<span style="color: #9E9E9E">**Et**</span> la différence est de 220 MB/mois (acceptable pour 4G/5G illimitée)

---

## 14. Utilisateur gratuit limité à 50 contenus téléchargés

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> j'accède au mode offline

<span style="color: #4CAF50">**Alors**</span> je peux télécharger jusqu'à 50 contenus maximum
<span style="color: #9E9E9E">**Et**</span> si j'essaie de télécharger un 51ème, je vois:

---

## 15. Utilisateur Premium téléchargements illimités

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium

<span style="color: #FF9800">**Quand**</span> j'accède au mode offline

<span style="color: #4CAF50">**Alors**</span> je peux télécharger autant de contenus que je veux
<span style="color: #9E9E9E">**Et**</span> la seule limite est l'espace de stockage de mon device
<span style="color: #9E9E9E">**Et**</span> par exemple 500 contenus × 10 MB = 5 GB

---

## 16. Justification limite 50 contenus gratuit

<span style="color: #2196F3">**Étant donné**</span> que 50 contenus de 10 minutes = ~8 heures d'écoute

<span style="color: #FF9800">**Quand**</span> un utilisateur gratuit prépare un road trip

<span style="color: #4CAF50">**Alors**</span> 8 heures couvrent largement une journée de trajet
<span style="color: #9E9E9E">**Et**</span> cela permet un usage offline raisonnable sans abuser

---

## 17. Justification illimité Premium pour longs road trips

<span style="color: #2196F3">**Étant donné**</span> qu'un road trip de plusieurs jours nécessite 20-50h de contenu

<span style="color: #FF9800">**Quand**</span> un utilisateur Premium télécharge 200 contenus

<span style="color: #4CAF50">**Alors**</span> il peut partir serein sans connexion internet pendant 1 semaine
<span style="color: #9E9E9E">**Et**</span> cela justifie pleinement l'abonnement Premium

---

## 18. Affichage compteur téléchargements gratuit

<span style="color: #2196F3">**Étant donné**</span> que je suis gratuit et j'ai téléchargé 37 contenus

<span style="color: #FF9800">**Quand**</span> j'accède à la page Téléchargements

<span style="color: #4CAF50">**Alors**</span> je vois:

---

## 19. Pas de compteur pour Premium

<span style="color: #2196F3">**Étant donné**</span> que je suis Premium et j'ai téléchargé 187 contenus

<span style="color: #FF9800">**Quand**</span> j'accède à la page Téléchargements

<span style="color: #4CAF50">**Alors**</span> je vois simplement:
<span style="color: #9E9E9E">**Et**</span> aucune limite n'est affichée

---

## 20. Utilisateur gratuit historique limité à 100 derniers

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> j'accède à mon historique d'écoute

<span style="color: #4CAF50">**Alors**</span> je vois les 100 derniers contenus écoutés
<span style="color: #9E9E9E">**Et**</span> les contenus plus anciens ne sont pas affichés
<span style="color: #9E9E9E">**Et**</span> je vois un message "Historique limité à 100 contenus. Passez Premium pour un historique illimité."

---

## 21. Utilisateur Premium historique illimité

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium

<span style="color: #FF9800">**Quand**</span> j'accède à mon historique d'écoute

<span style="color: #4CAF50">**Alors**</span> je vois tous les contenus que j'ai écoutés depuis mon inscription
<span style="color: #9E9E9E">**Et**</span> je peux scroller jusqu'au premier contenu jamais écouté
<span style="color: #9E9E9E">**Et**</span> l'historique est complet et permanent

---

## 22. Recherche dans historique Premium

<span style="color: #2196F3">**Étant donné**</span> que je suis Premium et j'ai 2 000 contenus dans mon historique

<span style="color: #FF9800">**Quand**</span> je recherche "Tesla" dans mon historique

<span style="color: #4CAF50">**Alors**</span> tous les contenus écoutés contenant "Tesla" sont affichés
<span style="color: #9E9E9E">**Et**</span> je peux retrouver facilement un contenu écouté il y a 6 mois

---

## 23. Justification limite 100 gratuit suffisante

<span style="color: #2196F3">**Étant donné**</span> que 100 contenus de 10 min = ~16 heures d'écoute

<span style="color: #FF9800">**Quand**</span> un utilisateur gratuit écoute 1h/jour

<span style="color: #4CAF50">**Alors**</span> l'historique couvre les 16 derniers jours
<span style="color: #9E9E9E">**Et**</span> cela suffit pour retrouver un contenu récent

---

## 24. Justification illimité Premium pour power users

<span style="color: #2196F3">**Étant donné**</span> qu'un power user écoute 3h/jour depuis 2 ans

<span style="color: #FF9800">**Quand**</span> il veut retrouver un contenu spécifique écouté il y a 1 an

<span style="color: #4CAF50">**Alors**</span> l'historique illimité Premium lui permet de retrouver ce contenu
<span style="color: #9E9E9E">**Et**</span> cela apporte une vraie valeur ajoutée

---

## 25. Export historique complet (Premium uniquement)

<span style="color: #2196F3">**Étant donné**</span> que je suis Premium

<span style="color: #FF9800">**Quand**</span> je demande l'export de mes données

<span style="color: #4CAF50">**Alors**</span> l'historique complet est inclus dans l'export:

---

## 26. Affichage tableau comparatif Gratuit vs Premium

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page Premium

<span style="color: #FF9800">**Quand**</span> je vois le tableau comparatif

<span style="color: #4CAF50">**Alors**</span> il affiche:

---

## 27. Justification 0 pub = argument principal

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30s tous les 5 contenus = 6 min/h de pub

<span style="color: #FF9800">**Quand**</span> un utilisateur écoute 1h/jour

<span style="color: #4CAF50">**Alors**</span> il subit 180 min de pub/mois (3 heures !)
<span style="color: #9E9E9E">**Et**</span> payer 4.99€ pour éviter 3h de pub/mois est très rentable
<span style="color: #9E9E9E">**Et**</span> c'est l'argument de conversion n°1

---

## 28. Justification qualité audio avantage tangible

<span style="color: #2196F3">**Étant donné**</span> que la différence 48 kbps → 64 kbps est audible

<span style="color: #FF9800">**Quand**</span> un audiophile compare les deux

<span style="color: #4CAF50">**Alors**</span> il entend clairement la différence sur un bon système audio voiture
<span style="color: #9E9E9E">**Et**</span> cela justifie l'abonnement pour les exigeants

---

## 29. Justification offline illimité pour road trips

<span style="color: #2196F3">**Étant donné**</span> qu'un road trip de 2 semaines nécessite 50-100h de contenu

<span style="color: #FF9800">**Quand**</span> un utilisateur Premium télécharge 300 contenus avant de partir

<span style="color: #4CAF50">**Alors**</span> il peut partir en zone sans réseau sereinement
<span style="color: #9E9E9E">**Et**</span> cela apporte une vraie valeur pratique

---

## 30. Justification pas d'over-engineering

<span style="color: #2196F3">**Étant donné**</span> que RoadWave se concentre sur l'essentiel

<span style="color: #FF9800">**Quand**</span> les avantages Premium sont définis

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> cela réduit la complexité et le coût de développement

---

## 31. CTA Premium après 5ème publicité

<span style="color: #2196F3">**Étant donné**</span> que je suis gratuit et je viens d'entendre ma 5ème pub

<span style="color: #FF9800">**Quand**</span> la publicité se termine

<span style="color: #4CAF50">**Alors**</span> je vois un message:

---

## 32. CTA Premium quand limite 50 téléchargements atteinte

<span style="color: #2196F3">**Étant donné**</span> que je suis gratuit et j'ai atteint 50 téléchargements

<span style="color: #FF9800">**Quand**</span> j'essaie de télécharger un 51ème contenu

<span style="color: #4CAF50">**Alors**</span> je vois une popup:

---

## 33. CTA Premium quand contenu exclusif bloqué

<span style="color: #2196F3">**Étant donné**</span> que je suis gratuit et je clique sur un contenu Premium

<span style="color: #FF9800">**Quand**</span> l'overlay bloquant apparaît

<span style="color: #4CAF50">**Alors**</span> je vois:

---

## 34. Statistiques conversion - Quel avantage convertit le mieux ?

<span style="color: #2196F3">**Étant donné**</span> qu'un admin consulte les statistiques de conversion

<span style="color: #FF9800">**Quand**</span> il analyse les sources de conversion

<span style="color: #4CAF50">**Alors**</span> 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% |

<span style="color: #9E9E9E">**Et**</span> cela aide à optimiser le placement des CTA

---

## 35. A/B test message CTA

<span style="color: #2196F3">**Étant donné**</span> que RoadWave veut optimiser les conversions

<span style="color: #FF9800">**Quand**</span> un A/B test est lancé sur le CTA après pub

<span style="color: #4CAF50">**Alors**</span> groupe A voit "Marre des pubs ?" (focus négatif)
<span style="color: #9E9E9E">**Et**</span> groupe B voit "Profitez de 0 publicité" (focus positif)
<span style="color: #9E9E9E">**Et**</span> le taux de conversion est mesuré
<span style="color: #9E9E9E">**Et**</span> le message le plus performant est déployé

---

## 36. Notification Premium après 30 jours d'utilisation gratuite

<span style="color: #2196F3">**Étant donné**</span> que je suis utilisateur gratuit depuis 30 jours
<span style="color: #9E9E9E">**Et**</span> que j'écoute régulièrement (15h cumulées)

<span style="color: #FF9800">**Quand**</span> le 30ème jour arrive

<span style="color: #4CAF50">**Alors**</span> je reçois une notification:

---

## 37. Trial gratuit refusé mais onboarding amélioré

<span style="color: #2196F3">**Étant donné**</span> qu'il n'y a pas de trial gratuit

<span style="color: #FF9800">**Quand**</span> un nouvel utilisateur s'inscrit

<span style="color: #4CAF50">**Alors**</span> un onboarding explique clairement les avantages Premium
<span style="color: #9E9E9E">**Et**</span> il peut comparer gratuit vs Premium dès le premier lancement
<span style="color: #9E9E9E">**Et**</span> cela l'aide à décider rapidement s'il veut payer

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis connecté à l'application RoadWave
## 1. Souscription via Web (desktop/mobile) avec Mangopay

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page Premium sur le site web

<span style="color: #FF9800">**Quand**</span> je clique sur "S'abonner - Mensuel 4.99€"

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers le formulaire de paiement Mangopay
<span style="color: #9E9E9E">**Et**</span> je saisis mes informations de carte bancaire
<span style="color: #9E9E9E">**Et**</span> le paiement de 4.99€ est prélevé immédiatement
<span style="color: #9E9E9E">**Et**</span> la commission Mangopay est de 1.8% + 0.18€ = 0.27€
<span style="color: #9E9E9E">**Et**</span> RoadWave reçoit 4.72€ net

---

## 2. Calcul commission Mangopay

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur paie 4.99€ via Mangopay

<span style="color: #FF9800">**Quand**</span> la commission est calculée

<span style="color: #4CAF50">**Alors**</span> la commission est : 4.99€ × 1.8% + 0.18€ = 0.09€ + 0.18€ = 0.27€
<span style="color: #9E9E9E">**Et**</span> RoadWave reçoit : 4.99€ - 0.27€ = 4.72€
<span style="color: #9E9E9E">**Et**</span> la commission représente 5.4% du prix

---

## 3. Souscription via iOS App avec Apple IAP

<span style="color: #2196F3">**Étant donné**</span> que j'utilise l'app iOS

<span style="color: #FF9800">**Quand**</span> je clique sur "S'abonner - Mensuel 5.99€"

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers l'interface Apple In-App Purchase
<span style="color: #9E9E9E">**Et**</span> le prix affiché est 5.99€ (majoré de 20%)
<span style="color: #9E9E9E">**Et**</span> le paiement est effectué via mon compte Apple
<span style="color: #9E9E9E">**Et**</span> Apple prend 30% de commission = 1.80€
<span style="color: #9E9E9E">**Et**</span> RoadWave reçoit 4.19€ net

---

## 4. Souscription via Android App avec Google Play Billing

<span style="color: #2196F3">**Étant donné**</span> que j'utilise l'app Android

<span style="color: #FF9800">**Quand**</span> je clique sur "S'abonner - Mensuel 5.99€"

<span style="color: #4CAF50">**Alors**</span> je suis redirigé vers l'interface Google Play Billing
<span style="color: #9E9E9E">**Et**</span> le prix affiché est 5.99€ (majoré de 20%)
<span style="color: #9E9E9E">**Et**</span> le paiement est effectué via mon compte Google
<span style="color: #9E9E9E">**Et**</span> Google prend 30% de commission = 1.80€
<span style="color: #9E9E9E">**Et**</span> RoadWave reçoit 4.19€ net

---

## 5. Majoration 20% sur mobile pour compenser commission 30%

<span style="color: #2196F3">**Étant donné**</span> que Apple/Google prennent 30% de commission

<span style="color: #FF9800">**Quand**</span> RoadWave fixe le prix mobile

<span style="color: #4CAF50">**Alors**</span> le prix web est 4.99€ (commission Mangopay 5.4%)
<span style="color: #9E9E9E">**Et**</span> le prix mobile est 5.99€ (commission Apple/Google 30%)
<span style="color: #9E9E9E">**Et**</span> la majoration est de 1€ (+20%)
<span style="color: #9E9E9E">**Et**</span> cela compense partiellement la commission excessive

---

## 6. Email incitation souscription web moins chère

<span style="color: #2196F3">**Étant donné**</span> que je consulte Premium depuis l'app mobile

<span style="color: #FF9800">**Quand**</span> je vois le prix 5.99€

<span style="color: #4CAF50">**Alors**</span> je vois aussi un message:
<span style="color: #9E9E9E">**Et**</span> un lien vers le site web est fourni

---

## 7. Calcul économie souscription web vs mobile

<span style="color: #2196F3">**Étant donné**</span> que le prix web est 4.99€/mois
<span style="color: #9E9E9E">**Et**</span> que le prix mobile est 5.99€/mois

<span style="color: #FF9800">**Quand**</span> je calcule l'économie annuelle

<span style="color: #4CAF50">**Alors**</span> web : 4.99€ × 12 = 59.88€/an
<span style="color: #9E9E9E">**Et**</span> mobile : 5.99€ × 12 = 71.88€/an
<span style="color: #9E9E9E">**Et**</span> économie : 12€/an (soit 20% d'économie)

---

## 8. Activation immédiate après paiement réussi

<span style="color: #2196F3">**Étant donné**</span> que je viens de payer mon abonnement Premium

<span style="color: #FF9800">**Quand**</span> le paiement est confirmé

<span style="color: #4CAF50">**Alors**</span> mon statut passe immédiatement à "Premium"
<span style="color: #9E9E9E">**Et**</span> je peux accéder aux avantages Premium dès maintenant
<span style="color: #9E9E9E">**Et**</span> je reçois un email de confirmation

---

## 9. Email confirmation souscription

<span style="color: #2196F3">**Étant donné**</span> que j'ai souscrit à Premium

<span style="color: #FF9800">**Quand**</span> la souscription est confirmée

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 10. Email rappel 7 jours avant renouvellement

<span style="color: #2196F3">**Étant donné**</span> que mon abonnement mensuel se renouvelle le 15 juillet

<span style="color: #FF9800">**Quand**</span> le 8 juillet arrive (7 jours avant)

<span style="color: #4CAF50">**Alors**</span> je reçois un email de rappel:

---

## 11. Renouvellement automatique réussi

<span style="color: #2196F3">**Étant donné**</span> que mon abonnement mensuel arrive à échéance le 15 juillet

<span style="color: #FF9800">**Quand**</span> le 15 juillet arrive

<span style="color: #4CAF50">**Alors**</span> Mangopay/Apple/Google prélève automatiquement 4.99€ (ou 5.99€)
<span style="color: #9E9E9E">**Et**</span> mon abonnement est renouvelé pour 1 mois supplémentaire
<span style="color: #9E9E9E">**Et**</span> je reçois un email de confirmation

---

## 12. Email confirmation renouvellement

<span style="color: #2196F3">**Étant donné**</span> que mon abonnement vient d'être renouvelé

<span style="color: #FF9800">**Quand**</span> le paiement est confirmé

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 13. Échec paiement renouvellement - Tentative 1

<span style="color: #2196F3">**Étant donné**</span> que mon abonnement doit se renouveler le 15 juillet
<span style="color: #F44336">**Mais**</span> que ma carte bancaire est expirée ou sans fonds

<span style="color: #FF9800">**Quand**</span> le prélèvement échoue

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 14. Retry automatique paiement après 3 jours

<span style="color: #2196F3">**Étant donné**</span> que le paiement a échoué le 15 juillet

<span style="color: #FF9800">**Quand**</span> le 18 juillet arrive (J+3)

<span style="color: #4CAF50">**Alors**</span> Mangopay/Apple/Google tente automatiquement un nouveau prélèvement
<span style="color: #9E9E9E">**Et**</span> si le paiement réussit, l'abonnement est renouvelé normalement
<span style="color: #9E9E9E">**Et**</span> si le paiement échoue encore, un 2ème retry est programmé

---

## 15. Retry automatique paiement après 7 jours

<span style="color: #2196F3">**Étant donné**</span> que 2 tentatives ont échoué (15 juillet et 18 juillet)

<span style="color: #FF9800">**Quand**</span> le 22 juillet arrive (J+7)

<span style="color: #4CAF50">**Alors**</span> une 3ème et dernière tentative est effectuée
<span style="color: #9E9E9E">**Et**</span> si le paiement réussit, l'abonnement est sauvé
<span style="color: #9E9E9E">**Et**</span> si le paiement échoue, l'abonnement est annulé automatiquement

---

## 16. Annulation automatique après 3 échecs paiement

<span style="color: #2196F3">**Étant donné**</span> que les 3 tentatives de renouvellement ont échoué (J+0, J+3, J+7)

<span style="color: #FF9800">**Quand**</span> la 3ème tentative échoue

<span style="color: #4CAF50">**Alors**</span> mon abonnement Premium est annulé automatiquement
<span style="color: #9E9E9E">**Et**</span> mon statut repasse à "Gratuit"
<span style="color: #9E9E9E">**Et**</span> je perds accès aux avantages Premium
<span style="color: #9E9E9E">**Et**</span> je reçois un email d'annulation

---

## 17. Email annulation automatique pour impayé

<span style="color: #2196F3">**Étant donné**</span> que mon abonnement a été annulé pour échec paiement

<span style="color: #FF9800">**Quand**</span> l'annulation devient effective

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 18. Annulation self-service dans Settings

<span style="color: #2196F3">**Étant donné**</span> que je veux annuler mon abonnement

<span style="color: #FF9800">**Quand**</span> j'accède à "Paramètres > Abonnement"

<span style="color: #4CAF50">**Alors**</span> je vois un bouton "Annuler l'abonnement"
<span style="color: #9E9E9E">**Et**</span> je peux annuler en 2 clics sans contacter le support

---

## 19. Confirmation avant annulation

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Annuler l'abonnement"

<span style="color: #FF9800">**Quand**</span> une popup de confirmation apparaît

<span style="color: #4CAF50">**Alors**</span> je vois:

---

## 20. Accès Premium maintenu jusqu'à fin période payée

<span style="color: #2196F3">**Étant donné**</span> que j'ai annulé mon abonnement le 1er juillet
<span style="color: #9E9E9E">**Et**</span> que mon abonnement mensuel était valable jusqu'au 15 juillet

<span style="color: #FF9800">**Quand**</span> l'annulation est confirmée

<span style="color: #4CAF50">**Alors**</span> je garde l'accès Premium jusqu'au 15 juillet
<span style="color: #9E9E9E">**Et**</span> à partir du 16 juillet, je repasse en gratuit
<span style="color: #9E9E9E">**Et**</span> je ne suis pas remboursé pour les 14 jours restants

---

## 21. Justification pas de remboursement prorata

<span style="color: #2196F3">**Étant donné**</span> que l'industrie (Spotify, Netflix, YouTube) ne rembourse pas prorata

<span style="color: #FF9800">**Quand**</span> RoadWave applique la même règle

<span style="color: #4CAF50">**Alors**</span> c'est le standard accepté par les utilisateurs
<span style="color: #9E9E9E">**Et**</span> cela simplifie la gestion comptable
<span style="color: #9E9E9E">**Et**</span> évite les abus (souscription puis annulation immédiate pour remboursement)

---

## 22. Email confirmation annulation

<span style="color: #2196F3">**Étant donné**</span> que j'ai annulé mon abonnement

<span style="color: #FF9800">**Quand**</span> l'annulation est enregistrée

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 23. Pas de renouvellement après annulation

<span style="color: #2196F3">**Étant donné**</span> que j'ai annulé mon abonnement le 1er juillet

<span style="color: #FF9800">**Quand**</span> le 15 juillet arrive (date de renouvellement prévue)

<span style="color: #4CAF50">**Alors**</span> aucun prélèvement n'est effectué
<span style="color: #9E9E9E">**Et**</span> mon statut passe automatiquement à "Gratuit"
<span style="color: #9E9E9E">**Et**</span> je ne reçois pas d'email de renouvellement

---

## 24. Réabonnement possible immédiatement

<span style="color: #2196F3">**Étant donné**</span> que j'ai annulé mon abonnement il y a 5 jours

<span style="color: #FF9800">**Quand**</span> j'accède à la page Premium

<span style="color: #4CAF50">**Alors**</span> je peux me réabonner immédiatement
<span style="color: #9E9E9E">**Et**</span> le processus de paiement est le même que la première fois

---

## 25. Pas de nouvelle période d'essai au réabonnement

<span style="color: #2196F3">**Étant donné**</span> que j'ai annulé mon abonnement il y a 3 mois

<span style="color: #FF9800">**Quand**</span> je me réabonne

<span style="color: #4CAF50">**Alors**</span> je paie immédiatement 4.99€ (pas d'essai gratuit)

---

## 26. Offre win-back pour utilisateurs ayant annulé

<span style="color: #2196F3">**Étant donné**</span> que j'ai annulé mon abonnement il y a 1 mois

<span style="color: #FF9800">**Quand**</span> je reçois un email de win-back

<span style="color: #4CAF50">**Alors**</span> je vois une offre spéciale:

---

## 27. Table subscriptions en base PostgreSQL

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur souscrit à Premium

<span style="color: #FF9800">**Quand**</span> les données sont enregistrées

<span style="color: #4CAF50">**Alors**</span> la table subscriptions contient:

---

## 28. Statuts possibles dans subscription.status

<span style="color: #2196F3">**Étant donné**</span> qu'un abonnement peut avoir différents statuts

<span style="color: #FF9800">**Quand**</span> le statut est stocké en base

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur lance un contenu

<span style="color: #FF9800">**Quand**</span> l'app vérifie s'il est Premium

<span style="color: #4CAF50">**Alors**</span> une clé Redis est consultée:
<span style="color: #9E9E9E">**Et**</span> si la clé n'existe pas, elle est recalculée depuis PostgreSQL
<span style="color: #9E9E9E">**Et**</span> cela garantit des performances <10ms

---

## 30. Refresh cache Redis via webhooks

<span style="color: #2196F3">**Étant donné**</span> qu'un paiement est confirmé par Mangopay/Apple/Google

<span style="color: #FF9800">**Quand**</span> un webhook est reçu par RoadWave

<span style="color: #4CAF50">**Alors**</span> le cache Redis premium:{user_id} est mis à jour immédiatement
<span style="color: #9E9E9E">**Et**</span> l'utilisateur voit son statut Premium activé sans délai

---

## 31. Webhooks Mangopay - PAYIN_NORMAL_SUCCEEDED

<span style="color: #2196F3">**Étant donné**</span> qu'un paiement Mangopay réussit

<span style="color: #FF9800">**Quand**</span> Mangopay envoie le webhook PAYIN_NORMAL_SUCCEEDED

<span style="color: #4CAF50">**Alors**</span> RoadWave met à jour subscriptions.status = 'active'
<span style="color: #9E9E9E">**Et**</span> met à jour current_period_end = NOW() + 1 mois
<span style="color: #9E9E9E">**Et**</span> refresh le cache Redis premium:{user_id} = true

---

## 32. Webhooks Mangopay - PAYIN_NORMAL_FAILED

<span style="color: #2196F3">**Étant donné**</span> qu'un paiement Mangopay échoue

<span style="color: #FF9800">**Quand**</span> Mangopay envoie le webhook PAYIN_NORMAL_FAILED

<span style="color: #4CAF50">**Alors**</span> RoadWave met à jour subscriptions.status = 'past_due'
<span style="color: #9E9E9E">**Et**</span> programme un retry automatique dans 3 jours
<span style="color: #9E9E9E">**Et**</span> envoie un email à l'utilisateur

---

## 33. Webhooks Apple - App Store Server Notifications

<span style="color: #2196F3">**Étant donné**</span> qu'un paiement Apple IAP change de statut

<span style="color: #FF9800">**Quand**</span> Apple envoie une notification serveur

<span style="color: #4CAF50">**Alors**</span> RoadWave parse la notification (JSON)
<span style="color: #9E9E9E">**Et**</span> met à jour la subscription en conséquence
<span style="color: #9E9E9E">**Et**</span> refresh le cache Redis

---

## 34. Webhooks Google - Real-time Developer Notifications

<span style="color: #2196F3">**Étant donné**</span> qu'un paiement Google Play change de statut

<span style="color: #FF9800">**Quand**</span> Google envoie une notification temps réel

<span style="color: #4CAF50">**Alors**</span> RoadWave parse la notification (JSON)
<span style="color: #9E9E9E">**Et**</span> met à jour la subscription en conséquence
<span style="color: #9E9E9E">**Et**</span> refresh le cache Redis

---

## 35. Dashboard admin - Métriques abonnements

<span style="color: #2196F3">**Étant donné**</span> qu'un admin consulte les métriques Premium

<span style="color: #FF9800">**Quand**</span> il accède au dashboard

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que 287 utilisateurs ont annulé ce mois
<span style="color: #9E9E9E">**Et**</span> qu'il y avait 12,547 abonnés au début du mois

<span style="color: #FF9800">**Quand**</span> le churn rate est calculé

<span style="color: #4CAF50">**Alors**</span> churn = 287 / 12,547 = 2.3%
<span style="color: #9E9E9E">**Et**</span> un churn <5% est considéré comme excellent
<span style="color: #9E9E9E">**Et**</span> RoadWave surveille cette métrique de près

---

## 37. Alerte si churn rate >5%

<span style="color: #2196F3">**Étant donné**</span> que le churn rate mensuel dépasse 5%

<span style="color: #FF9800">**Quand**</span> le système détecte cette anomalie

<span style="color: #4CAF50">**Alors**</span> une alerte est envoyée à l'équipe:

---

## 38. Enquête satisfaction à l'annulation

<span style="color: #2196F3">**Étant donné**</span> que je viens d'annuler mon abonnement

<span style="color: #FF9800">**Quand**</span> l'annulation est confirmée

<span style="color: #4CAF50">**Alors**</span> je vois un questionnaire rapide:
<span style="color: #9E9E9E">**Et**</span> les réponses aident à améliorer l'offre Premium

---

## 39. Répartition canaux souscription

<span style="color: #2196F3">**Étant donné**</span> qu'un admin analyse les canaux de souscription

<span style="color: #FF9800">**Quand**</span> il consulte les statistiques

<span style="color: #4CAF50">**Alors**</span> 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€ |

<span style="color: #9E9E9E">**Et**</span> cela aide à orienter les efforts marketing (inciter web = moins de commission)

---

## 40. Performance vérification Premium <10ms

<span style="color: #2196F3">**Étant donné**</span> que 100 000 utilisateurs consultent des contenus simultanément

<span style="color: #FF9800">**Quand**</span> chaque requête vérifie le statut Premium via Redis

<span style="color: #4CAF50">**Alors**</span> le temps de réponse moyen est <10ms
<span style="color: #9E9E9E">**Et**</span> Redis gère facilement 100 000 requêtes/seconde
<span style="color: #9E9E9E">**Et**</span> l'expérience utilisateur est fluide

---

## 41. Backup données abonnements

<span style="color: #2196F3">**Étant donné**</span> que les données d'abonnements sont critiques

<span style="color: #FF9800">**Quand**</span> un backup est effectué

<span style="color: #4CAF50">**Alors**</span> PostgreSQL est répliqué en temps réel sur un replica
<span style="color: #9E9E9E">**Et**</span> un snapshot quotidien est stocké sur S3
<span style="color: #9E9E9E">**Et**</span> en cas de crash, les données peuvent être restaurées <5 minutes

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium actif
> <span style="color: #9E9E9E">**Et**</span> que mon compte est valide
## 1. 1 seul stream actif autorisé par compte

<span style="color: #2196F3">**Étant donné**</span> que je n'écoute rien actuellement

<span style="color: #FF9800">**Quand**</span> je lance un contenu sur mon iPhone

<span style="color: #4CAF50">**Alors**</span> le stream démarre normalement
<span style="color: #9E9E9E">**Et**</span> Redis enregistre: active_streams:{user_id} = {device_id: "iPhone", started_at: timestamp}

---

## 2. Détection connexion simultanée - Arrêt premier device

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu sur mon iPhone

<span style="color: #FF9800">**Quand**</span> je lance un contenu sur mon iPad

<span style="color: #4CAF50">**Alors**</span> le système détecte une session active sur iPhone
<span style="color: #9E9E9E">**Et**</span> la lecture sur iPhone est arrêtée immédiatement (WebSocket close)
<span style="color: #9E9E9E">**Et**</span> je vois sur iPhone: "Lecture interrompue : votre compte est utilisé sur un autre appareil"
<span style="color: #9E9E9E">**Et**</span> la lecture démarre sur iPad normalement

---

## 3. Message explicite sur device interrompu

<span style="color: #2196F3">**Étant donné**</span> que ma lecture sur iPhone vient d'être interrompue

<span style="color: #FF9800">**Quand**</span> je regarde l'écran de mon iPhone

<span style="color: #4CAF50">**Alors**</span> je vois une overlay avec le message:
<span style="color: #9E9E9E">**Et**</span> un bouton "Reprendre ici" est disponible

---

## 4. Reprendre lecture sur device interrompu

<span style="color: #2196F3">**Étant donné**</span> que ma lecture sur iPhone a été interrompue
<span style="color: #9E9E9E">**Et**</span> que je veux reprendre sur iPhone

<span style="color: #FF9800">**Quand**</span> je clique sur "Reprendre ici"

<span style="color: #4CAF50">**Alors**</span> la lecture démarre sur iPhone
<span style="color: #9E9E9E">**Et**</span> l'iPad est à son tour interrompu avec le même message
<span style="color: #9E9E9E">**Et**</span> le "ping-pong" entre devices est possible (mais pénible)

---

## 5. Enregistrement session active dans Redis

<span style="color: #2196F3">**Étant donné**</span> que je lance un contenu sur mon iPhone

<span style="color: #FF9800">**Quand**</span> la lecture démarre

<span style="color: #4CAF50">**Alors**</span> une entrée Redis est créée:

---

## 6. Heartbeat toutes les 30 secondes pour maintenir session

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu sur mon iPhone

<span style="color: #FF9800">**Quand**</span> 30 secondes s'écoulent

<span style="color: #4CAF50">**Alors**</span> l'app envoie un heartbeat au serveur
<span style="color: #9E9E9E">**Et**</span> le serveur refresh le TTL Redis à 300 secondes
<span style="color: #9E9E9E">**Et**</span> la session reste active

---

## 7. Session considérée morte après 5 minutes sans heartbeat

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu sur mon iPhone
<span style="color: #F44336">**Mais**</span> que l'app crash ou que le réseau coupe

<span style="color: #FF9800">**Quand**</span> 5 minutes s'écoulent sans heartbeat

<span style="color: #4CAF50">**Alors**</span> l'entrée Redis expire automatiquement (TTL atteint)
<span style="color: #9E9E9E">**Et**</span> je peux relancer sur n'importe quel device sans conflit

---

## 8. Vérification session avant démarrage lecture

<span style="color: #2196F3">**Étant donné**</span> que je veux lancer un contenu sur mon iPad

<span style="color: #FF9800">**Quand**</span> j'appuie sur Play

<span style="color: #4CAF50">**Alors**</span> le serveur vérifie Redis: active_streams:{user_id}
<span style="color: #9E9E9E">**Et**</span> si une session existe sur un autre device, elle est tuée
<span style="color: #9E9E9E">**Et**</span> la nouvelle session iPad est enregistrée dans Redis

---

## 9. Gestion multi-utilisateurs simultanés

<span style="color: #2196F3">**Étant donné**</span> que 100 000 utilisateurs Premium écoutent simultanément

<span style="color: #FF9800">**Quand**</span> Redis stocke 100 000 entrées active_streams

<span style="color: #4CAF50">**Alors**</span> chaque entrée a un TTL de 5 minutes
<span style="color: #9E9E9E">**Et**</span> Redis gère facilement cette charge (~10 MB de RAM)
<span style="color: #9E9E9E">**Et**</span> les vérifications sont quasi-instantanées (O(1))

---

## 10. Contenus téléchargés (offline) ne comptent pas comme stream

<span style="color: #2196F3">**Étant donné**</span> que j'ai téléchargé 20 contenus en mode offline

<span style="color: #FF9800">**Quand**</span> j'écoute un contenu téléchargé sur mon iPhone sans réseau

<span style="color: #4CAF50">**Alors**</span> aucune session active n'est enregistrée dans Redis
<span style="color: #9E9E9E">**Et**</span> je peux écouter offline pendant qu'un autre device stream online

---

## 11. Transition rapide device <10s tolérée

<span style="color: #2196F3">**Étant donné**</span> que j'écoute dans ma voiture sur mon iPhone
<span style="color: #9E9E9E">**Et**</span> que j'arrive chez moi

<span style="color: #FF9800">**Quand**</span> je lance la lecture sur mon iPad dans les 10 secondes

<span style="color: #4CAF50">**Alors**</span> la transition est considérée comme un changement de device légitime
<span style="color: #9E9E9E">**Et**</span> aucun message d'erreur n'est affiché sur iPhone
<span style="color: #9E9E9E">**Et**</span> la lecture reprend exactement où j'étais sur iPad

---

## 12. Détection transition rapide via timestamps

<span style="color: #2196F3">**Étant donné**</span> que la session iPhone a started_at = 14:30:00

<span style="color: #FF9800">**Quand**</span> je lance sur iPad à 14:30:05 (5 secondes après)

<span style="color: #4CAF50">**Alors**</span> le serveur détecte: diff = 5s < 10s
<span style="color: #9E9E9E">**Et**</span> applique une "graceful transition" (pas de message d'erreur iPhone)
<span style="color: #9E9E9E">**Et**</span> Redis met à jour: active_streams:{user_id} = {device_id: "iPad", ...}

---

## 13. Plusieurs devices disponibles mais 1 seul actif

<span style="color: #2196F3">**Étant donné**</span> que je possède:

    | device | status |
    |---|---|
    | iPhone | Installé |
    | iPad | Installé |
    | MacBook (web) | Connecté |
    | Android (conjoint) | Installé |


<span style="color: #FF9800">**Quand**</span> je lance un stream sur n'importe quel device

<span style="color: #4CAF50">**Alors**</span> seulement 1 peut être actif à la fois
<span style="color: #9E9E9E">**Et**</span> les autres devices sont en "standby"

---

## 14. Justification anti-partage compte

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium partage son compte avec un ami

<span style="color: #FF9800">**Quand**</span> les 2 personnes essaient d'écouter simultanément

<span style="color: #4CAF50">**Alors**</span> la lecture est constamment interrompue sur l'un ou l'autre
<span style="color: #9E9E9E">**Et**</span> l'expérience devient inutilisable
<span style="color: #9E9E9E">**Et**</span> cela décourage fortement le partage de compte

---

## 15. Justification protection revenus créateurs

<span style="color: #2196F3">**Étant donné**</span> que 1 abonnement Premium = 4.99€/mois

<span style="color: #FF9800">**Quand**</span> 70% sont reversés aux créateurs (3.49€)

<span style="color: #4CAF50">**Alors**</span> les créateurs sont rémunérés pour 1 personne
<span style="color: #9E9E9E">**Et**</span> si 2 personnes utilisent le même compte simultanément, c'est injuste
<span style="color: #9E9E9E">**Et**</span> la limite 1 stream protège l'équité du système

---

## 16. Justification UX claire

<span style="color: #2196F3">**Étant donné**</span> qu'un stream est interrompu sur un device

<span style="color: #FF9800">**Quand**</span> l'utilisateur voit le message explicite

<span style="color: #4CAF50">**Alors**</span> il comprend immédiatement pourquoi (autre device actif)
<span style="color: #9E9E9E">**Et**</span> il peut choisir de reprendre sur le device actuel ou l'autre
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de confusion ou frustration

---

## 17. Comparaison avec Spotify (limite 1 stream)

<span style="color: #2196F3">**Étant donné**</span> que Spotify Premium limite aussi à 1 stream actif

<span style="color: #FF9800">**Quand**</span> RoadWave applique la même règle

<span style="color: #4CAF50">**Alors**</span> les utilisateurs connaissent déjà ce comportement
<span style="color: #9E9E9E">**Et**</span> cela paraît normal et accepté par l'industrie

---

## 18. Comparaison avec Netflix (plusieurs streams selon formule)

<span style="color: #2196F3">**Étant donné**</span> que Netflix permet 1-4 streams selon la formule

<span style="color: #FF9800">**Quand**</span> RoadWave limite à 1 stream pour tous

<span style="color: #4CAF50">**Alors**</span> c'est plus strict que Netflix
<span style="color: #F44336">**Mais**</span> Netflix cible le foyer familial (TV partagée)
<span style="color: #4CAF50">**Alors**</span> que RoadWave cible l'individu conducteur (usage personnel)

---

## 19. Détection pattern suspect - Changements devices fréquents

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur change de device 50 fois en 1 heure

<span style="color: #FF9800">**Quand**</span> le système détecte ce pattern anormal

<span style="color: #4CAF50">**Alors**</span> une alerte est générée pour l'équipe modération
<span style="color: #9E9E9E">**Et**</span> le compte peut être marqué pour surveillance
<span style="color: #9E9E9E">**Et**</span> si abus confirmé, suspension possible

---

## 20. Logs des changements de device

<span style="color: #2196F3">**Étant donné**</span> que je change de device plusieurs fois par jour

<span style="color: #FF9800">**Quand**</span> les changements sont loggés

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ces logs aident à détecter les partages de compte

---

## 21. Métriques admin - Changements devices par utilisateur

<span style="color: #2196F3">**Étant donné**</span> qu'un admin consulte les métriques de streaming

<span style="color: #FF9800">**Quand**</span> il accède au dashboard

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je change de device 30 fois par jour pendant 3 jours

<span style="color: #FF9800">**Quand**</span> le système détecte ce pattern

<span style="color: #4CAF50">**Alors**</span> je reçois un email d'avertissement:

---

## 23. Suspension compte après avertissement ignoré

<span style="color: #2196F3">**Étant donné**</span> que j'ai reçu un email d'avertissement il y a 7 jours
<span style="color: #F44336">**Mais**</span> que je continue à changer de device 30 fois par jour

<span style="color: #FF9800">**Quand**</span> l'équipe modération examine le compte

<span style="color: #4CAF50">**Alors**</span> mon compte Premium peut être suspendu pour partage abusif
<span style="color: #9E9E9E">**Et**</span> je reçois un email de suspension avec justification

---

## 24. FAQ - Pourquoi ma lecture s'arrête quand j'utilise un autre device ?

<span style="color: #2196F3">**Étant donné**</span> que je consulte la FAQ Premium

<span style="color: #FF9800">**Quand**</span> je cherche "lecture interrompue"

<span style="color: #4CAF50">**Alors**</span> je trouve la réponse:

---

## 25. Support - Utilisateur pense être piraté

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur voit constamment "Lecture interrompue"
<span style="color: #9E9E9E">**Et**</span> qu'il pense que son compte est piraté

<span style="color: #FF9800">**Quand**</span> il contacte le support

<span style="color: #4CAF50">**Alors**</span> le support vérifie les logs de changements de device
<span style="color: #9E9E9E">**Et**</span> peut identifier les devices (iPhone, iPad perso vs iPhone inconnu)
<span style="color: #9E9E9E">**Et**</span> conseille de changer le mot de passe si device inconnu détecté

---

## 26. Changement mot de passe déconnecte tous les devices

<span style="color: #2196F3">**Étant donné**</span> que je pense que mon compte est compromis

<span style="color: #FF9800">**Quand**</span> je change mon mot de passe

<span style="color: #4CAF50">**Alors**</span> tous mes devices sont déconnectés immédiatement
<span style="color: #9E9E9E">**Et**</span> les sessions actives dans Redis sont supprimées
<span style="color: #9E9E9E">**Et**</span> je dois me reconnecter sur chaque device
<span style="color: #9E9E9E">**Et**</span> cela sécurise mon compte

---

## 27. Test charge - 100 000 vérifications/seconde

<span style="color: #2196F3">**Étant donné**</span> que 100 000 utilisateurs Premium lancent des contenus

<span style="color: #FF9800">**Quand**</span> chaque lancement vérifie Redis (GET active_streams:{user_id})

<span style="color: #4CAF50">**Alors**</span> Redis peut gérer facilement 100 000 requêtes/seconde
<span style="color: #9E9E9E">**Et**</span> le temps de réponse moyen est <1ms
<span style="color: #9E9E9E">**Et**</span> aucun ralentissement n'est constaté

---

## 28. Test failover Redis

<span style="color: #2196F3">**Étant donné**</span> que le serveur Redis principal tombe en panne

<span style="color: #FF9800">**Quand**</span> le failover automatique vers le replica Redis s'active

<span style="color: #4CAF50">**Alors**</span> les sessions actives peuvent être perdues temporairement (max 5 min)
<span style="color: #F44336">**Mais**</span> les utilisateurs peuvent relancer immédiatement
<span style="color: #9E9E9E">**Et**</span> l'impact est minimal (pas de perte de données critiques)

---

## 29. Test concurrence - Lancement simultané 2 devices

<span style="color: #2196F3">**Étant donné**</span> que je lance exactement au même instant sur iPhone et iPad

<span style="color: #FF9800">**Quand**</span> les 2 requêtes arrivent en parallèle au serveur

<span style="color: #4CAF50">**Alors**</span> Redis utilise un lock (SETNX) pour atomicité
<span style="color: #9E9E9E">**Et**</span> 1 seul device gagne (par exemple iPhone)
<span style="color: #9E9E9E">**Et**</span> l'autre device (iPad) reçoit immédiatement une erreur
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut retry sur iPad si souhaité

---

## 30. Nettoyage automatique sessions expirées

<span style="color: #2196F3">**Étant donné**</span> que 1000 sessions Redis ont expiré (TTL atteint)

<span style="color: #FF9800">**Quand**</span> Redis supprime automatiquement ces entrées

<span style="color: #4CAF50">**Alors**</span> la mémoire est libérée
<span style="color: #9E9E9E">**Et**</span> les nouveaux streams peuvent démarrer sans conflit
<span style="color: #9E9E9E">**Et**</span> aucune intervention manuelle n'est nécessaire

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant qu'utilisateur
## 1. Formule mensuelle à 4.99€/mois

<span style="color: #2196F3">**Étant donné**</span> que je consulte les offres Premium

<span style="color: #FF9800">**Quand**</span> je vois la formule mensuelle

<span style="color: #4CAF50">**Alors**</span> le prix affiché est 4.99€/mois
<span style="color: #9E9E9E">**Et**</span> il n'y a aucune réduction
<span style="color: #9E9E9E">**Et**</span> le prix effectif par mois est 4.99€

---

## 2. Formule annuelle à 49.99€/an (2 mois offerts)

<span style="color: #2196F3">**Étant donné**</span> que je consulte les offres Premium

<span style="color: #FF9800">**Quand**</span> je vois la formule annuelle

<span style="color: #4CAF50">**Alors**</span> le prix affiché est 49.99€/an
<span style="color: #9E9E9E">**Et**</span> l'économie affichée est "2 mois offerts"
<span style="color: #9E9E9E">**Et**</span> le prix effectif par mois est 4.16€
<span style="color: #9E9E9E">**Et**</span> je vois le badge "Meilleure offre"

---

## 3. Calcul économie formule annuelle

<span style="color: #2196F3">**Étant donné**</span> que la formule mensuelle coûte 4.99€/mois

<span style="color: #FF9800">**Quand**</span> je calcule le coût annuel en mensuel

<span style="color: #4CAF50">**Alors**</span> 12 mois × 4.99€ = 59.88€/an
<span style="color: #9E9E9E">**Et**</span> la formule annuelle coûte 49.99€
<span style="color: #9E9E9E">**Et**</span> l'économie est de 9.89€ (≈ 2 mois gratuits)
<span style="color: #9E9E9E">**Et**</span> la réduction est de 16.5%

---

## 4. Pas d'essai gratuit disponible

<span style="color: #2196F3">**Étant donné**</span> que je consulte les offres Premium

<span style="color: #FF9800">**Quand**</span> je recherche une option "Essai gratuit"

<span style="color: #4CAF50">**Alors**</span> aucune option d'essai gratuit n'est proposée
<span style="color: #9E9E9E">**Et**</span> je dois payer dès le premier jour pour accéder au Premium

---

## 5. Justification absence essai gratuit - Anti-abus vacances

<span style="color: #2196F3">**Étant donné**</span> que RoadWave ne propose pas d'essai gratuit

<span style="color: #FF9800">**Quand**</span> un utilisateur envisage un road trip de 14 jours

<span style="color: #4CAF50">**Alors**</span> il ne peut pas s'abonner pour l'essai gratuit puis annuler
<span style="color: #9E9E9E">**Et**</span> cela évite les inscriptions opportunistes
<span style="color: #9E9E9E">**Et**</span> protège les revenus des créateurs

---

## 6. Justification absence essai gratuit - Protection revenus créateurs

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur Premium écoute des contenus

<span style="color: #FF9800">**Quand**</span> il génère des écoutes dès le jour 1

<span style="color: #4CAF50">**Alors**</span> les créateurs sont rémunérés immédiatement (70% de 4.99€)
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de "période gratuite" sans rémunération créateurs

---

## 7. Justification absence essai gratuit - Simplicité

<span style="color: #2196F3">**Étant donné**</span> que RoadWave gère les abonnements

<span style="color: #FF9800">**Quand**</span> il n'y a pas d'essai gratuit

<span style="color: #4CAF50">**Alors**</span> pas de gestion complexe de période trial
<span style="color: #9E9E9E">**Et**</span> pas de workflow de conversion trial → payant
<span style="color: #9E9E9E">**Et**</span> cela réduit la complexité technique

---

## 8. Justification absence essai gratuit - Engagement

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur paie dès le début

<span style="color: #FF9800">**Quand**</span> il souscrit à Premium

<span style="color: #4CAF50">**Alors**</span> il est plus engagé qu'un utilisateur en essai gratuit
<span style="color: #9E9E9E">**Et**</span> le taux de churn est généralement plus faible
<span style="color: #9E9E9E">**Et**</span> la lifetime value (LTV) est plus élevée

---

## 9. Pas de partage familial au MVP

<span style="color: #2196F3">**Étant donné**</span> que je consulte les offres Premium

<span style="color: #FF9800">**Quand**</span> je recherche une option "Famille" ou "Partage"

<span style="color: #4CAF50">**Alors**</span> aucune option de partage familial n'est disponible
<span style="color: #9E9E9E">**Et**</span> seuls les abonnements individuels sont proposés

---

## 10. Justification absence partage familial - Complexité technique

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> RoadWave évalue le ROI

<span style="color: #4CAF50">**Alors**</span> le coût dev/support est trop élevé pour le MVP
<span style="color: #9E9E9E">**Et**</span> la fonctionnalité est reportée post-MVP

---

## 11. Justification absence partage familial - Risque abus

<span style="color: #2196F3">**Étant donné**</span> qu'une offre famille permet 5-6 membres

<span style="color: #FF9800">**Quand**</span> il n'y a pas de vérification stricte de lien familial

<span style="color: #4CAF50">**Alors**</span> des "familles" de 6 inconnus pourraient se former
<span style="color: #9E9E9E">**Et**</span> cela réduirait fortement les revenus (6 personnes pour 1 abonnement)

---

## 12. Justification absence partage familial - Cible individuelle

<span style="color: #2196F3">**Étant donné**</span> que RoadWave cible principalement les conducteurs

<span style="color: #FF9800">**Quand**</span> chaque conducteur utilise l'app individuellement en voiture

<span style="color: #4CAF50">**Alors**</span> le besoin de partage familial est limité
<span style="color: #9E9E9E">**Et**</span> la plupart des utilisateurs sont des individus (pas des familles)

---

## 13. Post-MVP - Offre Famille à 9.99€/mois pour 5 comptes

<span style="color: #2196F3">**Étant donné**</span> que RoadWave envisage une offre Famille post-MVP

<span style="color: #FF9800">**Quand**</span> la fonctionnalité est spécifiée

<span style="color: #4CAF50">**Alors**</span> le prix serait 9.99€/mois pour 5 comptes
<span style="color: #9E9E9E">**Et**</span> cela représente 2€/mois/personne
<span style="color: #F44336">**Mais**</span> cette offre n'est pas disponible au MVP

---

## 14. Comparaison tarif - Spotify à 10.99€/mois

<span style="color: #2196F3">**Étant donné**</span> que Spotify Premium coûte 10.99€/mois

<span style="color: #FF9800">**Quand**</span> RoadWave fixe son prix à 4.99€/mois

<span style="color: #4CAF50">**Alors**</span> RoadWave est 54.5% moins cher que Spotify
<span style="color: #9E9E9E">**Et**</span> cela positionne RoadWave comme très accessible

---

## 15. Comparaison tarif - YouTube Premium à 11.99€/mois

<span style="color: #2196F3">**Étant donné**</span> que YouTube Premium coûte 11.99€/mois

<span style="color: #FF9800">**Quand**</span> RoadWave fixe son prix à 4.99€/mois

<span style="color: #4CAF50">**Alors**</span> RoadWave est 58.4% moins cher que YouTube Premium
<span style="color: #9E9E9E">**Et**</span> cela est un argument commercial fort

---

## 16. Comparaison tarif - Apple Music à 10.99€/mois

<span style="color: #2196F3">**Étant donné**</span> qu'Apple Music coûte 10.99€/mois

<span style="color: #FF9800">**Quand**</span> RoadWave fixe son prix à 4.99€/mois

<span style="color: #4CAF50">**Alors**</span> RoadWave est 54.5% moins cher qu'Apple Music
<span style="color: #9E9E9E">**Et**</span> cela attire les utilisateurs sensibles au prix

---

## 17. Justification tarif bas - Cible conducteurs quotidiens

<span style="color: #2196F3">**Étant donné**</span> que RoadWave cible les trajets quotidiens domicile-travail

<span style="color: #FF9800">**Quand**</span> le prix est fixé à 4.99€/mois

<span style="color: #4CAF50">**Alors**</span> c'est un budget raisonnable pour un conducteur
<span style="color: #9E9E9E">**Et**</span> équivalent à ~1-2 cafés/mois
<span style="color: #9E9E9E">**Et**</span> psychologiquement acceptable pour un usage quotidien

---

## 18. Justification formule annuelle - Engagement long terme

<span style="color: #2196F3">**Étant donné**</span> que la formule annuelle offre 2 mois gratuits

<span style="color: #FF9800">**Quand**</span> un utilisateur souscrit pour 1 an

<span style="color: #4CAF50">**Alors**</span> il s'engage sur le long terme
<span style="color: #9E9E9E">**Et**</span> RoadWave sécurise 49.99€ de revenus immédiatement
<span style="color: #9E9E9E">**Et**</span> le cash flow est amélioré

---

## 19. Justification formule annuelle - Réduction churn

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur paie 49.99€ pour l'année

<span style="color: #FF9800">**Quand**</span> il envisage d'arrêter après 3 mois

<span style="color: #4CAF50">**Alors**</span> il a déjà payé pour 12 mois
<span style="color: #9E9E9E">**Et**</span> il continuera probablement à utiliser l'app
<span style="color: #9E9E9E">**Et**</span> le taux de churn est réduit significativement

---

## 20. Affichage comparatif des deux formules

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page Premium

<span style="color: #FF9800">**Quand**</span> je vois les deux formules côte à côte

<span style="color: #4CAF50">**Alors**</span> je vois:

---

## 21. Mise en avant formule annuelle

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page Premium

<span style="color: #FF9800">**Quand**</span> je vois les deux formules

<span style="color: #4CAF50">**Alors**</span> la formule annuelle a un badge "Meilleure offre" ⭐
<span style="color: #9E9E9E">**Et**</span> elle est visuellement mise en avant (bordure colorée, taille plus grande)
<span style="color: #9E9E9E">**Et**</span> l'économie de 2 mois est affichée en gros
<span style="color: #9E9E9E">**Et**</span> cela incite à choisir la formule annuelle

---

## 22. Lien "Pourquoi pas d'essai gratuit ?" en FAQ

<span style="color: #2196F3">**Étant donné**</span> que je consulte la page Premium

<span style="color: #FF9800">**Quand**</span> je clique sur "FAQ"

<span style="color: #4CAF50">**Alors**</span> je vois une question "Pourquoi pas d'essai gratuit ?"
<span style="color: #9E9E9E">**Et**</span> la réponse explique:

---

## 23. A/B test formule annuelle (post-MVP)

<span style="color: #2196F3">**Étant donné**</span> que RoadWave veut optimiser la conversion annuelle

<span style="color: #FF9800">**Quand**</span> un A/B test est lancé

<span style="color: #4CAF50">**Alors**</span> groupe A voit "2 mois offerts" (économie en durée)
<span style="color: #9E9E9E">**Et**</span> groupe B voit "Économisez 9.89€" (économie en argent)
<span style="color: #9E9E9E">**Et**</span> les taux de souscription sont mesurés
<span style="color: #9E9E9E">**Et**</span> le message le plus performant est déployé

---

## 24. Promo temporaire exceptionnelle (Black Friday, etc.)

<span style="color: #2196F3">**Étant donné**</span> que c'est le Black Friday

<span style="color: #FF9800">**Quand**</span> une promo temporaire est activée

<span style="color: #4CAF50">**Alors**</span> la formule annuelle peut passer à 39.99€/an (au lieu de 49.99€)
<span style="color: #9E9E9E">**Et**</span> l'économie affichée est "4 mois offerts !"
<span style="color: #9E9E9E">**Et**</span> la promo dure 3 jours uniquement
<span style="color: #9E9E9E">**Et**</span> cela génère un pic de souscriptions

---

## 25. Code promo partenariat influenceur

<span style="color: #2196F3">**Étant donné**</span> qu'un influenceur promeut RoadWave

<span style="color: #FF9800">**Quand**</span> il partage un code promo "INFLUENCEUR20"

<span style="color: #4CAF50">**Alors**</span> les utilisateurs obtiennent -20% sur le premier mois (3.99€ au lieu de 4.99€)
<span style="color: #9E9E9E">**Et**</span> le code est valable 1 mois
<span style="color: #9E9E9E">**Et**</span> les conversions sont trackées par code promo

---

## 26. Statistiques admin - Répartition formules

<span style="color: #2196F3">**Étant donné**</span> qu'un admin consulte les métriques d'abonnements

<span style="color: #FF9800">**Quand**</span> il accède au dashboard

<span style="color: #4CAF50">**Alors**</span> 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€ |

<span style="color: #9E9E9E">**Et**</span> ces données aident à piloter la stratégie tarifaire

---

## 27. Calcul revenus mensuels récurrents (MRR)

<span style="color: #2196F3">**Étant donné**</span> que RoadWave a:

    | formule | nombre abonnés | prix |
    |---|---|---|
    | Mensuel | 7,234 | 4.99€/mois |
    | Annuel | 5,313 | 49.99€/an |


<span style="color: #FF9800">**Quand**</span> le MRR est calculé

<span style="color: #4CAF50">**Alors**</span> MRR mensuel = 7,234 × 4.99€ = 36,098€
<span style="color: #9E9E9E">**Et**</span> MRR annuel ramené au mois = 5,313 × 49.99€ / 12 = 22,139€
<span style="color: #9E9E9E">**Et**</span> MRR total = 58,237€/mois

---

## 28. Projection revenus annuels (ARR)

<span style="color: #2196F3">**Étant donné**</span> que le MRR est de 58,237€

<span style="color: #FF9800">**Quand**</span> l'ARR est calculé

<span style="color: #4CAF50">**Alors**</span> ARR = 58,237€ × 12 = 698,844€/an
<span style="color: #9E9E9E">**Et**</span> cela aide à évaluer la valorisation de l'entreprise

---

## 29. Affichage prix TTC (TVA incluse)

<span style="color: #2196F3">**Étant donné**</span> que RoadWave est une plateforme française

<span style="color: #FF9800">**Quand**</span> les prix sont affichés

<span style="color: #4CAF50">**Alors**</span> tous les prix sont TTC (TVA 20% incluse)
<span style="color: #9E9E9E">**Et**</span> le prix 4.99€ inclut déjà la TVA
<span style="color: #9E9E9E">**Et**</span> cela respecte la réglementation française

---

## 30. Performance page Premium avec cache

<span style="color: #2196F3">**Étant donné**</span> que la page Premium est consultée fréquemment

<span style="color: #FF9800">**Quand**</span> un utilisateur charge la page

<span style="color: #4CAF50">**Alors**</span> les prix et avantages sont servis depuis un cache CDN
<span style="color: #9E9E9E">**Et**</span> le temps de chargement est <200ms
<span style="color: #9E9E9E">**Et**</span> cela garantit une expérience fluide

---

## 31. Localisation prix selon pays (post-MVP)

<span style="color: #2196F3">**Étant donné**</span> que RoadWave se lance à l'international post-MVP

<span style="color: #FF9800">**Quand**</span> un utilisateur se connecte depuis l'Allemagne

<span style="color: #4CAF50">**Alors**</span> les prix peuvent être ajustés (ex: 4.99€ en France, 4.49€ en Pologne)
<span style="color: #9E9E9E">**Et**</span> cela respecte le pouvoir d'achat local
<span style="color: #F44336">**Mais**</span> cette fonctionnalité n'est pas au MVP (France uniquement)

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
## 1. URL du profil créateur

<span style="color: #2196F3">**Étant donné**</span> un créateur avec le pseudo "paris_stories"

<span style="color: #FF9800">**Quand**</span> l'utilisateur accède au profil

<span style="color: #4CAF50">**Alors**</span> l'URL est "https://roadwave.fr/@paris_stories"

---

## 2. Informations principales du profil

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> le profil est affiché

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un créateur avec <valeur_exacte> <métrique>

<span style="color: #FF9800">**Quand**</span> le profil est affiché

<span style="color: #4CAF50">**Alors**</span> la valeur affichée est "<valeur_affichée>"

**📊 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

<span style="color: #2196F3">**Étant donné**</span> un créateur avec la bio suivante en markdown:

<span style="color: #FF9800">**Quand**</span> le profil est affiché

<span style="color: #4CAF50">**Alors**</span> le texte en gras "Histoires de Paris" est formaté
<span style="color: #9E9E9E">**Et**</span> le texte en italique "Nouveau contenu chaque semaine" est formaté
<span style="color: #9E9E9E">**Et**</span> le lien "https://paris-stories.fr" est cliquable

---

## 5. Limitation de la bio à 300 caractères

<span style="color: #2196F3">**Étant donné**</span> un créateur qui entre une bio de 350 caractères

<span style="color: #FF9800">**Quand**</span> la bio est sauvegardée

<span style="color: #4CAF50">**Alors**</span> seuls les 300 premiers caractères sont conservés
<span style="color: #9E9E9E">**Et**</span> un message "Maximum 300 caractères" s'affiche

---

## 6. Boutons d'action principaux

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur consulte un profil créateur

<span style="color: #FF9800">**Quand**</span> la page est chargée

<span style="color: #4CAF50">**Alors**</span> 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 [•••]

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur clique sur le bouton [•••]

<span style="color: #FF9800">**Quand**</span> le menu s'ouvre

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un créateur avec 3 contenus publiés

<span style="color: #FF9800">**Quand**</span> le profil est affiché

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un créateur avec 10 contenus publiés

<span style="color: #FF9800">**Quand**</span> l'utilisateur sélectionne le tri "<option_tri>"

<span style="color: #4CAF50">**Alors**</span> les contenus sont triés par <critère>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> un créateur avec des contenus taggés "Voyage", "Histoire", "Gastronomie"

<span style="color: #FF9800">**Quand**</span> l'utilisateur filtre par tags "Voyage, Histoire"

<span style="color: #4CAF50">**Alors**</span> seuls les contenus avec ces tags sont affichés
<span style="color: #9E9E9E">**Et**</span> le nombre de résultats est indiqué "12 contenus"

---

## 11. Recherche locale dans le profil

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur consulte le profil de "@paris_stories"
<span style="color: #9E9E9E">**Et**</span> que le créateur a publié 50 contenus

<span style="color: #FF9800">**Quand**</span> l'utilisateur entre "Montmartre" dans la barre de recherche

<span style="color: #4CAF50">**Alors**</span> la recherche s'effectue sur les titres et descriptions
<span style="color: #9E9E9E">**Et**</span> seuls les contenus correspondants sont affichés
<span style="color: #9E9E9E">**Et**</span> le placeholder indique "Rechercher dans les contenus de @paris_stories"

---

## 12. Chargement paginé des contenus

<span style="color: #2196F3">**Étant donné**</span> un créateur avec 100 contenus publiés

<span style="color: #FF9800">**Quand**</span> le profil est affiché

<span style="color: #4CAF50">**Alors**</span> 20 contenus sont chargés initialement
<span style="color: #9E9E9E">**Et**</span> un bouton "Charger plus" est visible en bas de page

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "Charger plus"

<span style="color: #4CAF50">**Alors**</span> 20 contenus supplémentaires sont chargés

---

## 13. Informations publiques visibles par tous

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur consulte un profil créateur

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur consulte un profil créateur

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le créateur "@paris_stories" consulte son propre dashboard

<span style="color: #FF9800">**Quand**</span> la page statistiques est affichée

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le créateur consulte son dashboard

<span style="color: #FF9800">**Quand**</span> il sélectionne la période "30 jours"

<span style="color: #4CAF50">**Alors**</span> un graphique d'évolution des abonnés est affiché
<span style="color: #9E9E9E">**Et**</span> les périodes disponibles sont:

    | période |
    |---|
    | 30j |
    | 90j |
    | 1 an |


---

## 17. Tableau détaillé des écoutes par contenu

<span style="color: #2196F3">**Étant donné**</span> un créateur avec 10 contenus publiés

<span style="color: #FF9800">**Quand**</span> il consulte le tableau des performances

<span style="color: #4CAF50">**Alors**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> un créateur vérifié "@paris_stories"

<span style="color: #FF9800">**Quand**</span> son profil est affiché

<span style="color: #4CAF50">**Alors**</span> le badge bleu "✓" est accolé au pseudo
<span style="color: #9E9E9E">**Et**</span> un tooltip "Compte vérifié" s'affiche au survol

---

## 19. Badge vérifié visible partout

<span style="color: #2196F3">**Étant donné**</span> un créateur vérifié "@paris_stories"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un créateur avec <critère>

<span style="color: #FF9800">**Quand**</span> les conditions sont validées

<span style="color: #4CAF50">**Alors**</span> le badge vérifié est attribué <automatique>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> un créateur qui complète son KYC Mangopay

<span style="color: #FF9800">**Quand**</span> les documents sont validés

<span style="color: #4CAF50">**Alors**</span> le badge vérifié est attribué automatiquement
<span style="color: #9E9E9E">**Et**</span> une notification "Votre compte est maintenant vérifié ✓" est envoyée

---

## 22. Attribution automatique à 10K abonnés

<span style="color: #2196F3">**Étant donné**</span> un créateur avec 9999 abonnés et un compte de 7 mois

<span style="color: #FF9800">**Quand**</span> il atteint 10000 abonnés

<span style="color: #4CAF50">**Alors**</span> le badge vérifié est attribué automatiquement
<span style="color: #9E9E9E">**Et**</span> une notification de félicitations est envoyée

---

## 23. Demande manuelle de vérification (célébrité)

<span style="color: #2196F3">**Étant donné**</span> un créateur reconnu publiquement

<span style="color: #FF9800">**Quand**</span> il soumet le formulaire de demande de vérification

<span style="color: #4CAF50">**Alors**</span> une requête est créée pour l'équipe RoadWave
<span style="color: #9E9E9E">**Et**</span> l'équipe vérifie l'identité sous 48-72h
<span style="color: #9E9E9E">**Et**</span> le badge est attribué si validation réussie

---

## 24. Retrait du badge en cas de suspension

<span style="color: #2196F3">**Étant donné**</span> un créateur vérifié avec le badge "✓"

<span style="color: #FF9800">**Quand**</span> sa monétisation est suspendue

<span style="color: #4CAF50">**Alors**</span> le badge vérifié est retiré temporairement
<span style="color: #9E9E9E">**Et**</span> le badge est restauré après levée de la suspension

---

## 25. Retrait définitif du badge pour strikes multiples

<span style="color: #2196F3">**Étant donné**</span> un créateur vérifié avec 3 strikes actifs

<span style="color: #FF9800">**Quand**</span> un 4ème strike est appliqué (ban)

<span style="color: #4CAF50">**Alors**</span> le badge vérifié est retiré définitivement
<span style="color: #9E9E9E">**Et**</span> le compte est banni

---

## 26. Retrait du badge pour usurpation d'identité

<span style="color: #2196F3">**Étant donné**</span> un créateur vérifié qui usurpe l'identité d'une célébrité

<span style="color: #FF9800">**Quand**</span> la fraude est détectée

<span style="color: #4CAF50">**Alors**</span> le badge est retiré immédiatement
<span style="color: #9E9E9E">**Et**</span> le compte est banni
<span style="color: #9E9E9E">**Et**</span> une enquête est ouverte

---

## 27. Profil créateur supprimé

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur tente d'accéder à "@deleted_user"

<span style="color: #FF9800">**Quand**</span> la page est chargée

<span style="color: #4CAF50">**Alors**</span> un message "Ce profil n'existe pas ou a été supprimé" s'affiche

---

## 28. Blocage d'un créateur

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur bloque le créateur "@spam_account"

<span style="color: #FF9800">**Quand**</span> l'utilisateur consulte son flux de recommandations

<span style="color: #4CAF50">**Alors**</span> aucun contenu de "@spam_account" n'est affiché
<span style="color: #9E9E9E">**Et**</span> le créateur n'apparaît plus dans les recherches

---

## 29. Déblocage d'un créateur

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a bloqué "@paris_stories"

<span style="color: #FF9800">**Quand**</span> il accède à ses paramètres "Comptes bloqués"
<span style="color: #9E9E9E">**Et**</span> qu'il débloque "@paris_stories"

<span style="color: #4CAF50">**Alors**</span> les contenus du créateur réapparaissent dans les recommandations

---

## 30. Signalement d'un profil pour spam

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur signale le profil "@spam_account"

<span style="color: #FF9800">**Quand**</span> il sélectionne la raison "Spam"

<span style="color: #4CAF50">**Alors**</span> le signalement est envoyé à la modération
<span style="color: #9E9E9E">**Et**</span> un message de confirmation s'affiche
<span style="color: #9E9E9E">**Et**</span> le profil reste visible jusqu'à décision de modération

---

## 31. Signalement pour usurpation d'identité

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur signale le profil "@fake_celebrity"

<span style="color: #FF9800">**Quand**</span> il sélectionne "Usurpation d'identité"
<span style="color: #9E9E9E">**Et**</span> qu'il fournit une preuve

<span style="color: #4CAF50">**Alors**</span> le signalement est priorisé (priorité HAUTE)
<span style="color: #9E9E9E">**Et**</span> l'équipe modération traite sous 24h

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un compte publicitaire est créé et vérifié
## 1. Création d'une campagne publicitaire complète

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté en tant que publicitaire

<span style="color: #FF9800">**Quand**</span> 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+ |


<span style="color: #4CAF50">**Alors**</span> la campagne est créée avec succès
<span style="color: #9E9E9E">**Et**</span> le budget quotidien calculé est de 21.43€/jour
<span style="color: #9E9E9E">**Et**</span> les diffusions estimées sont de ~430 écoutes complètes
<span style="color: #9E9E9E">**Et**</span> un statut "En attente de validation" est assigné

---

## 2. Budget minimum 50€ requis

<span style="color: #2196F3">**Étant donné**</span> que je crée une nouvelle campagne

<span style="color: #FF9800">**Quand**</span> je définis un budget de 40€

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Budget minimum requis: 50€"
<span style="color: #9E9E9E">**Et**</span> la campagne n'est pas créée

---

## 3. Budget de 50€ exactement accepté

<span style="color: #2196F3">**Étant donné**</span> que je crée une nouvelle campagne

<span style="color: #FF9800">**Quand**</span> je définis un budget de 50€

<span style="color: #4CAF50">**Alors**</span> la campagne est créée avec succès
<span style="color: #9E9E9E">**Et**</span> aucune erreur n'est affichée

---

## 4. Calcul automatique du budget quotidien

<span style="color: #2196F3">**Étant donné**</span> une campagne avec:

    | Budget total | 300€ |
    |---|---|
    | Durée | 14 j |


<span style="color: #FF9800">**Quand**</span> le système calcule le budget quotidien

<span style="color: #4CAF50">**Alors**</span> le budget/jour est de 21.43€
<span style="color: #9E9E9E">**Et**</span> le nombre estimé de diffusions/jour est de 430 (à 0.05€/écoute)

---

## 5. Ciblage géographique point GPS précis

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je sélectionne "Point GPS" avec coordonnées (43.1234, 5.9234)
<span style="color: #9E9E9E">**Et**</span> que je définis un rayon de 5km

<span style="color: #4CAF50">**Alors**</span> la campagne cible uniquement les utilisateurs dans ce rayon
<span style="color: #9E9E9E">**Et**</span> la zone est représentée par un cercle sur la carte

---

## 6. Ciblage géographique ville

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je sélectionne "Ville" et choisis "Marseille"

<span style="color: #4CAF50">**Alors**</span> la campagne cible tous les utilisateurs dans la commune de Marseille
<span style="color: #9E9E9E">**Et**</span> les limites administratives sont affichées sur la carte

---

## 7. Ciblage géographique département

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je sélectionne "Département" et choisis "Var (83)"

<span style="color: #4CAF50">**Alors**</span> la campagne cible tout le département du Var
<span style="color: #9E9E9E">**Et**</span> une estimation de population cible est affichée

---

## 8. Ciblage géographique région

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je sélectionne "Région" et choisis "Provence-Alpes-Côte d'Azur"

<span style="color: #4CAF50">**Alors**</span> la campagne cible toute la région PACA
<span style="color: #9E9E9E">**Et**</span> l'estimation de population cible est mise à jour

---

## 9. Ciblage géographique national

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je sélectionne "National"

<span style="color: #4CAF50">**Alors**</span> la campagne cible tous les utilisateurs en France
<span style="color: #9E9E9E">**Et**</span> aucune limite géographique n'est appliquée

---

## 10. Ciblage horaire plages multiples

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je définis les plages horaires:

    | Plage |
    |---|
    | 7h-9h |
    | 12h-14h |
    | 17h-19h |


<span style="color: #4CAF50">**Alors**</span> la publicité est diffusée uniquement pendant ces plages
<span style="color: #9E9E9E">**Et**</span> elle n'est jamais diffusée en dehors (ex: 10h, 15h, 20h)

---

## 11. Ciblage horaire toute la journée

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je ne définis aucune plage horaire spécifique

<span style="color: #4CAF50">**Alors**</span> la publicité est diffusée 24h/24
<span style="color: #9E9E9E">**Et**</span> aucune restriction horaire n'est appliquée

---

## 12. Ciblage par centres d'intérêt

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne pour un garage automobile

<span style="color: #FF9800">**Quand**</span> je sélectionne les tags:

    | Tag |
    |---|
    | Automobile |
    | Mécanique |
    | Sport |


<span style="color: #4CAF50">**Alors**</span> la publicité est prioritaire pour les utilisateurs avec jauges élevées sur ces tags
<span style="color: #9E9E9E">**Et**</span> elle peut quand même être diffusée à d'autres utilisateurs (ciblage non exclusif)

---

## 13. Classification d'âge obligatoire

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> j'essaie de valider sans sélectionner une tranche d'âge

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Classification d'âge obligatoire"
<span style="color: #9E9E9E">**Et**</span> les options proposées sont:

    | Option |
    |---|
    | Tout public |
    | 13+ |
    | 16+ |
    | 18+ |


---

## 14. Upload audio publicitaire formats acceptés

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> j'upload un fichier audio format MP3

<span style="color: #4CAF50">**Alors**</span> le fichier est accepté

<span style="color: #FF9800">**Quand**</span> j'upload un fichier audio format AAC (.aac ou .m4a)

<span style="color: #4CAF50">**Alors**</span> le fichier est accepté

<span style="color: #FF9800">**Quand**</span> j'upload un fichier audio format WAV

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Format non supporté. Utilisez MP3 ou AAC"

---

## 15. Durée audio publicitaire validée

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> j'upload un audio de 8 secondes

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Durée minimale: 10 secondes"

<span style="color: #FF9800">**Quand**</span> j'upload un audio de 65 secondes

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Durée maximale: 60 secondes"

<span style="color: #FF9800">**Quand**</span> j'upload un audio de 30 secondes

<span style="color: #4CAF50">**Alors**</span> le fichier est accepté

---

## 16. Prépaiement obligatoire via Mangopay

<span style="color: #2196F3">**Étant donné**</span> que j'ai configuré une campagne à 300€

<span style="color: #FF9800">**Quand**</span> j'arrive à l'étape de paiement

<span style="color: #4CAF50">**Alors**</span> je dois payer les 300€ avant validation
<span style="color: #9E9E9E">**Et**</span> le paiement est traité via Mangopay
<span style="color: #9E9E9E">**Et**</span> seule la carte bancaire est acceptée

---

## 17. Recharge automatique optionnelle

<span style="color: #2196F3">**Étant donné**</span> que j'ai une campagne active

<span style="color: #FF9800">**Quand**</span> je configure la recharge automatique à 10% du budget

<span style="color: #4CAF50">**Alors**</span> si le budget restant passe sous 30€ (10% de 300€)
<span style="color: #9E9E9E">**Et**</span> que la campagne recharge automatiquement 100€
<span style="color: #9E9E9E">**Et**</span> ma carte bancaire est débitée de 100€
<span style="color: #9E9E9E">**Et**</span> le budget total passe à 130€

---

## 18. Désactivation recharge automatique

<span style="color: #2196F3">**Étant donné**</span> que j'ai activé la recharge automatique

<span style="color: #FF9800">**Quand**</span> je désactive cette option

<span style="color: #4CAF50">**Alors**</span> aucune recharge ne se produit automatiquement
<span style="color: #9E9E9E">**Et**</span> la campagne s'arrête quand le budget atteint 0€

---

## 19. Étalement budget sur période longue

<span style="color: #2196F3">**Étant donné**</span> une campagne avec:

    | Budget total | 1000€ |
    |---|---|
    | Durée | 30 j |


<span style="color: #FF9800">**Quand**</span> le système calcule l'étalement

<span style="color: #4CAF50">**Alors**</span> le budget/jour est de 33.33€
<span style="color: #9E9E9E">**Et**</span> si le budget se consomme plus vite (ex: 50€/jour)
<span style="color: #4CAF50">**Alors**</span> une alerte "Budget épuisé dans 10 jours" est envoyée

---

## 20. Estimation population cible selon zone

<span style="color: #2196F3">**Étant donné**</span> que je sélectionne la zone "Marseille"

<span style="color: #FF9800">**Quand**</span> le système calcule la population cible

<span style="color: #4CAF50">**Alors**</span> l'estimation affichée est "~15 000 utilisateurs potentiels"
<span style="color: #9E9E9E">**Et**</span> un message "Estimation basée sur utilisateurs actifs dans la zone" s'affiche

---

## 21. Campagne avec date de début différée

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je définis la date de début au 2026-03-01 (dans 1 mois)

<span style="color: #4CAF50">**Alors**</span> la campagne a le statut "Programmée"
<span style="color: #9E9E9E">**Et**</span> elle démarre automatiquement le 2026-03-01 à 00h00
<span style="color: #9E9E9E">**Et**</span> le budget n'est pas consommé avant cette date

---

## 22. Interface self-service accessible

<span style="color: #2196F3">**Étant donné**</span> que je suis un publicitaire

<span style="color: #FF9800">**Quand**</span> j'accède à l'interface publicitaire

<span style="color: #4CAF50">**Alors**</span> je peux créer une campagne sans contact commercial RoadWave
<span style="color: #9E9E9E">**Et**</span> toutes les options sont configurables en autonomie
<span style="color: #9E9E9E">**Et**</span> un tutoriel guidé est disponible (première utilisation)

---

## 23. Aperçu zone ciblée sur carte interactive

<span style="color: #2196F3">**Étant donné**</span> que je configure une zone géographique

<span style="color: #FF9800">**Quand**</span> je sélectionne "Département du Var"

<span style="color: #4CAF50">**Alors**</span> une carte Leaflet affiche les limites du département en surbrillance
<span style="color: #9E9E9E">**Et**</span> un compteur "~50 000 utilisateurs actifs" est affiché
<span style="color: #9E9E9E">**Et**</span> je peux zoomer/dézoomer pour visualiser la zone

---

## 24. Tags multiples pour ciblage affiné

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne pour un restaurant

<span style="color: #FF9800">**Quand**</span> je sélectionne les tags:

    | Tag |
    |---|
    | Gastronomie |
    | Tourisme |
    | Famille |


<span style="color: #4CAF50">**Alors**</span> la publicité est prioritaire pour utilisateurs intéressés par ces 3 thèmes
<span style="color: #9E9E9E">**Et**</span> le score de ciblage combine les 3 jauges d'intérêt

---

## 25. Validation des dates de campagne

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je définis une date de début postérieure à la date de fin

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Date de fin doit être après date de début"
<span style="color: #9E9E9E">**Et**</span> la campagne n'est pas créée

---

## 26. Durée minimale de campagne

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je définis une durée de moins de 24 heures

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Durée minimale: 1 jour"
<span style="color: #9E9E9E">**Et**</span> je dois ajuster les dates

---

## 27. Durée maximale de campagne

<span style="color: #2196F3">**Étant donné**</span> que je crée une campagne

<span style="color: #FF9800">**Quand**</span> je définis une durée de plus de 90 jours

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Durée maximale: 90 jours"
<span style="color: #9E9E9E">**Et**</span> je dois ajuster les dates ou créer plusieurs campagnes

---

## 28. 📋 Plan: Calcul budget quotidien selon durée

<span style="color: #2196F3">**Étant donné**</span> une campagne avec un budget de <budget>€

<span style="color: #FF9800">**Quand**</span> la durée est de <duree> jours

<span style="color: #4CAF50">**Alors**</span> le budget quotidien est de <budget_jour>€/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

<span style="color: #2196F3">**Étant donné**</span> un budget quotidien de <budget_jour>€

<span style="color: #FF9800">**Quand**</span> le coût par écoute complète est 0.05€

<span style="color: #4CAF50">**Alors**</span> le nombre estimé de diffusions/jour est <diffusions>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que j'upload un fichier <fichier>

<span style="color: #FF9800">**Quand**</span> le format est <format>

<span style="color: #4CAF50">**Alors**</span> le résultat est <resultat>

**📊 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é |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur gratuit écoute du contenu
## 1. Durée minimale 10 secondes

<span style="color: #2196F3">**Étant donné**</span> qu'un publicitaire uploade une publicité de 8 secondes

<span style="color: #FF9800">**Quand**</span> le système valide la durée

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Durée minimale: 10 secondes"
<span style="color: #9E9E9E">**Et**</span> l'upload est rejeté

---

## 2. Durée maximale 60 secondes

<span style="color: #2196F3">**Étant donné**</span> qu'un publicitaire uploade une publicité de 65 secondes

<span style="color: #FF9800">**Quand**</span> le système valide la durée

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Durée maximale: 60 secondes"
<span style="color: #9E9E9E">**Et**</span> l'upload est rejeté

---

## 3. Durée recommandée 15-30 secondes

<span style="color: #2196F3">**Étant donné**</span> qu'un publicitaire crée une campagne

<span style="color: #FF9800">**Quand**</span> il voit les recommandations

<span style="color: #4CAF50">**Alors**</span> un message s'affiche:

---

## 4. Publicité de 10 secondes acceptée

<span style="color: #2196F3">**Étant donné**</span> qu'un publicitaire uploade une publicité de 10 secondes

<span style="color: #FF9800">**Quand**</span> le système valide la durée

<span style="color: #4CAF50">**Alors**</span> le fichier est accepté
<span style="color: #9E9E9E">**Et**</span> aucune erreur n'est affichée

---

## 5. Publicité de 60 secondes acceptée

<span style="color: #2196F3">**Étant donné**</span> qu'un publicitaire uploade une publicité de 60 secondes

<span style="color: #FF9800">**Quand**</span> le système valide la durée

<span style="color: #4CAF50">**Alors**</span> le fichier est accepté
<span style="color: #9E9E9E">**Et**</span> un avertissement s'affiche: "⚠️ Durée longue: taux de skip potentiellement élevé"

---

## 6. Délai minimum skippable 5 secondes par défaut

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30 secondes démarre
<span style="color: #9E9E9E">**Et**</span> que le délai minimal est configuré à 5 secondes

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 3 secondes

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" n'est pas visible
<span style="color: #9E9E9E">**Et**</span> je dois attendre 2 secondes supplémentaires

<span style="color: #FF9800">**Quand**</span> j'atteins 5 secondes d'écoute

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" apparaît
<span style="color: #9E9E9E">**Et**</span> je peux cliquer pour passer au contenu suivant

---

## 7. Délai minimum paramétrable admin (3 secondes)

<span style="color: #2196F3">**Étant donné**</span> que l'admin configure le délai à 3 secondes
<span style="color: #9E9E9E">**Et**</span> qu'une publicité démarre

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 3 secondes

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" apparaît immédiatement
<span style="color: #9E9E9E">**Et**</span> je peux skipper

---

## 8. Délai minimum paramétrable admin (10 secondes)

<span style="color: #2196F3">**Étant donné**</span> que l'admin configure le délai à 10 secondes
<span style="color: #9E9E9E">**Et**</span> qu'une publicité démarre

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 9 secondes

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" n'est toujours pas visible

<span style="color: #FF9800">**Quand**</span> j'atteins 10 secondes

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" apparaît

---

## 9. Facturation écoute complète (>80%) - 0.05€

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30 secondes est diffusée

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 25 secondes (83%)

<span style="color: #4CAF50">**Alors**</span> l'écoute est considérée comme "complète"
<span style="color: #9E9E9E">**Et**</span> le publicitaire est facturé 0.05€
<span style="color: #9E9E9E">**Et**</span> le compteur "écoutes complètes" s'incrémente

---

## 10. Facturation écoute complète exactement 80%

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30 secondes est diffusée

<span style="color: #FF9800">**Quand**</span> j'écoute pendant exactement 24 secondes (80%)

<span style="color: #4CAF50">**Alors**</span> l'écoute est considérée comme "complète"
<span style="color: #9E9E9E">**Et**</span> le publicitaire est facturé 0.05€

---

## 11. Facturation skip après délai minimal - 0.02€

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30 secondes est diffusée
<span style="color: #9E9E9E">**Et**</span> que le délai minimal est 5 secondes

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 10 secondes (33%)
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Passer"

<span style="color: #4CAF50">**Alors**</span> l'écoute est considérée comme "partielle"
<span style="color: #9E9E9E">**Et**</span> le publicitaire est facturé 0.02€

---

## 12. Facturation skip immédiat (<5s) - 0€

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30 secondes est diffusée
<span style="color: #9E9E9E">**Et**</span> que le délai minimal est 5 secondes

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 3 secondes
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Suivant" (pas de bouton skip encore)

<span style="color: #4CAF50">**Alors**</span> l'écoute est considérée comme "non engagée"
<span style="color: #9E9E9E">**Et**</span> le publicitaire n'est PAS facturé (0€)

---

## 13. Comptabilisation écoute complète à 79%

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30 secondes est diffusée

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 23 secondes (77%)

<span style="color: #4CAF50">**Alors**</span> l'écoute est considérée comme "partielle" (pas complète)
<span style="color: #9E9E9E">**Et**</span> le publicitaire est facturé 0.02€

---

## 14. Comptabilisation écoute complète à 100%

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30 secondes est diffusée

<span style="color: #FF9800">**Quand**</span> j'écoute les 30 secondes complètes (100%)

<span style="color: #4CAF50">**Alors**</span> l'écoute est considérée comme "complète"
<span style="color: #9E9E9E">**Et**</span> le publicitaire est facturé 0.05€

---

## 15. Budget consommé selon mix écoutes

<span style="color: #2196F3">**Étant donné**</span> 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€ |


<span style="color: #FF9800">**Quand**</span> je calcule le budget consommé

<span style="color: #4CAF50">**Alors**</span> le total est 240€
<span style="color: #9E9E9E">**Et**</span> il reste 60€ de budget disponible

---

## 16. Affichage compteur secondes restantes

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30s démarre
<span style="color: #9E9E9E">**Et**</span> que le délai minimal est 5s

<span style="color: #FF9800">**Quand**</span> j'écoute pendant 2 secondes

<span style="color: #4CAF50">**Alors**</span> un compteur s'affiche: "Passer dans 3s..."

<span style="color: #FF9800">**Quand**</span> j'atteins 5 secondes

<span style="color: #4CAF50">**Alors**</span> le compteur disparaît
<span style="color: #9E9E9E">**Et**</span> le bouton "Passer la publicité" s'affiche

---

## 17. Progress bar publicité visible

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30s est en lecture

<span style="color: #FF9800">**Quand**</span> 10 secondes se sont écoulées

<span style="color: #4CAF50">**Alors**</span> la progress bar affiche 33% (10/30)
<span style="color: #9E9E9E">**Et**</span> l'indicateur temporel affiche "0:10 / 0:30"
<span style="color: #9E9E9E">**Et**</span> l'utilisateur visualise la progression

---

## 18. Message "Publicité" clairement affiché

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité démarre

<span style="color: #FF9800">**Quand**</span> l'audio commence

<span style="color: #4CAF50">**Alors**</span> un badge "Publicité" est affiché en haut de l'écran
<span style="color: #9E9E9E">**Et**</span> la durée totale est indiquée: "Publicité (30s)"
<span style="color: #9E9E9E">**Et**</span> la transparence est maximale (utilisateur sait que c'est une pub)

---

## 19. Transition fluide après publicité

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30s se termine

<span style="color: #FF9800">**Quand**</span> la lecture atteint 30 secondes

<span style="color: #4CAF50">**Alors**</span> le délai de transition de 2s démarre
<span style="color: #9E9E9E">**Et**</span> le contenu normal suivant est annoncé
<span style="color: #9E9E9E">**Et**</span> l'enchaînement est naturel (même UX que entre contenus)

---

## 20. Like autorisé sur publicité

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité est en lecture
<span style="color: #9E9E9E">**Et**</span> que le véhicule est à l'arrêt

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton cœur

<span style="color: #4CAF50">**Alors**</span> un like explicite (+2%) est enregistré
<span style="color: #9E9E9E">**Et**</span> mes jauges d'intérêt sont mises à jour selon les tags de la pub
<span style="color: #9E9E9E">**Et**</span> le publicitaire voit un compteur "Likes" incrémenté

---

## 21. Abonnement autorisé sur publicité

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité est diffusée par un créateur
<span style="color: #9E9E9E">**Et**</span> que le véhicule est à l'arrêt

<span style="color: #FF9800">**Quand**</span> je clique sur "S'abonner"

<span style="color: #4CAF50">**Alors**</span> l'abonnement est enregistré (+5% jauges)
<span style="color: #9E9E9E">**Et**</span> le publicitaire bénéficie de l'engagement fort
<span style="color: #9E9E9E">**Et**</span> cela compte comme une conversion majeure

---

## 22. Bouton skip visible et accessible

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité a dépassé le délai minimal

<span style="color: #FF9800">**Quand**</span> le bouton "Passer" s'affiche

<span style="color: #4CAF50">**Alors**</span> il est positionné en bas à droite de l'écran
<span style="color: #9E9E9E">**Et**</span> il a une taille de clic confortable (44×44px minimum iOS)
<span style="color: #9E9E9E">**Et**</span> il est clairement visible (contraste élevé)

---

## 23. Analytics tracking précis par type

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité est diffusée

<span style="color: #FF9800">**Quand**</span> un événement se produit

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> les statistiques RoadWave globales:

    | Durée pub | Taux complétion moyen |
    |---|---|
    | 10s | 65% |
    | 15s | 55% |
    | 30s | 45% |
    | 45s | 30% |
    | 60s | 20% |


<span style="color: #FF9800">**Quand**</span> un publicitaire consulte les recommandations

<span style="color: #4CAF50">**Alors**</span> le sweet spot affiché est "15-30 secondes"
<span style="color: #9E9E9E">**Et**</span> l'explication est "Meilleur compromis engagement/message"

---

## 25. Optimisation durée selon taux de skip campagne

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne de 60s a un taux de skip de 85%

<span style="color: #FF9800">**Quand**</span> le publicitaire consulte les recommandations

<span style="color: #4CAF50">**Alors**</span> le système suggère:

---

## 26. Coût effectif moyen (CEM) calculé

<span style="color: #2196F3">**Étant donné**</span> 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€ |


<span style="color: #FF9800">**Quand**</span> je calcule le coût effectif moyen

<span style="color: #4CAF50">**Alors**</span> CEM = 160€ / 6000 impressions = 0.027€/impression
<span style="color: #9E9E9E">**Et**</span> cette métrique aide à comparer avec CPM industrie

---

## 27. Publicité non skippable interdite

<span style="color: #2196F3">**Étant donné**</span> qu'un publicitaire demande "Publicité non skippable"

<span style="color: #FF9800">**Quand**</span> il configure sa campagne

<span style="color: #4CAF50">**Alors**</span> cette option n'existe pas
<span style="color: #9E9E9E">**Et**</span> toutes les publicités sont obligatoirement skippables après 5s minimum

---

## 28. Délai minimal jamais <3 secondes

<span style="color: #2196F3">**Étant donné**</span> qu'un admin essaie de configurer le délai à 2 secondes

<span style="color: #FF9800">**Quand**</span> il valide le paramètre

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Délai minimal: 3 secondes minimum"

---

## 29. Délai minimal jamais >10 secondes

<span style="color: #2196F3">**Étant donné**</span> qu'un admin essaie de configurer le délai à 15 secondes

<span style="color: #FF9800">**Quand**</span> il valide le paramètre

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche: "Délai maximal: 10 secondes maximum"

---

## 30. 📋 Plan: Facturation selon durée écoutée

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité de 30s est diffusée

<span style="color: #FF9800">**Quand**</span> j'écoute pendant <duree>s (<pourcentage>%)

<span style="color: #4CAF50">**Alors**</span> le type d'écoute est <type>
<span style="color: #9E9E9E">**Et**</span> le coût facturé est <cout>€

**📊 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

<span style="color: #2196F3">**Étant donné**</span> <completes> écoutes complètes à 0.05€
<span style="color: #9E9E9E">**Et**</span> <partielles> écoutes partielles à 0.02€
<span style="color: #9E9E9E">**Et**</span> <skips> skips immédiats à 0€

<span style="color: #FF9800">**Quand**</span> je calcule le budget total consommé

<span style="color: #4CAF50">**Alors**</span> le résultat est <budget_total>€

**📊 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é

<span style="color: #2196F3">**Étant donné**</span> que le délai minimal est configuré à <delai>s

<span style="color: #FF9800">**Quand**</span> j'écoute pendant <temps_ecoute>s

<span style="color: #4CAF50">**Alors**</span> le bouton "Passer" est <visible>

**📊 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 |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un compte publicitaire est connecté
> <span style="color: #9E9E9E">**Et**</span> qu'une campagne active est en cours
## 1. Dashboard budget temps réel

<span style="color: #2196F3">**Étant donné**</span> que ma campagne a un budget de 300€
<span style="color: #9E9E9E">**Et**</span> que j'ai consommé 220€

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard budget

<span style="color: #4CAF50">**Alors**</span> je vois:

    | Métrique | Valeur |
    |---|---|
    | Budget total | 300€ |
    | Budget consommé | 220€ |
    | Budget restant | 80€ |
    | Pourcentage | 73% consommé |


---

## 2. Jauge visuelle budget consommé

<span style="color: #2196F3">**Étant donné**</span> que j'ai consommé 220€ sur 300€

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard

<span style="color: #4CAF50">**Alors**</span> une jauge de progression affiche 73%
<span style="color: #9E9E9E">**Et**</span> la couleur est orange (seuil 50-80%)
<span style="color: #9E9E9E">**Et**</span> un indicateur "80€ restants" est affiché clairement

---

## 3. Couleur jauge selon seuil

<span style="color: #2196F3">**Étant donné**</span> un budget de 300€

<span style="color: #FF9800">**Quand**</span> j'ai consommé 150€ (50%)

<span style="color: #4CAF50">**Alors**</span> la jauge est verte

<span style="color: #FF9800">**Quand**</span> j'ai consommé 240€ (80%)

<span style="color: #4CAF50">**Alors**</span> la jauge est orange

<span style="color: #FF9800">**Quand**</span> j'ai consommé 285€ (95%)

<span style="color: #4CAF50">**Alors**</span> la jauge est rouge
<span style="color: #9E9E9E">**Et**</span> un message "Budget presque épuisé" s'affiche

---

## 4. Projection épuisement budget

<span style="color: #2196F3">**Étant donné**</span> que j'ai consommé 220€ en 10 jours
<span style="color: #9E9E9E">**Et**</span> qu'il reste 4 jours de campagne

<span style="color: #FF9800">**Quand**</span> le système calcule la projection

<span style="color: #4CAF50">**Alors**</span> la consommation quotidienne moyenne est 22€/jour
<span style="color: #9E9E9E">**Et**</span> la projection affiche "Budget épuisé dans 3.6 jours"
<span style="color: #9E9E9E">**Et**</span> un avertissement "Campagne s'arrêtera avant la fin prévue" s'affiche

---

## 5. Projection avec budget suffisant

<span style="color: #2196F3">**Étant donné**</span> que j'ai consommé 100€ en 10 jours
<span style="color: #9E9E9E">**Et**</span> qu'il reste 4 jours de campagne
<span style="color: #9E9E9E">**Et**</span> que le budget total est 300€

<span style="color: #FF9800">**Quand**</span> le système calcule la projection

<span style="color: #4CAF50">**Alors**</span> la consommation quotidienne moyenne est 10€/jour
<span style="color: #9E9E9E">**Et**</span> la projection affiche "Budget suffisant pour toute la campagne"
<span style="color: #9E9E9E">**Et**</span> le budget restant estimé à la fin est 160€

---

## 6. Alerte 80% budget consommé

<span style="color: #2196F3">**Étant donné**</span> que mon budget est de 300€

<span style="color: #FF9800">**Quand**</span> je consomme 240€ (80%)

<span style="color: #4CAF50">**Alors**</span> je reçois immédiatement un email:
<span style="color: #9E9E9E">**Et**</span> une notification push est envoyée
<span style="color: #9E9E9E">**Et**</span> une notification in-app s'affiche

---

## 7. Alerte 90% budget consommé

<span style="color: #2196F3">**Étant donné**</span> que mon budget est de 300€

<span style="color: #FF9800">**Quand**</span> je consomme 270€ (90%)

<span style="color: #4CAF50">**Alors**</span> je reçois immédiatement un email:

---

## 8. Alerte budget épuisé (100%)

<span style="color: #2196F3">**Étant donné**</span> que mon budget est de 300€

<span style="color: #FF9800">**Quand**</span> je consomme les 300€ (100%)

<span style="color: #4CAF50">**Alors**</span> je reçois immédiatement un email:
<span style="color: #9E9E9E">**Et**</span> la campagne est automatiquement mise en pause
<span style="color: #9E9E9E">**Et**</span> plus aucune diffusion ne se produit

---

## 9. Pause manuelle de campagne

<span style="color: #2196F3">**Étant donné**</span> que ma campagne est active
<span style="color: #9E9E9E">**Et**</span> qu'il reste 150€ de budget

<span style="color: #FF9800">**Quand**</span> je clique sur "Mettre en pause"

<span style="color: #4CAF50">**Alors**</span> le statut passe à "En pause"
<span style="color: #9E9E9E">**Et**</span> les diffusions s'arrêtent immédiatement
<span style="color: #9E9E9E">**Et**</span> le budget de 150€ est conservé
<span style="color: #9E9E9E">**Et**</span> je peux réactiver la campagne plus tard

---

## 10. Reprise campagne pausée

<span style="color: #2196F3">**Étant donné**</span> que ma campagne est en pause
<span style="color: #9E9E9E">**Et**</span> qu'il reste 150€ de budget

<span style="color: #FF9800">**Quand**</span> je clique sur "Reprendre la campagne"

<span style="color: #4CAF50">**Alors**</span> le statut passe à "Active"
<span style="color: #9E9E9E">**Et**</span> les diffusions reprennent immédiatement
<span style="color: #9E9E9E">**Et**</span> le budget restant de 150€ continue de se consommer

---

## 11. Prolongation campagne avec recharge

<span style="color: #2196F3">**Étant donné**</span> que ma campagne se termine dans 2 jours
<span style="color: #9E9E9E">**Et**</span> qu'il reste 20€ de budget

<span style="color: #FF9800">**Quand**</span> je clique sur "Prolonger la campagne"
<span style="color: #9E9E9E">**Et**</span> que j'ajoute 200€ supplémentaires

<span style="color: #4CAF50">**Alors**</span> le budget total passe à 220€
<span style="color: #9E9E9E">**Et**</span> la date de fin peut être prolongée de 10 jours
<span style="color: #9E9E9E">**Et**</span> un nouveau paiement Mangopay de 200€ est traité

---

## 12. Recharge automatique activée

<span style="color: #2196F3">**Étant donné**</span> que j'ai configuré la recharge automatique
<span style="color: #9E9E9E">**Et**</span> que le seuil est fixé à 10% (30€ sur budget 300€)
<span style="color: #9E9E9E">**Et**</span> que le montant de recharge est 100€

<span style="color: #FF9800">**Quand**</span> le budget restant passe sous 30€

<span style="color: #4CAF50">**Alors**</span> une recharge automatique de 100€ est déclenchée
<span style="color: #9E9E9E">**Et**</span> ma carte bancaire est débitée via Mangopay
<span style="color: #9E9E9E">**Et**</span> le budget total passe à budget_restant + 100€
<span style="color: #9E9E9E">**Et**</span> je reçois un email de confirmation

---

## 13. Échec recharge automatique (carte expirée)

<span style="color: #2196F3">**Étant donné**</span> que la recharge automatique est activée
<span style="color: #9E9E9E">**Et**</span> que ma carte bancaire a expiré

<span style="color: #FF9800">**Quand**</span> le budget passe sous le seuil de 10%

<span style="color: #4CAF50">**Alors**</span> la recharge automatique échoue
<span style="color: #9E9E9E">**Et**</span> je reçois un email urgent:
<span style="color: #9E9E9E">**Et**</span> la campagne continue jusqu'à épuisement du budget restant

---

## 14. Modification ciblage si budget <50% consommé

<span style="color: #2196F3">**Étant donné**</span> que j'ai consommé 120€ sur 300€ (40%)

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier le ciblage géographique

<span style="color: #4CAF50">**Alors**</span> la modification est autorisée
<span style="color: #9E9E9E">**Et**</span> le ciblage est mis à jour immédiatement
<span style="color: #9E9E9E">**Et**</span> les nouvelles diffusions utilisent le nouveau ciblage

---

## 15. Blocage modification ciblage si budget >50% consommé

<span style="color: #2196F3">**Étant donné**</span> que j'ai consommé 180€ sur 300€ (60%)

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier le ciblage géographique

<span style="color: #4CAF50">**Alors**</span> une erreur s'affiche:

---

## 16. Modification audio nécessite nouvelle validation

<span style="color: #2196F3">**Étant donné**</span> que ma campagne est active

<span style="color: #FF9800">**Quand**</span> je veux modifier le fichier audio

<span style="color: #4CAF50">**Alors**</span> un message s'affiche:

---

## 17. Modification plages horaires autorisée

<span style="color: #2196F3">**Étant donné**</span> que ma campagne cible 7h-9h et 17h-19h

<span style="color: #FF9800">**Quand**</span> je modifie pour cibler 12h-14h aussi

<span style="color: #4CAF50">**Alors**</span> la modification est appliquée immédiatement
<span style="color: #9E9E9E">**Et**</span> les diffusions suivantes incluent la nouvelle plage
<span style="color: #9E9E9E">**Et**</span> aucune re-validation n'est nécessaire

---

## 18. Historique consommation budget jour par jour

<span style="color: #2196F3">**Étant donné**</span> que ma campagne a duré 10 jours

<span style="color: #FF9800">**Quand**</span> je consulte l'historique

<span style="color: #4CAF50">**Alors**</span> je vois un graphique avec:

    | Jour | Consommation | Cumulé |
    |---|---|---|
    | 1 | 22€ | 22€ |
    | 2 | 25€ | 47€ |
    | 3 | 20€ | 67€ |
    | ... | ... | ... |
    | 10 | 18€ | 220€ |

<span style="color: #9E9E9E">**Et**</span> je peux identifier les pics de consommation

---

## 19. Notification fin de campagne programmée

<span style="color: #2196F3">**Étant donné**</span> que ma campagne se termine le 14/02

<span style="color: #FF9800">**Quand**</span> la date de fin est atteinte

<span style="color: #4CAF50">**Alors**</span> je reçois un email:

---

## 20. Remboursement budget non utilisé

<span style="color: #2196F3">**Étant donné**</span> que ma campagne avait 300€ de budget
<span style="color: #9E9E9E">**Et**</span> qu'elle s'est terminée avec 280€ consommés

<span style="color: #FF9800">**Quand**</span> la campagne se termine (date ou épuisement)

<span style="color: #4CAF50">**Alors**</span> un remboursement de 20€ est initié via Mangopay
<span style="color: #9E9E9E">**Et**</span> le délai est de 5-7 jours ouvrés
<span style="color: #9E9E9E">**Et**</span> je reçois une notification de confirmation

---

## 21. Aucun remboursement si budget entièrement consommé

<span style="color: #2196F3">**Étant donné**</span> que ma campagne avait 300€ de budget
<span style="color: #9E9E9E">**Et**</span> qu'elle s'est terminée avec 300€ consommés

<span style="color: #FF9800">**Quand**</span> la campagne se termine

<span style="color: #4CAF50">**Alors**</span> aucun remboursement n'est initié
<span style="color: #9E9E9E">**Et**</span> le message final indique "Budget entièrement utilisé"

---

## 22. Statistiques comparatives budget vs objectif

<span style="color: #2196F3">**Étant donné**</span> que j'avais défini un objectif de 5000 impressions
<span style="color: #9E9E9E">**Et**</span> que mon budget était 300€

<span style="color: #FF9800">**Quand**</span> je consulte les statistiques finales

<span style="color: #4CAF50">**Alors**</span> je vois:

    | Métrique | Objectif | Réalisé | Écart |
    |---|---|---|---|
    | Impressions | 5000 | 6000 | +20% |
    | Budget | 300€ | 280€ | -7% |
    | Coût/impression | 0.06€ | 0.047€ | -22% |

<span style="color: #9E9E9E">**Et**</span> une analyse "✅ Objectifs dépassés avec budget optimisé"

---

## 23. Export rapport financier détaillé

<span style="color: #2196F3">**Étant donné**</span> que je veux analyser mes dépenses

<span style="color: #FF9800">**Quand**</span> je clique sur "Exporter rapport financier"

<span style="color: #4CAF50">**Alors**</span> je télécharge un CSV avec:

    | Colonne |
    |---|
    | Date/Heure |
    | Type écoute |
    | Coût unitaire |
    | Zone géographique |
    | Utilisateur (anonyme) |
    | Durée écoutée |

<span style="color: #9E9E9E">**Et**</span> je peux l'importer dans Excel pour analyses

---

## 24. Tableau de bord multi-campagnes

<span style="color: #2196F3">**Étant donné**</span> que j'ai 3 campagnes actives

<span style="color: #FF9800">**Quand**</span> je consulte la vue d'ensemble

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> un badge alerte rouge sur la campagne B

---

## 25. Alerte consolidée multi-campagnes

<span style="color: #2196F3">**Étant donné**</span> que j'ai 5 campagnes actives
<span style="color: #9E9E9E">**Et**</span> que 2 campagnes ont >80% budget consommé

<span style="color: #FF9800">**Quand**</span> je reçois les notifications

<span style="color: #4CAF50">**Alors**</span> un email consolidé unique est envoyé:
<span style="color: #9E9E9E">**Et**</span> je ne reçois pas 2 emails séparés (évite spam)

---

## 26. Configuration seuils alertes personnalisés

<span style="color: #2196F3">**Étant donné**</span> que je configure mes préférences d'alerte

<span style="color: #FF9800">**Quand**</span> je définis les seuils:

    | Seuil | Valeur |
    |---|---|
    | Alerte 1 | 70% |
    | Alerte 2 | 85% |
    | Alerte 3 | 95% |


<span style="color: #4CAF50">**Alors**</span> je reçois des alertes à 70%, 85% et 95%
<span style="color: #9E9E9E">**Et**</span> non aux seuils par défaut 80%, 90%, 100%

---

## 27. Désactivation alertes email

<span style="color: #2196F3">**Étant donné**</span> que je préfère uniquement les notifications in-app

<span style="color: #FF9800">**Quand**</span> je désactive les alertes email dans mes préférences

<span style="color: #4CAF50">**Alors**</span> je ne reçois plus d'emails d'alerte budget
<span style="color: #F44336">**Mais**</span> les notifications in-app continuent
<span style="color: #9E9E9E">**Et**</span> les alertes critiques (échec paiement) sont toujours envoyées par email

---

## 28. 📋 Plan: Couleur jauge selon pourcentage consommé

<span style="color: #2196F3">**Étant donné**</span> un budget de 300€

<span style="color: #FF9800">**Quand**</span> j'ai consommé <montant>€ (<pourcentage>%)

<span style="color: #4CAF50">**Alors**</span> la couleur de la jauge est <couleur>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> un budget de 300€
<span style="color: #9E9E9E">**Et**</span> une consommation actuelle de <consomme>€
<span style="color: #9E9E9E">**Et**</span> une durée écoulée de <jours_ecoules> jours

<span style="color: #FF9800">**Quand**</span> je calcule la consommation quotidienne moyenne

<span style="color: #4CAF50">**Alors**</span> elle est de <conso_jour>€/jour
<span style="color: #9E9E9E">**Et**</span> le budget sera épuisé dans <jours_restants> 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

<span style="color: #2196F3">**Étant donné**</span> un budget de 500€

<span style="color: #FF9800">**Quand**</span> je consomme <montant>€ (<pourcentage>%)

<span style="color: #4CAF50">**Alors**</span> je reçois une alerte <niveau>

**📊 Exemples de données:**

| montant | pourcentage | niveau |
|---|---|---|
| 350 | 70 | aucune |
| 400 | 80 | alerte 80% |
| 450 | 90 | alerte 90% |
| 500 | 100 | budget épuisé |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un utilisateur gratuit est connecté
## 1. Fréquence par défaut 1 pub / 5 contenus

<span style="color: #2196F3">**Étant donné**</span> que la fréquence par défaut est configurée à 1/5
<span style="color: #9E9E9E">**Et**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> j'écoute 5 contenus

<span style="color: #4CAF50">**Alors**</span> 1 publicité est insérée après le 5ème contenu

<span style="color: #FF9800">**Quand**</span> j'écoute 10 contenus

<span style="color: #4CAF50">**Alors**</span> 2 publicités sont insérées (après les contenus 5 et 10)

---

## 2. Aucune publicité pour utilisateurs Premium

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium

<span style="color: #FF9800">**Quand**</span> j'écoute 100 contenus

<span style="color: #4CAF50">**Alors**</span> aucune publicité n'est insérée
<span style="color: #9E9E9E">**Et**</span> je bénéficie d'une expérience sans interruption publicitaire

---

## 3. Fréquence paramétrable par admin (1/3)

<span style="color: #2196F3">**Étant donné**</span> que l'admin configure la fréquence à 1/3
<span style="color: #9E9E9E">**Et**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> j'écoute 6 contenus

<span style="color: #4CAF50">**Alors**</span> 2 publicités sont insérées (après contenus 3 et 6)

---

## 4. Fréquence paramétrable par admin (1/10)

<span style="color: #2196F3">**Étant donné**</span> que l'admin configure la fréquence à 1/10
<span style="color: #9E9E9E">**Et**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> j'écoute 20 contenus

<span style="color: #4CAF50">**Alors**</span> 2 publicités sont insérées (après contenus 10 et 20)

---

## 5. Jamais d'interruption d'un contenu en cours

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu de 10 minutes
<span style="color: #9E9E9E">**Et**</span> que je suis à 5 minutes de lecture
<span style="color: #9E9E9E">**Et**</span> qu'une publicité devrait être insérée selon la fréquence

<span style="color: #FF9800">**Quand**</span> le système vérifie l'insertion

<span style="color: #4CAF50">**Alors**</span> la publicité attend la fin du contenu actuel
<span style="color: #9E9E9E">**Et**</span> elle s'insère pendant le délai de transition (2s)
<span style="color: #9E9E9E">**Et**</span> le contenu n'est jamais interrompu

---

## 6. Insertion entre deux contenus uniquement

<span style="color: #2196F3">**Étant donné**</span> que le contenu "A" se termine
<span style="color: #9E9E9E">**Et**</span> que le délai de transition de 2s démarre

<span style="color: #FF9800">**Quand**</span> le système détecte qu'une publicité doit être insérée

<span style="color: #4CAF50">**Alors**</span> le message "Publicité (30s)" s'affiche
<span style="color: #9E9E9E">**Et**</span> la publicité démarre après les 2 secondes
<span style="color: #9E9E9E">**Et**</span> l'enchaînement est naturel et fluide

---

## 7. Rotation limite 3 fois/jour par utilisateur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a entendu la publicité "A" 3 fois aujourd'hui

<span style="color: #FF9800">**Quand**</span> le système sélectionne une nouvelle publicité à diffuser

<span style="color: #4CAF50">**Alors**</span> la publicité "A" n'est plus éligible pour cet utilisateur aujourd'hui
<span style="color: #9E9E9E">**Et**</span> une autre publicité "B" est sélectionnée
<span style="color: #9E9E9E">**Et**</span> cela évite la saturation publicitaire

---

## 8. Compteur de diffusions par pub et par utilisateur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur écoute la pub "RestaurantX"

<span style="color: #FF9800">**Quand**</span> la diffusion se termine

<span style="color: #4CAF50">**Alors**</span> un compteur Redis "pub:RestaurantX:user:123:count" s'incrémente
<span style="color: #9E9E9E">**Et**</span> le TTL est de 24h (reset à minuit)

<span style="color: #FF9800">**Quand**</span> le compteur atteint 3

<span style="color: #4CAF50">**Alors**</span> la pub "RestaurantX" est exclue des prochaines sélections aujourd'hui

---

## 9. Limite max 6 pubs/heure par utilisateur

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a entendu 6 publicités dans la dernière heure

<span style="color: #FF9800">**Quand**</span> le système devrait insérer une 7ème pub

<span style="color: #4CAF50">**Alors**</span> l'insertion est reportée à l'heure suivante
<span style="color: #9E9E9E">**Et**</span> un compteur horaire Redis "pub:user:123:hourly" est vérifié
<span style="color: #9E9E9E">**Et**</span> cela évite le spam publicitaire

---

## 10. Ciblage géographique prioritaire - Point GPS

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité cible un point GPS à 2km de ma position
<span style="color: #9E9E9E">**Et**</span> qu'une autre publicité cible ma ville entière

<span style="color: #FF9800">**Quand**</span> le système sélectionne une publicité

<span style="color: #4CAF50">**Alors**</span> la publicité point GPS est priorisée (score géo plus élevé)
<span style="color: #9E9E9E">**Et**</span> le ciblage précis est favorisé

---

## 11. Ciblage géographique prioritaire - Hiérarchie

<span style="color: #2196F3">**Étant donné**</span> que 4 publicités sont éligibles:

    | Publicité | Zone | Distance |
    |---|---|---|
    | A | Point GPS | 1km |
    | B | Ville | 0km |
    | C | Département | 0km |
    | D | National | N/A |


<span style="color: #FF9800">**Quand**</span> le système sélectionne selon priorité géographique

<span style="color: #4CAF50">**Alors**</span> l'ordre de priorité est: A > B > C > D
<span style="color: #9E9E9E">**Et**</span> la publicité A (Point GPS, la plus précise) est diffusée

---

## 12. Ciblage centres d'intérêt secondaire

<span style="color: #2196F3">**Étant donné**</span> que 2 publicités ciblent ma zone géographique:

    | Publicité | Tags | Mes jauges |
    |---|---|---|
    | A | Automobile | 80% |
    | B | Voyage | 40% |


<span style="color: #FF9800">**Quand**</span> le système applique le score centres d'intérêt

<span style="color: #4CAF50">**Alors**</span> la publicité A est favorisée (meilleur match jauges)
<span style="color: #9E9E9E">**Et**</span> le ciblage thématique affine la sélection

---

## 13. Ciblage horaire strict

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne cible uniquement 7h-9h
<span style="color: #9E9E9E">**Et**</span> qu'il est 10h30

<span style="color: #FF9800">**Quand**</span> le système sélectionne une publicité

<span style="color: #4CAF50">**Alors**</span> cette campagne n'est PAS éligible
<span style="color: #9E9E9E">**Et**</span> seules les campagnes "toute la journée" ou avec plage horaire actuelle sont considérées

---

## 14. Ciblage horaire pendant plage active

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne cible 7h-9h et 17h-19h
<span style="color: #9E9E9E">**Et**</span> qu'il est 8h15

<span style="color: #FF9800">**Quand**</span> le système sélectionne une publicité

<span style="color: #4CAF50">**Alors**</span> cette campagne est éligible
<span style="color: #9E9E9E">**Et**</span> elle peut être diffusée

---

## 15. Normalisation volume audio -14 LUFS

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité est uploadée avec volume trop élevé (-6 LUFS)

<span style="color: #FF9800">**Quand**</span> le système encode l'audio via FFmpeg

<span style="color: #4CAF50">**Alors**</span> le volume est normalisé automatiquement à -14 LUFS
<span style="color: #9E9E9E">**Et**</span> le publicitaire reçoit une notification "Volume audio ajusté pour conformité"
<span style="color: #9E9E9E">**Et**</span> cela évite l'effet "pub trop forte" frustrant

---

## 16. Validation volume audio lors encodage

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité est soumise

<span style="color: #FF9800">**Quand**</span> FFmpeg encode le fichier

<span style="color: #4CAF50">**Alors**</span> une commande loudnorm est appliquée:
<span style="color: #9E9E9E">**Et**</span> le fichier final respecte le standard broadcast -14 LUFS

---

## 17. Sélection aléatoire si critères équivalents

<span style="color: #2196F3">**Étant donné**</span> que 3 publicités ont le même score géo
<span style="color: #9E9E9E">**Et**</span> qu'elles ont toutes des jauges centres d'intérêt équivalentes
<span style="color: #9E9E9E">**Et**</span> qu'aucune n'a été diffusée 3 fois aujourd'hui

<span style="color: #FF9800">**Quand**</span> le système sélectionne une publicité

<span style="color: #4CAF50">**Alors**</span> une sélection aléatoire équitable est faite
<span style="color: #9E9E9E">**Et**</span> chaque campagne a 33% de chances d'être diffusée

---

## 18. Exclusion publicités avec budget épuisé

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne "A" a épuisé son budget
<span style="color: #9E9E9E">**Et**</span> qu'une campagne "B" a encore du budget disponible

<span style="color: #FF9800">**Quand**</span> le système sélectionne une publicité

<span style="color: #4CAF50">**Alors**</span> seule la campagne "B" est éligible
<span style="color: #9E9E9E">**Et**</span> la campagne "A" est automatiquement exclue

---

## 19. Exclusion publicités hors dates de campagne

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne "A" est programmée du 01/02 au 14/02
<span style="color: #9E9E9E">**Et**</span> que nous sommes le 20/01

<span style="color: #FF9800">**Quand**</span> le système sélectionne une publicité

<span style="color: #4CAF50">**Alors**</span> la campagne "A" n'est pas éligible
<span style="color: #9E9E9E">**Et**</span> seules les campagnes actives aujourd'hui sont considérées

---

## 20. Publicité visible uniquement dans zone géographique

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité cible "Marseille uniquement"
<span style="color: #9E9E9E">**Et**</span> que je suis à Lyon

<span style="color: #FF9800">**Quand**</span> le système sélectionne une publicité

<span style="color: #4CAF50">**Alors**</span> cette publicité n'est jamais éligible pour moi
<span style="color: #9E9E9E">**Et**</span> je ne la verrai jamais tant que je reste à Lyon

---

## 21. Tracking compteur horaire avec TTL

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur entend une pub à 10h05

<span style="color: #FF9800">**Quand**</span> le compteur horaire est incrémenté

<span style="color: #4CAF50">**Alors**</span> la clé Redis "pub:user:123:hourly:2026012110" est créée
<span style="color: #9E9E9E">**Et**</span> le TTL est de 1 heure (expire à 11h05)
<span style="color: #9E9E9E">**Et**</span> le système compte les pubs dans la fenêtre glissante d'1h

---

## 22. Reset compteur quotidien à minuit

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a entendu la pub "A" 3 fois le 20/01

<span style="color: #FF9800">**Quand**</span> minuit passe et on est le 21/01

<span style="color: #4CAF50">**Alors**</span> le compteur "pub:A:user:123:count" est expiré (TTL 24h)
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut à nouveau entendre la pub "A" jusqu'à 3 fois

---

## 23. Aucune pub si aucune campagne éligible

<span style="color: #2196F3">**Étant donné**</span> qu'aucune campagne n'a de budget disponible

<span style="color: #FF9800">**Quand**</span> le système devrait insérer une publicité

<span style="color: #4CAF50">**Alors**</span> aucune pub n'est insérée
<span style="color: #9E9E9E">**Et**</span> l'enchaînement de contenus continue normalement
<span style="color: #9E9E9E">**Et**</span> le prochain contenu démarre directement

---

## 24. Priorisation campagnes avec budget important restant

<span style="color: #2196F3">**Étant donné**</span> que 2 campagnes sont éligibles:

    | Campagne | Budget restant | Jours restants |
    |---|---|---|
    | A | 500€ | 2j |
    | B | 50€ | 10j |


<span style="color: #FF9800">**Quand**</span> le système applique la priorisation budgétaire

<span style="color: #4CAF50">**Alors**</span> la campagne A est légèrement favorisée (urgence dépense)
<span style="color: #9E9E9E">**Et**</span> cela aide à épuiser les budgets avant fin de campagne

---

## 25. Log des sélections pour analytics

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité "RestaurantX" est sélectionnée

<span style="color: #FF9800">**Quand**</span> elle est diffusée à l'utilisateur "123"

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> cela permet l'analytics publicitaire

---

## 26. Détection changement statut utilisateur (gratuit → premium)

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit
<span style="color: #9E9E9E">**Et**</span> que j'entends des publicités

<span style="color: #FF9800">**Quand**</span> je souscris à Premium

<span style="color: #4CAF50">**Alors**</span> le système détecte le changement de statut immédiatement
<span style="color: #9E9E9E">**Et**</span> plus aucune publicité n'est insérée dès le prochain contenu
<span style="color: #9E9E9E">**Et**</span> mon expérience devient sans pub instantanément

---

## 27. Interface admin pour ajuster fréquence globale

<span style="color: #2196F3">**Étant donné**</span> que je suis admin RoadWave

<span style="color: #FF9800">**Quand**</span> j'accède aux paramètres publicitaires

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le changement s'applique en temps réel à tous les utilisateurs

---

## 28. A/B testing fréquence sur cohortes utilisateurs

<span style="color: #2196F3">**Étant donné**</span> que l'admin active un test A/B

<span style="color: #FF9800">**Quand**</span> 50% des utilisateurs ont fréquence 1/5
<span style="color: #9E9E9E">**Et**</span> 50% des utilisateurs ont fréquence 1/7

<span style="color: #4CAF50">**Alors**</span> 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€ |

<span style="color: #9E9E9E">**Et**</span> l'admin peut identifier la fréquence optimale

---

## 29. 📋 Plan: Insertion publicité selon fréquence

<span style="color: #2196F3">**Étant donné**</span> que la fréquence est <frequence>

<span style="color: #FF9800">**Quand**</span> j'écoute <contenus> contenus

<span style="color: #4CAF50">**Alors**</span> <pubs> 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

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité cible <type_zone>

<span style="color: #FF9800">**Quand**</span> le système calcule le score géographique

<span style="color: #4CAF50">**Alors**</span> la priorité est <score>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité a été entendue <fois> fois aujourd'hui

<span style="color: #FF9800">**Quand**</span> le système vérifie l'éligibilité

<span style="color: #4CAF50">**Alors**</span> la publicité est <eligible>

**📊 Exemples de données:**

| fois | eligible |
|---|---|
| 0 | éligible |
| 1 | éligible |
| 2 | éligible |
| 3 | non éligible |
| 4 | non éligible |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un compte publicitaire est connecté
> <span style="color: #9E9E9E">**Et**</span> qu'une campagne active est en cours
## 1. Dashboard temps réel avec métriques essentielles

<span style="color: #2196F3">**Étant donné**</span> que ma campagne a généré 1000 diffusions

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que ma publicité a été diffusée 2500 fois

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard

<span style="color: #4CAF50">**Alors**</span> le compteur "Impressions" affiche 2500
<span style="color: #9E9E9E">**Et**</span> il s'incrémente en temps réel à chaque nouvelle diffusion

---

## 3. Calcul écoutes complètes (>80%)

<span style="color: #2196F3">**Étant donné**</span> que ma publicité de 30s a été:

    | Durée écoutée | Nombre |
    |---|---|
    | 25s (83%) | 300 |
    | 20s (67%) | 200 |
    | 10s (33%) | 150 |
    | 5s (17%) | 50 |


<span style="color: #FF9800">**Quand**</span> je consulte les écoutes complètes

<span style="color: #4CAF50">**Alors**</span> le compteur affiche 300 (uniquement ≥80%)
<span style="color: #9E9E9E">**Et**</span> le taux d'écoute complète est de 43% (300/700)

---

## 4. Calcul taux de skip

<span style="color: #2196F3">**Étant donné**</span> 1000 diffusions totales
<span style="color: #9E9E9E">**Et**</span> 400 écoutes complètes

<span style="color: #FF9800">**Quand**</span> je consulte le taux de skip

<span style="color: #4CAF50">**Alors**</span> il affiche 60% ((1000-400)/1000)
<span style="color: #9E9E9E">**Et**</span> il est calculé comme (total - complètes) / total

---

## 5. Durée moyenne d'écoute calculée

<span style="color: #2196F3">**Étant donné**</span> que ma publicité de 30s a été écoutée:

    | Durée | Nombre d'utilisateurs |
    |---|---|
    | 30s | 400 |
    | 20s | 300 |
    | 10s | 200 |
    | 5s | 100 |


<span style="color: #FF9800">**Quand**</span> je consulte la durée moyenne

<span style="color: #4CAF50">**Alors**</span> le calcul est: (30×400 + 20×300 + 10×200 + 5×100) / 1000
<span style="color: #9E9E9E">**Et**</span> le résultat affiché est 21s

---

## 6. Métriques de likes sur publicité

<span style="color: #2196F3">**Étant donné**</span> que 50 utilisateurs ont liké ma publicité

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard

<span style="color: #4CAF50">**Alors**</span> le compteur "Likes" affiche 50
<span style="color: #9E9E9E">**Et**</span> un taux de like de 5% est calculé (50/1000 impressions)
<span style="color: #9E9E9E">**Et**</span> cela indique une forte appréciation du contenu

---

## 7. Métriques d'abonnements générés

<span style="color: #2196F3">**Étant donné**</span> que 10 utilisateurs se sont abonnés après avoir entendu ma pub

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard

<span style="color: #4CAF50">**Alors**</span> le compteur "Abonnements" affiche 10
<span style="color: #9E9E9E">**Et**</span> un taux de conversion de 1% est calculé (10/1000)
<span style="color: #9E9E9E">**Et**</span> cela représente un engagement très fort

---

## 8. Calcul coût par écoute (CPE)

<span style="color: #2196F3">**Étant donné**</span> que j'ai dépensé 200€
<span style="color: #9E9E9E">**Et**</span> obtenu 4000 écoutes complètes

<span style="color: #FF9800">**Quand**</span> je consulte le coût par écoute

<span style="color: #4CAF50">**Alors**</span> le CPE affiché est 0.05€ (200/4000)
<span style="color: #9E9E9E">**Et**</span> il correspond au tarif standard RoadWave

---

## 9. Répartition géographique avec heatmap

<span style="color: #2196F3">**Étant donné**</span> que ma campagne cible le département du Var
<span style="color: #9E9E9E">**Et**</span> que j'ai 1000 diffusions réparties:

    | Zone | Diffusions | Pourcentage |
    |---|---|---|
    | Toulon | 400 | 40% |
    | Hyères | 250 | 25% |
    | Fréjus | 200 | 20% |
    | Autres | 150 | 15% |


<span style="color: #FF9800">**Quand**</span> je consulte la heatmap géographique

<span style="color: #4CAF50">**Alors**</span> une carte Leaflet affiche les zones avec intensité proportionnelle
<span style="color: #9E9E9E">**Et**</span> Toulon apparaît en rouge foncé (forte concentration)
<span style="color: #9E9E9E">**Et**</span> les autres villes en dégradé orange/jaune

---

## 10. Répartition horaire avec graphique

<span style="color: #2196F3">**Étant donné**</span> que ma campagne cible les plages 7h-9h et 17h-19h
<span style="color: #9E9E9E">**Et**</span> que j'ai 1000 diffusions:

    | Plage horaire | Diffusions |
    |---|---|
    | 7h-8h | 300 |
    | 8h-9h | 250 |
    | 17h-18h | 280 |
    | 18h-19h | 170 |


<span style="color: #FF9800">**Quand**</span> je consulte le graphique horaire

<span style="color: #4CAF50">**Alors**</span> un histogramme Chart.js affiche les 4 barres
<span style="color: #9E9E9E">**Et**</span> je peux identifier que 7h-8h est le pic d'écoute
<span style="color: #9E9E9E">**Et**</span> optimiser mes futures campagnes sur cette plage

---

## 11. Taux de complétion par tranche d'âge

<span style="color: #2196F3">**Étant donné**</span> que ma campagne est Tout Public
<span style="color: #9E9E9E">**Et**</span> 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% |


<span style="color: #FF9800">**Quand**</span> je consulte l'analyse par âge

<span style="color: #4CAF50">**Alors**</span> je vois que les 25-34 ans ont le meilleur taux (50%)
<span style="color: #9E9E9E">**Et**</span> je peux cibler cette tranche pour mes prochaines campagnes

---

## 12. Comparatif de campagnes A/B testing

<span style="color: #2196F3">**Étant donné**</span> 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€ |


<span style="color: #FF9800">**Quand**</span> je consulte le comparatif

<span style="color: #4CAF50">**Alors**</span> je vois que la campagne B performe mieux
<span style="color: #9E9E9E">**Et**</span> le tableau recommande "Campagne B: +50% écoutes, -33% CPE"
<span style="color: #9E9E9E">**Et**</span> je peux allouer plus de budget à la campagne B

---

## 13. Export données CSV pour analyse externe

<span style="color: #2196F3">**Étant donné**</span> que je veux analyser mes données dans Excel

<span style="color: #FF9800">**Quand**</span> je clique sur "Exporter CSV"

<span style="color: #4CAF50">**Alors**</span> 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) |

<span style="color: #9E9E9E">**Et**</span> je peux faire des analyses personnalisées

---

## 14. Export graphiques interactifs

<span style="color: #2196F3">**Étant donné**</span> que je consulte le dashboard

<span style="color: #FF9800">**Quand**</span> je clique sur un graphique Chart.js

<span style="color: #4CAF50">**Alors**</span> je peux zoomer/filtrer interactivement
<span style="color: #9E9E9E">**Et**</span> je peux exporter le graphique en PNG
<span style="color: #9E9E9E">**Et**</span> l'image est en haute résolution pour présentations

---

## 15. Rapport PDF automatique fin de campagne

<span style="color: #2196F3">**Étant donné**</span> que ma campagne de 14 jours se termine

<span style="color: #FF9800">**Quand**</span> la date de fin est atteinte

<span style="color: #4CAF50">**Alors**</span> un rapport PDF est généré automatiquement
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> je reçois un email avec le PDF en pièce jointe

---

## 16. Métriques temps réel rafraîchies automatiquement

<span style="color: #2196F3">**Étant donné**</span> que je consulte le dashboard à 10h00

<span style="color: #FF9800">**Quand**</span> une nouvelle diffusion se produit à 10h01

<span style="color: #4CAF50">**Alors**</span> les métriques sont rafraîchies automatiquement (polling 30s)
<span style="color: #9E9E9E">**Et**</span> je vois les nouveaux chiffres sans recharger la page
<span style="color: #9E9E9E">**Et**</span> un badge "Mis à jour il y a 15s" s'affiche

---

## 17. Alertes performance personnalisées

<span style="color: #2196F3">**Étant donné**</span> que je configure une alerte "Taux de skip >70%"
<span style="color: #9E9E9E">**Et**</span> que ma campagne atteint 72% de skip

<span style="color: #FF9800">**Quand**</span> le seuil est dépassé

<span style="color: #4CAF50">**Alors**</span> je reçois un email d'alerte:

---

## 18. Benchmark vs moyennes RoadWave

<span style="color: #2196F3">**Étant donné**</span> que ma campagne a 45% d'écoutes complètes

<span style="color: #FF9800">**Quand**</span> je consulte le benchmark

<span style="color: #4CAF50">**Alors**</span> je vois "Votre taux: 45% | Moyenne RoadWave: 40%"
<span style="color: #9E9E9E">**Et**</span> un badge "📊 Performance: +12% vs moyenne" s'affiche
<span style="color: #9E9E9E">**Et**</span> je sais que ma campagne performe au-dessus de la moyenne

---

## 19. Coût total consommé vs budget

<span style="color: #2196F3">**Étant donné**</span> que j'ai un budget de 300€
<span style="color: #9E9E9E">**Et**</span> que j'ai consommé 220€

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard

<span style="color: #4CAF50">**Alors**</span> je vois une jauge "Budget consommé: 73%" (220/300)
<span style="color: #9E9E9E">**Et**</span> le montant restant "80€ restants"
<span style="color: #9E9E9E">**Et**</span> une projection "Épuisé dans 3 jours à ce rythme"

---

## 20. Répartition coûts par type d'écoute

<span style="color: #2196F3">**Étant donné**</span> 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€ |


<span style="color: #FF9800">**Quand**</span> je consulte la répartition

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> une campagne de 30 jours

<span style="color: #FF9800">**Quand**</span> je consulte le graphique d'évolution

<span style="color: #4CAF50">**Alors**</span> je vois une courbe Chart.js avec:

    | Axe | Donnée |
    |---|---|
    | X | Jours (1-30) |
    | Y | Taux d'écoute complète (%) |

<span style="color: #9E9E9E">**Et**</span> je peux identifier les tendances (amélioration/dégradation)
<span style="color: #9E9E9E">**Et**</span> les jours avec pics d'engagement

---

## 22. Métriques avancées - Taux de réécoute

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a entendu ma pub 3 fois
<span style="color: #9E9E9E">**Et**</span> qu'il l'a écoutée complètement les 3 fois

<span style="color: #FF9800">**Quand**</span> je consulte les métriques avancées

<span style="color: #4CAF50">**Alors**</span> le "Taux de réécoute" affiche 100%
<span style="color: #9E9E9E">**Et**</span> cela indique que le contenu n'est pas perçu comme spam
<span style="color: #9E9E9E">**Et**</span> les utilisateurs tolèrent bien la répétition

---

## 23. Recommandations automatiques d'optimisation

<span style="color: #2196F3">**Étant donné**</span> que ma campagne a un taux de skip de 75%
<span style="color: #9E9E9E">**Et**</span> que la durée moyenne d'écoute est de 8s sur 30s

<span style="color: #FF9800">**Quand**</span> je consulte les recommandations

<span style="color: #4CAF50">**Alors**</span> le système suggère:

---

## 24. Suivi multi-campagnes avec vue consolidée

<span style="color: #2196F3">**Étant donné**</span> que j'ai 3 campagnes actives simultanément

<span style="color: #FF9800">**Quand**</span> je consulte la vue consolidée

<span style="color: #4CAF50">**Alors**</span> 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€ |

<span style="color: #9E9E9E">**Et**</span> je peux comparer les performances d'un coup d'œil

---

## 25. 📋 Plan: Calcul taux d'écoute complète

<span style="color: #2196F3">**Étant donné**</span> <total> diffusions totales
<span style="color: #9E9E9E">**Et**</span> <completes> écoutes complètes (≥80%)

<span style="color: #FF9800">**Quand**</span> je calcule le taux

<span style="color: #4CAF50">**Alors**</span> le résultat est <taux>%

**📊 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)

<span style="color: #2196F3">**Étant donné**</span> un budget dépensé de <depense>€
<span style="color: #9E9E9E">**Et**</span> <ecoutes> écoutes complètes

<span style="color: #FF9800">**Quand**</span> je calcule le CPE

<span style="color: #4CAF50">**Alors**</span> le résultat est <cpe>€

**📊 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

<span style="color: #2196F3">**Étant donné**</span> un taux d'écoute complète de <taux>%
<span style="color: #9E9E9E">**Et**</span> une moyenne RoadWave de 40%

<span style="color: #FF9800">**Quand**</span> je compare à la moyenne

<span style="color: #4CAF50">**Alors**</span> la performance est <classification>

**📊 Exemples de données:**

| taux | classification |
|---|---|
| 60 | Excellente (+50%) |
| 50 | Bonne (+25%) |
| 40 | Moyenne |
| 30 | Faible (-25%) |
| 20 | Très faible (-50%) |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> qu'un modérateur RoadWave est connecté
## 1. Validation manuelle obligatoire avant diffusion

<span style="color: #2196F3">**Étant donné**</span> qu'un publicitaire a créé une campagne
<span style="color: #9E9E9E">**Et**</span> que le paiement de 300€ a été effectué

<span style="color: #FF9800">**Quand**</span> la campagne est soumise

<span style="color: #4CAF50">**Alors**</span> elle passe en statut "En attente de validation"
<span style="color: #9E9E9E">**Et**</span> elle est ajoutée à la file d'attente des modérateurs
<span style="color: #9E9E9E">**Et**</span> la diffusion ne démarre PAS avant validation manuelle
<span style="color: #9E9E9E">**Et**</span> le publicitaire reçoit un email "Votre campagne est en cours de validation (24-48h)"

---

## 2. Délai de validation 24-48h ouvrées

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne est soumise le lundi 10h

<span style="color: #FF9800">**Quand**</span> le modérateur la valide le mardi 15h

<span style="color: #4CAF50">**Alors**</span> le délai est de 29h (dans les 48h ouvrées)
<span style="color: #9E9E9E">**Et**</span> le publicitaire reçoit une notification "Votre campagne est approuvée"

---

## 3. Validation dépassant 48h avec notification

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne est soumise le lundi 10h

<span style="color: #FF9800">**Quand**</span> 48h ouvrées se sont écoulées
<span style="color: #9E9E9E">**Et**</span> que la campagne n'est toujours pas validée

<span style="color: #4CAF50">**Alors**</span> le publicitaire reçoit un email automatique:
<span style="color: #9E9E9E">**Et**</span> un modérateur senior est assigné automatiquement

---

## 4. Acceptation de campagne publicitaire

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne est en attente de validation
<span style="color: #9E9E9E">**Et**</span> que l'audio respecte toutes les règles

<span style="color: #FF9800">**Quand**</span> le modérateur clique sur "Approuver"

<span style="color: #4CAF50">**Alors**</span> le statut passe à "Approuvée"
<span style="color: #9E9E9E">**Et**</span> la campagne démarre à la date programmée
<span style="color: #9E9E9E">**Et**</span> le publicitaire reçoit un email de confirmation
<span style="color: #9E9E9E">**Et**</span> le budget commence à être consommé dès le début

---

## 5. Refus de campagne avec motif détaillé

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne contient du contenu alcool

<span style="color: #FF9800">**Quand**</span> le modérateur clique sur "Refuser"
<span style="color: #9E9E9E">**Et**</span> qu'il sélectionne le motif "Contenu interdit: Alcool"
<span style="color: #9E9E9E">**Et**</span> qu'il ajoute le commentaire "La publicité pour l'alcool est interdite en France"

<span style="color: #4CAF50">**Alors**</span> le statut passe à "Refusée"
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> un remboursement automatique de 300€ est déclenché

---

## 6. Remboursement automatique après refus

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne à 500€ est refusée

<span style="color: #FF9800">**Quand**</span> le statut passe à "Refusée"

<span style="color: #4CAF50">**Alors**</span> un remboursement Mangopay de 500€ est initié automatiquement
<span style="color: #9E9E9E">**Et**</span> le délai de remboursement est de 5-7 jours ouvrés
<span style="color: #9E9E9E">**Et**</span> le publicitaire reçoit un email "Remboursement en cours"

---

## 7. Contenus interdits - Alcool

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité mentionne "Whisky premium 40°"

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit refuser la campagne
<span style="color: #9E9E9E">**Et**</span> sélectionner le motif "Contenu interdit: Alcool"

---

## 8. Contenus interdits - Tabac

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité mentionne "Cigarettes électroniques"

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit refuser la campagne
<span style="color: #9E9E9E">**Et**</span> sélectionner le motif "Contenu interdit: Tabac/Vape"

---

## 9. Contenus interdits - Jeux d'argent

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité mentionne "Gagnez 10 000€ - Paris sportifs"

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit refuser la campagne
<span style="color: #9E9E9E">**Et**</span> sélectionner le motif "Contenu interdit: Jeux d'argent"

---

## 10. Contenus interdits - Politique pendant campagne électorale

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité politique est soumise
<span style="color: #9E9E9E">**Et**</span> que nous sommes en période de campagne électorale officielle

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit refuser la campagne
<span style="color: #9E9E9E">**Et**</span> sélectionner le motif "Contenu interdit: Publicité politique (période électorale)"

---

## 11. Contenus interdits - Contenu sexuel

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité contient des propos sexuellement explicites

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit refuser la campagne
<span style="color: #9E9E9E">**Et**</span> sélectionner le motif "Contenu interdit: Contenu sexuel"

---

## 12. Contenus interdits - Violence

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité contient des descriptions violentes

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit refuser la campagne
<span style="color: #9E9E9E">**Et**</span> sélectionner le motif "Contenu interdit: Violence"

---

## 13. Contenu légal autorisé - Commerce local

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité pour un restaurant local dit "Découvrez notre menu du jour"

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit approuver la campagne

---

## 14. Contenu légal autorisé - Service professionnel

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité pour un garage dit "Révision complète à partir de 99€"

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit approuver la campagne

---

## 15. Critères de validation - Qualité audio

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité a une qualité audio très basse (bruits, saturation)

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il peut refuser avec le motif "Qualité audio insuffisante"
<span style="color: #9E9E9E">**Et**</span> recommander "Veuillez soumettre un fichier audio de meilleure qualité"

---

## 16. Critères de validation - Classification d'âge correcte

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité contient du langage familier
<span style="color: #9E9E9E">**Et**</span> qu'elle est classée "Tout public"

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il peut refuser avec le motif "Classification d'âge incorrecte"
<span style="color: #9E9E9E">**Et**</span> recommander "Reclasser en 13+ minimum"

---

## 17. Critères de validation - Respect réglementation française

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité fait des promesses mensongères "Perdez 10kg en 1 semaine"

<span style="color: #FF9800">**Quand**</span> le modérateur écoute l'audio

<span style="color: #4CAF50">**Alors**</span> il doit refuser avec le motif "Non-conformité réglementaire: Publicité mensongère"

---

## 18. File d'attente modération priorisée

<span style="color: #2196F3">**Étant donné**</span> que 10 campagnes sont en attente de validation
<span style="color: #9E9E9E">**Et**</span> que la campagne A a été soumise il y a 40h
<span style="color: #9E9E9E">**Et**</span> que la campagne B a été soumise il y a 2h

<span style="color: #FF9800">**Quand**</span> le modérateur consulte sa file

<span style="color: #4CAF50">**Alors**</span> la campagne A apparaît en premier (priorité temporelle)
<span style="color: #9E9E9E">**Et**</span> un badge "Urgente - >40h" est affiché

---

## 19. Dashboard modération - Vue d'ensemble

<span style="color: #2196F3">**Étant donné**</span> que je suis modérateur

<span style="color: #FF9800">**Quand**</span> j'accède au dashboard modération publicités

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité audio est soumise

<span style="color: #FF9800">**Quand**</span> le système traite l'audio

<span style="color: #4CAF50">**Alors**</span> une transcription automatique est générée via Whisper
<span style="color: #9E9E9E">**Et**</span> elle est affichée au modérateur pour faciliter la revue
<span style="color: #9E9E9E">**Et**</span> elle permet une recherche par mots-clés (alcool, tabac, etc.)

---

## 21. Détection automatique mots-clés interdits

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité audio est soumise

<span style="color: #FF9800">**Quand**</span> la transcription contient "whisky" ou "vodka"

<span style="color: #4CAF50">**Alors**</span> un flag automatique "⚠️ Alcool détecté" est ajouté
<span style="color: #9E9E9E">**Et**</span> la campagne est priorisée pour validation manuelle rapide
<span style="color: #9E9E9E">**Et**</span> le modérateur est alerté du contenu potentiellement interdit

---

## 22. Historique modération publicitaire

<span style="color: #2196F3">**Étant donné**</span> qu'un publicitaire a eu 2 campagnes refusées

<span style="color: #FF9800">**Quand**</span> il soumet une 3ème campagne

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> il peut en tenir compte dans sa décision

---

## 23. Appel possible après refus

<span style="color: #2196F3">**Étant donné**</span> que ma campagne a été refusée pour "Classification incorrecte"

<span style="color: #FF9800">**Quand**</span> je conteste la décision via le formulaire d'appel

<span style="color: #4CAF50">**Alors**</span> un modérateur senior revoit la campagne
<span style="color: #9E9E9E">**Et**</span> il peut approuver si la classification est en fait correcte
<span style="color: #9E9E9E">**Et**</span> le délai de réponse est de 48-72h

---

## 24. Notification temps réel pour modérateurs

<span style="color: #2196F3">**Étant donné**</span> que je suis modérateur connecté

<span style="color: #FF9800">**Quand**</span> une nouvelle campagne est soumise

<span style="color: #4CAF50">**Alors**</span> je reçois une notification in-app
<span style="color: #9E9E9E">**Et**</span> le compteur "Campagnes en attente" s'incrémente en temps réel
<span style="color: #9E9E9E">**Et**</span> je peux cliquer pour consulter immédiatement

---

## 25. Statistiques conformité par catégorie

<span style="color: #2196F3">**Étant donné**</span> que je suis admin modération

<span style="color: #FF9800">**Quand**</span> je consulte les statistiques mensuelles

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis modérateur senior

<span style="color: #FF9800">**Quand**</span> j'exporte le rapport mensuel

<span style="color: #4CAF50">**Alors**</span> je reçois un fichier CSV avec:

    | Colonne |
    |---|
    | Campagne ID |
    | Publicitaire |
    | Date soumission |
    | Date décision |
    | Statut |
    | Motif (si refus) |
    | Modérateur |

<span style="color: #9E9E9E">**Et**</span> je peux l'analyser dans Excel

---

## 27. Validation partielle avec demande modification

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne a un contenu acceptable
<span style="color: #F44336">**Mais**</span> que la classification d'âge est incorrecte

<span style="color: #FF9800">**Quand**</span> le modérateur clique sur "Demander modification"

<span style="color: #4CAF50">**Alors**</span> le publicitaire reçoit un email:
<span style="color: #9E9E9E">**Et**</span> le statut devient "Modification requise"
<span style="color: #9E9E9E">**Et**</span> le publicitaire peut modifier sans repayer

---

## 28. 📋 Plan: Contenus interdits automatiquement détectés

<span style="color: #2196F3">**Étant donné**</span> qu'une publicité contient le mot <mot_cle>

<span style="color: #FF9800">**Quand**</span> la transcription automatique est analysée

<span style="color: #4CAF50">**Alors**</span> un flag <flag> est ajouté
<span style="color: #9E9E9E">**Et**</span> le motif de refus suggéré est <motif>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> qu'une campagne est soumise <jour> à <heure>

<span style="color: #FF9800">**Quand**</span> elle est validée <delai> heures plus tard

<span style="color: #4CAF50">**Alors**</span> le statut est <conformite>

**📊 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) |

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'infrastructure RoadWave est opérationnelle
> <span style="color: #9E9E9E">**Et**</span> que les serveurs Go avec Pion WebRTC sont actifs
## 1. Ingestion WebRTC du flux créateur

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur démarre un live depuis son application mobile

<span style="color: #FF9800">**Quand**</span> le flux audio WebRTC (Opus 48 kbps) arrive sur le serveur

<span style="color: #4CAF50">**Alors**</span> le serveur Go avec Pion WebRTC accepte la connexion
<span style="color: #9E9E9E">**Et**</span> le flux est traité en temps réel

---

## 2. Conversion temps réel Opus vers segments HLS

<span style="color: #2196F3">**Étant donné**</span> qu'un flux WebRTC Opus est reçu par le serveur

<span style="color: #FF9800">**Quand**</span> le serveur traite le flux

<span style="color: #4CAF50">**Alors**</span> FFmpeg convertit en segments HLS (.ts)
<span style="color: #9E9E9E">**Et**</span> un fichier manifest .m3u8 est généré et mis à jour régulièrement
<span style="color: #9E9E9E">**Et**</span> les segments ont une durée de 2 secondes chacun

---

## 3. Distribution via Bunny CDN

<span style="color: #2196F3">**Étant donné**</span> que les segments HLS sont générés

<span style="color: #FF9800">**Quand**</span> un auditeur demande à rejoindre le live

<span style="color: #4CAF50">**Alors**</span> le manifest .m3u8 est servi via Bunny CDN
<span style="color: #9E9E9E">**Et**</span> les segments .ts sont cachés sur le CDN
<span style="color: #9E9E9E">**Et**</span> la distribution est globale avec latence minimale

---

## 4. Lecture HLS native sur mobile iOS

<span style="color: #2196F3">**Étant donné**</span> qu'un auditeur iOS rejoint un live

<span style="color: #FF9800">**Quand**</span> l'application charge le flux HLS

<span style="color: #4CAF50">**Alors**</span> le player natif AVPlayer gère la lecture
<span style="color: #9E9E9E">**Et**</span> le buffer de 15 secondes est appliqué automatiquement
<span style="color: #9E9E9E">**Et**</span> la qualité s'adapte selon la connexion

---

## 5. Lecture HLS native sur mobile Android

<span style="color: #2196F3">**Étant donné**</span> qu'un auditeur Android rejoint un live

<span style="color: #FF9800">**Quand**</span> l'application charge le flux HLS

<span style="color: #4CAF50">**Alors**</span> le player natif ExoPlayer gère la lecture
<span style="color: #9E9E9E">**Et**</span> le buffer de 15 secondes est configuré
<span style="color: #9E9E9E">**Et**</span> la qualité s'adapte selon la connexion

---

## 6. Enregistrement parallèle du flux pour replay

<span style="color: #2196F3">**Étant donné**</span> qu'un live est en cours

<span style="color: #4CAF50">**Alors**</span> un processus parallèle enregistre le flux Opus raw
<span style="color: #9E9E9E">**Et**</span> l'enregistrement est stocké temporairement sur le serveur
<span style="color: #9E9E9E">**Et**</span> l'enregistrement est indépendant de la diffusion HLS

---

## 7. Traitement post-live asynchrone

<span style="color: #2196F3">**Étant donné**</span> qu'un live vient de se terminer

<span style="color: #FF9800">**Quand**</span> le processus post-live démarre

<span style="color: #4CAF50">**Alors**</span> un job asynchrone est créé dans la queue Redis
<span style="color: #9E9E9E">**Et**</span> un worker Go récupère le job
<span style="color: #9E9E9E">**Et**</span> le worker exécute FFmpeg pour les conversions

---

## 8. Conversion Opus raw vers MP3 256 kbps

<span style="color: #2196F3">**Étant donné**</span> qu'un worker traite un job post-live

<span style="color: #FF9800">**Quand**</span> la conversion démarre

<span style="color: #4CAF50">**Alors**</span> FFmpeg convertit Opus raw en MP3 256 kbps
<span style="color: #9E9E9E">**Et**</span> la normalisation audio à -14 LUFS est appliquée
<span style="color: #9E9E9E">**Et**</span> les silences prolongés (>3 secondes) sont détectés et nettoyés

---

## 9. Génération segments HLS pour le replay

<span style="color: #2196F3">**Étant donné**</span> que le MP3 256 kbps est généré

<span style="color: #FF9800">**Quand**</span> le worker crée les segments HLS

<span style="color: #4CAF50">**Alors**</span> des segments .ts de 10 secondes sont créés
<span style="color: #9E9E9E">**Et**</span> un manifest .m3u8 est généré
<span style="color: #9E9E9E">**Et**</span> les segments sont uploadés vers le stockage Bunny

---

## 10. Publication automatique du replay

<span style="color: #2196F3">**Étant donné**</span> que tous les segments HLS sont uploadés

<span style="color: #FF9800">**Quand**</span> le worker finalise le job

<span style="color: #4CAF50">**Alors**</span> une entrée de contenu "replay" est créée en base PostgreSQL
<span style="color: #9E9E9E">**Et**</span> le titre est "[REPLAY] [Titre live original]"
<span style="color: #9E9E9E">**Et**</span> le type géographique est "Géo-neutre"
<span style="color: #9E9E9E">**Et**</span> le replay est immédiatement disponible pour les auditeurs

---

## 11. Suppression automatique fichier Opus raw après 7 jours

<span style="color: #2196F3">**Étant donné**</span> qu'un replay est publié depuis 7 jours

<span style="color: #FF9800">**Quand**</span> le job de nettoyage quotidien s'exécute

<span style="color: #4CAF50">**Alors**</span> le fichier Opus raw est supprimé du stockage
<span style="color: #9E9E9E">**Et**</span> seul le MP3 256 kbps et les segments HLS sont conservés
<span style="color: #9E9E9E">**Et**</span> l'espace de stockage est libéré

---

## 12. Scalabilité horizontale des workers de conversion

<span style="color: #2196F3">**Étant donné**</span> que 50 lives se terminent simultanément

<span style="color: #FF9800">**Quand**</span> les jobs post-live sont créés

<span style="color: #4CAF50">**Alors**</span> les workers Go disponibles traitent les jobs en parallèle
<span style="color: #9E9E9E">**Et**</span> si tous les workers sont occupés, les jobs attendent en queue Redis
<span style="color: #9E9E9E">**Et**</span> de nouveaux workers peuvent être lancés automatiquement (Kubernetes)

---

## 13. Limitation du nombre de lives simultanés (MVP)

<span style="color: #2196F3">**Étant donné**</span> que l'infrastructure MVP est configurée pour 100 lives simultanés
<span style="color: #9E9E9E">**Et**</span> que 100 lives sont actuellement en cours

<span style="color: #FF9800">**Quand**</span> un nouveau créateur essaie de démarrer un live

<span style="color: #4CAF50">**Alors**</span> la demande est refusée avec le code erreur 503
<span style="color: #9E9E9E">**Et**</span> le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes" est retourné
<span style="color: #9E9E9E">**Et**</span> la demande peut être mise en queue prioritaire si créateur Premium

---

## 14. Monitoring des ressources serveur en temps réel

<span style="color: #2196F3">**Étant donné**</span> que plusieurs lives sont en cours

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> si un seuil est dépassé, une alerte est envoyée à l'équipe technique

---

## 15. Calcul du coût de bande passante CDN

<span style="color: #2196F3">**Étant donné**</span> qu'un live a 100 auditeurs simultanés
<span style="color: #9E9E9E">**Et**</span> que la qualité est 48 kbps Opus

<span style="color: #FF9800">**Quand**</span> le live dure 1 heure

<span style="color: #4CAF50">**Alors**</span> la bande passante totale est d'environ 2.16 GB
<span style="color: #9E9E9E">**Et**</span> le coût Bunny CDN est d'environ 0.02€ (tarif ~0.01€/GB)
<span style="color: #9E9E9E">**Et**</span> ces métriques sont enregistrées pour facturation créateur si nécessaire

---

## 16. Cache CDN des segments HLS

<span style="color: #2196F3">**Étant donné**</span> qu'un live est diffusé via Bunny CDN

<span style="color: #FF9800">**Quand**</span> un segment .ts est généré

<span style="color: #4CAF50">**Alors**</span> le segment est uploadé vers Bunny origin
<span style="color: #9E9E9E">**Et**</span> Bunny CDN cache le segment sur ses edge servers
<span style="color: #9E9E9E">**Et**</span> les auditeurs suivants récupèrent le segment depuis le cache
<span style="color: #9E9E9E">**Et**</span> la charge sur le serveur origin est réduite de ~90%

---

## 17. Gestion de la latence WebRTC créateur

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur diffuse avec une connexion 4G

<span style="color: #FF9800">**Quand**</span> la latence réseau augmente ponctuellement

<span style="color: #4CAF50">**Alors**</span> le buffer côté serveur absorbe les fluctuations
<span style="color: #9E9E9E">**Et**</span> la qualité peut être réduite temporairement (48 kbps → 32 kbps)
<span style="color: #9E9E9E">**Et**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> qu'un live contient de la musique en arrière-plan

<span style="color: #FF9800">**Quand**</span> le système d'audio fingerprint analyse le flux

<span style="color: #4CAF50">**Alors**</span> une empreinte audio est calculée toutes les 30 secondes
<span style="color: #9E9E9E">**Et**</span> l'empreinte est comparée à une base de données de contenus protégés
<span style="color: #9E9E9E">**Et**</span> si une correspondance est trouvée, un warning est envoyé au créateur
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un créateur démarre un live

<span style="color: #4CAF50">**Alors**</span> 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" |

<span style="color: #9E9E9E">**Et**</span> ces données sont indexées pour recherche et analytics

---

## 20. Cache Redis pour compteurs temps réel

<span style="color: #2196F3">**Étant donné**</span> qu'un live est en cours

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ces compteurs sont mis à jour toutes les 2 secondes
<span style="color: #9E9E9E">**Et**</span> les compteurs sont persistés en PostgreSQL toutes les 60 secondes

---

## 21. Heartbeat auditeurs pour compteur précis

<span style="color: #2196F3">**Étant donné**</span> qu'un auditeur écoute un live

<span style="color: #4CAF50">**Alors**</span> l'application envoie un heartbeat toutes les 10 secondes
<span style="color: #9E9E9E">**Et**</span> le heartbeat met à jour le timestamp dans Redis
<span style="color: #9E9E9E">**Et**</span> si aucun heartbeat n'est reçu pendant 30 secondes, l'auditeur est retiré du compteur

---

## 22. Gestion des pannes serveur pendant un live

<span style="color: #2196F3">**Étant donné**</span> qu'un live est en cours sur serveur A

<span style="color: #FF9800">**Quand**</span> le serveur A tombe en panne

<span style="color: #4CAF50">**Alors**</span> Kubernetes redémarre automatiquement un pod
<span style="color: #F44336">**Mais**</span> le live en cours est perdu (pas de failover temps réel en MVP)
<span style="color: #9E9E9E">**Et**</span> le créateur voit le message "Connexion perdue. Veuillez redémarrer le live"
<span style="color: #9E9E9E">**Et**</span> les auditeurs voient "Le live est terminé suite à un problème technique"

---

## 23. Backup automatique des enregistrements live

<span style="color: #2196F3">**Étant donné**</span> qu'un live est enregistré en Opus raw

<span style="color: #FF9800">**Quand**</span> l'enregistrement dépasse 10 minutes

<span style="color: #4CAF50">**Alors**</span> un backup incrémental est créé toutes les 10 minutes
<span style="color: #9E9E9E">**Et**</span> le backup est stocké sur un stockage secondaire (S3-compatible)
<span style="color: #9E9E9E">**Et**</span> en cas de crash serveur, le live peut être récupéré jusqu'au dernier backup

---

## 24. Logs et audit trail des lives

<span style="color: #2196F3">**Étant donné**</span> qu'un live démarre, se déroule et se termine

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ces logs sont conservés 90 jours pour analytics et conformité RGPD

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant que créateur
> <span style="color: #9E9E9E">**Et**</span> que je diffuse actuellement un live
## 1. Arrêt manuel avec compte à rebours 5 secondes

<span style="color: #FF9800">**Quand**</span> j'appuie sur le bouton "Arrêter live"

<span style="color: #4CAF50">**Alors**</span> un compte à rebours de 5 secondes démarre
<span style="color: #9E9E9E">**Et**</span> je vois le message "Ce live se termine dans 5... 4... 3... 2... 1"
<span style="color: #9E9E9E">**Et**</span> un bouton "Annuler" est affiché pendant le décompte
<span style="color: #9E9E9E">**Et**</span> l'audio du compte à rebours est diffusé aux auditeurs

---

## 2. Annulation du compte à rebours

<span style="color: #2196F3">**Étant donné**</span> que j'ai appuyé sur "Arrêter live"
<span style="color: #9E9E9E">**Et**</span> que le compte à rebours affiche "3 secondes"

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Annuler"

<span style="color: #4CAF50">**Alors**</span> le compte à rebours s'arrête
<span style="color: #9E9E9E">**Et**</span> le live continue normalement
<span style="color: #9E9E9E">**Et**</span> aucune notification n'est envoyée aux auditeurs

---

## 3. Arrêt effectif après compte à rebours

<span style="color: #2196F3">**Étant donné**</span> que le compte à rebours est à 0

<span style="color: #4CAF50">**Alors**</span> le live s'arrête
<span style="color: #9E9E9E">**Et**</span> la diffusion aux auditeurs se termine
<span style="color: #9E9E9E">**Et**</span> le message "Live terminé" s'affiche
<span style="color: #9E9E9E">**Et**</span> le processus de traitement post-live démarre automatiquement

---

## 4. Déconnexion créateur courte (moins de 60 secondes)

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un live

<span style="color: #FF9800">**Quand**</span> ma connexion est perdue pendant 30 secondes

<span style="color: #4CAF50">**Alors**</span> les auditeurs voient le message "Connexion créateur perdue, reconnexion en cours..."
<span style="color: #9E9E9E">**Et**</span> le live continue de bufferer
<span style="color: #9E9E9E">**Et**</span> quand ma connexion revient, le live reprend normalement

---

## 5. Déconnexion créateur longue (60 secondes ou plus)

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un live

<span style="color: #FF9800">**Quand**</span> ma connexion est perdue pendant 60 secondes

<span style="color: #4CAF50">**Alors**</span> le live s'arrête automatiquement
<span style="color: #9E9E9E">**Et**</span> les auditeurs voient le message "Le live est terminé suite à une coupure de connexion"
<span style="color: #9E9E9E">**Et**</span> le processus de traitement post-live démarre

---

## 6. Enregistrement automatique pendant le live

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un live

<span style="color: #4CAF50">**Alors**</span> mon flux audio est enregistré en continu
<span style="color: #9E9E9E">**Et**</span> le format d'enregistrement est Opus raw
<span style="color: #9E9E9E">**Et**</span> l'enregistrement est stocké temporairement sur le serveur

---

## 7. Génération automatique du replay après arrêt

<span style="color: #2196F3">**Étant donné**</span> que mon live vient de se terminer
<span style="color: #9E9E9E">**Et**</span> que l'option "Publier replay automatiquement" est activée (par défaut)

<span style="color: #FF9800">**Quand**</span> le traitement post-live démarre

<span style="color: #4CAF50">**Alors**</span> un job asynchrone est créé
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le traitement post-live est terminé

<span style="color: #4CAF50">**Alors**</span> le replay est publié automatiquement sous 5 à 10 minutes
<span style="color: #9E9E9E">**Et**</span> le titre est "[REPLAY] [Titre live original]"
<span style="color: #9E9E9E">**Et**</span> la zone de diffusion est la même que le live
<span style="color: #9E9E9E">**Et**</span> les tags sont identiques au live
<span style="color: #9E9E9E">**Et**</span> la classification d'âge est identique
<span style="color: #9E9E9E">**Et**</span> le type géographique est "Géo-neutre" (contenu pérenne)

---

## 9. Notification de disponibilité du replay aux auditeurs

<span style="color: #2196F3">**Étant donné**</span> que le replay de mon live est publié

<span style="color: #FF9800">**Quand**</span> un auditeur qui a écouté le live se reconnecte

<span style="color: #4CAF50">**Alors**</span> il voit une notification in-app "Le replay de [Titre] est disponible"

---

## 10. Option désactivation publication automatique replay

<span style="color: #2196F3">**Étant donné**</span> que je configure un nouveau live

<span style="color: #FF9800">**Quand**</span> je désactive l'option "Publier replay automatiquement"
<span style="color: #9E9E9E">**Et**</span> que je démarre puis arrête le live

<span style="color: #4CAF50">**Alors**</span> le live est enregistré
<span style="color: #F44336">**Mais**</span> le replay n'est pas publié automatiquement
<span style="color: #9E9E9E">**Et**</span> je peux décider manuellement de le publier plus tard

---

## 11. Suppression manuelle du replay après publication

<span style="color: #2196F3">**Étant donné**</span> que mon live a généré un replay publié

<span style="color: #FF9800">**Quand**</span> j'accède à mes contenus

<span style="color: #4CAF50">**Alors**</span> je vois le replay dans ma liste
<span style="color: #9E9E9E">**Et**</span> je peux le supprimer comme n'importe quel contenu

<span style="color: #FF9800">**Quand**</span> je supprime le replay

<span style="color: #4CAF50">**Alors**</span> le fichier source Opus raw est supprimé immédiatement

---

## 12. Conservation fichier source Opus raw

<span style="color: #2196F3">**Étant donné**</span> que mon live est terminé
<span style="color: #9E9E9E">**Et**</span> que le replay est publié

<span style="color: #4CAF50">**Alors**</span> le fichier Opus raw est conservé pendant 7 jours
<span style="color: #9E9E9E">**Et**</span> après 7 jours, le fichier raw est supprimé automatiquement
<span style="color: #9E9E9E">**Et**</span> seul le MP3 256 kbps est conservé

---

## 13. Modification du replay interdite

<span style="color: #2196F3">**Étant donné**</span> que mon live a généré un replay publié

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier l'audio du replay

<span style="color: #4CAF50">**Alors**</span> l'action est refusée
<span style="color: #9E9E9E">**Et**</span> je vois le message "Les replays ne peuvent pas être modifiés pour garantir l'intégrité de l'enregistrement"
<span style="color: #9E9E9E">**Et**</span> je peux uniquement modifier les métadonnées (titre, description)

---

## 14. Statistiques du live disponibles après arrêt

<span style="color: #2196F3">**Étant donné**</span> que mon live est terminé

<span style="color: #FF9800">**Quand**</span> j'accède aux statistiques

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que mon live a reçu 3 signalements pendant la diffusion

<span style="color: #FF9800">**Quand**</span> le live se termine

<span style="color: #4CAF50">**Alors**</span> le replay n'est pas publié automatiquement
<span style="color: #9E9E9E">**Et**</span> le contenu est en attente de modération
<span style="color: #9E9E9E">**Et**</span> je vois le message "Votre replay sera publié après vérification suite aux signalements reçus"
<span style="color: #9E9E9E">**Et**</span> un modérateur doit valider ou refuser le replay sous 24h

---

## 16. Arrêt forcé par un modérateur

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un live
<span style="color: #9E9E9E">**Et**</span> qu'un modérateur détecte du contenu interdit

<span style="color: #FF9800">**Quand**</span> le modérateur clique sur "Arrêter le live immédiatement"

<span style="color: #4CAF50">**Alors**</span> le live s'arrête sans compte à rebours
<span style="color: #9E9E9E">**Et**</span> je vois le message "Votre live a été interrompu par la modération"
<span style="color: #9E9E9E">**Et**</span> je reçois une notification détaillant la raison
<span style="color: #9E9E9E">**Et**</span> le replay n'est pas publié
<span style="color: #9E9E9E">**Et**</span> le fichier source est conservé 30 jours pour appel

---

## 17. Métriques de bande passante pendant le live

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un live
<span style="color: #9E9E9E">**Et**</span> que 100 auditeurs écoutent simultanément

<span style="color: #4CAF50">**Alors**</span> la bande passante consommée est d'environ 4.8 Mbps via CDN
<span style="color: #9E9E9E">**Et**</span> le coût estimé Bunny CDN est d'environ 0.02€ par heure de diffusion
<span style="color: #9E9E9E">**Et**</span> je peux voir ces métriques en temps réel dans l'interface créateur

---

## 18. Live sans auditeurs pendant 5 minutes

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un live
<span style="color: #9E9E9E">**Et**</span> qu'aucun auditeur n'écoute depuis 5 minutes

<span style="color: #4CAF50">**Alors**</span> je vois un message d'information "Aucun auditeur actuellement connecté"
<span style="color: #F44336">**Mais**</span> le live continue normalement
<span style="color: #9E9E9E">**Et**</span> je peux choisir de continuer ou d'arrêter

---

## 19. Qualité audio du replay supérieure au live

<span style="color: #2196F3">**Étant donné**</span> que mon live était diffusé en Opus 48 kbps

<span style="color: #FF9800">**Quand**</span> le replay est généré

<span style="color: #4CAF50">**Alors**</span> le replay est encodé en MP3 256 kbps
<span style="color: #9E9E9E">**Et**</span> la qualité audio du replay est supérieure au live
<span style="color: #9E9E9E">**Et**</span> la taille du fichier est optimisée pour le stockage long terme

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant qu'auditeur
> <span style="color: #9E9E9E">**Et**</span> qu'un créateur diffuse actuellement un live
## 1. Rejoindre un live avec buffer de synchronisation 15 secondes

<span style="color: #FF9800">**Quand**</span> je clique sur "Rejoindre le live"

<span style="color: #4CAF50">**Alors**</span> la connexion au flux HLS s'établit
<span style="color: #9E9E9E">**Et**</span> je commence à écouter avec un décalage de 15 secondes par rapport au créateur
<span style="color: #9E9E9E">**Et**</span> le buffer de 15 secondes garantit une lecture stable

---

## 2. Justification du buffer 15 secondes

<span style="color: #2196F3">**Étant donné**</span> 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 | ❌ |


<span style="color: #4CAF50">**Alors**</span> le buffer optimal est 15 secondes

---

## 3. Lecture stable sur réseau 3G

<span style="color: #2196F3">**Étant donné**</span> que je suis sur réseau 3G
<span style="color: #9E9E9E">**Et**</span> que j'écoute un live

<span style="color: #FF9800">**Quand**</span> des micro-coupures réseau surviennent

<span style="color: #4CAF50">**Alors**</span> le buffer de 15 secondes absorbe les coupures
<span style="color: #9E9E9E">**Et**</span> la lecture continue sans interruption perceptible

---

## 4. Lecture stable sur réseau 4G

<span style="color: #2196F3">**Étant donné**</span> que je suis sur réseau 4G
<span style="color: #9E9E9E">**Et**</span> que j'écoute un live

<span style="color: #4CAF50">**Alors**</span> la lecture est fluide
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live régional "Île-de-France"
<span style="color: #9E9E9E">**Et**</span> que je suis situé en Île-de-France

<span style="color: #FF9800">**Quand**</span> je me déplace et sors du département

<span style="color: #4CAF50">**Alors**</span> le live continue de jouer normalement
<span style="color: #9E9E9E">**Et**</span> je peux écouter jusqu'à la fin naturelle du live
<span style="color: #9E9E9E">**Et**</span> après la fin du live, l'algorithme propose du contenu correspondant à ma nouvelle position

---

## 6. Abonné dans la zone reçoit notification push

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "JeanDupont"
<span style="color: #9E9E9E">**Et**</span> que je suis situé en Île-de-France

<span style="color: #FF9800">**Quand**</span> "JeanDupont" démarre un live en Île-de-France

<span style="color: #4CAF50">**Alors**</span> je reçois une notification push "🔴 JeanDupont est en direct : [Titre du live]"
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis abonné au créateur "JeanDupont"
<span style="color: #9E9E9E">**Et**</span> que je suis situé à Lyon

<span style="color: #FF9800">**Quand**</span> "JeanDupont" démarre un live en Île-de-France

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de notification push
<span style="color: #9E9E9E">**Et**</span> cela évite la frustration de ne pas pouvoir écouter un live hors zone

---

## 8. Découverte d'un live via l'algorithme de recommandation

<span style="color: #2196F3">**Étant donné**</span> que je suis dans la zone géographique du live
<span style="color: #9E9E9E">**Et**</span> que je navigue dans l'app avec "Suivant"

<span style="color: #FF9800">**Quand**</span> l'algorithme propose un live en cours

<span style="color: #4CAF50">**Alors**</span> je vois l'indicateur "🔴 EN DIRECT"
<span style="color: #9E9E9E">**Et**</span> je peux choisir de le rejoindre ou de passer au suivant

---

## 9. Reconnexion rapide après coupure réseau (moins de 90 secondes)

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live

<span style="color: #FF9800">**Quand**</span> je perds ma connexion réseau pendant 45 secondes
<span style="color: #9E9E9E">**Et**</span> que je retrouve ma connexion

<span style="color: #4CAF50">**Alors**</span> je reprends le live au moment actuel (pas au buffer ancien)
<span style="color: #9E9E9E">**Et**</span> le saut temporel est transparent (pas de message d'erreur)
<span style="color: #9E9E9E">**Et**</span> je ne rate que quelques secondes de contenu

---

## 10. Reconnexion longue après coupure réseau (90 secondes ou plus)

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live

<span style="color: #FF9800">**Quand**</span> je perds ma connexion réseau pendant 90 secondes
<span style="color: #9E9E9E">**Et**</span> que je retrouve ma connexion

<span style="color: #4CAF50">**Alors**</span> je vois le message "Live en cours perdu, passage au contenu suivant"
<span style="color: #9E9E9E">**Et**</span> l'algorithme propose automatiquement le contenu suivant
<span style="color: #9E9E9E">**Et**</span> je peux manuellement revenir au live s'il est toujours en cours

---

## 11. Interactions disponibles pendant le live - Like

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live
<span style="color: #9E9E9E">**Et**</span> que mon véhicule est à l'arrêt

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton "❤️ Like"

<span style="color: #4CAF50">**Alors**</span> le like est enregistré immédiatement
<span style="color: #9E9E9E">**Et**</span> le compteur de likes visible par le créateur s'incrémente
<span style="color: #9E9E9E">**Et**</span> ma jauge d'intérêt pour les tags du live augmente de +2%

---

## 12. Interactions disponibles pendant le live - Abonnement

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live
<span style="color: #9E9E9E">**Et**</span> que je ne suis pas encore abonné au créateur

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton "S'abonner"

<span style="color: #4CAF50">**Alors**</span> je m'abonne au créateur
<span style="color: #9E9E9E">**Et**</span> ma jauge d'intérêt pour tous les tags du créateur augmente de +5%
<span style="color: #9E9E9E">**Et**</span> je recevrai des notifications pour ses prochains lives

---

## 13. Interactions disponibles pendant le live - Skip

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant" (ou commande au volant)

<span style="color: #4CAF50">**Alors**</span> je quitte le live immédiatement
<span style="color: #9E9E9E">**Et**</span> l'algorithme propose le contenu suivant
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent" (ou commande au volant)

<span style="color: #4CAF50">**Alors**</span> rien ne se passe
<span style="color: #9E9E9E">**Et**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live

<span style="color: #4CAF50">**Alors**</span> aucune interface de chat n'est disponible
<span style="color: #9E9E9E">**Et**</span> je ne peux pas envoyer de messages au créateur
<span style="color: #9E9E9E">**Et**</span> je ne peux pas voir de messages d'autres auditeurs
<span style="color: #9E9E9E">**Et**</span> cette fonctionnalité ne sera jamais implémentée

---

## 16. Réactions emoji désactivées (décision définitive)

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live

<span style="color: #4CAF50">**Alors**</span> aucune réaction emoji n'est disponible
<span style="color: #9E9E9E">**Et**</span> je ne peux pas envoyer d'emoji en temps réel
<span style="color: #9E9E9E">**Et**</span> cette fonctionnalité ne sera jamais implémentée

---

## 17. Message d'information sur l'absence de chat

<span style="color: #2196F3">**Étant donné**</span> que j'écoute mon premier live

<span style="color: #FF9800">**Quand**</span> j'accède à l'interface du live

<span style="color: #4CAF50">**Alors**</span> 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."
<span style="color: #9E9E9E">**Et**</span> ce bandeau n'apparaît qu'une seule fois (première expérience)

---

## 18. Signalement d'un live en cours

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live
<span style="color: #9E9E9E">**Et**</span> que le contenu me semble inapproprié

<span style="color: #FF9800">**Quand**</span> je clique sur le bouton "Signaler"

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> quand je sélectionne une catégorie
<span style="color: #4CAF50">**Alors**</span> le signalement est envoyé en priorité selon la catégorie
<span style="color: #9E9E9E">**Et**</span> un modérateur peut écouter le live en temps réel si besoin

---

## 19. Statistiques visibles par les auditeurs pendant le live

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live

<span style="color: #FF9800">**Quand**</span> je consulte les informations du live

<span style="color: #4CAF50">**Alors**</span> 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é |

<span style="color: #F44336">**Mais**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live avec exactement 247 auditeurs

<span style="color: #FF9800">**Quand**</span> je consulte le nombre d'auditeurs

<span style="color: #4CAF50">**Alors**</span> je vois "~250 auditeurs" (arrondi à la dizaine supérieure)

---

## 21. Qualité audio adaptative pendant le live

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live

<span style="color: #FF9800">**Quand**</span> ma connexion passe de 4G à 3G

<span style="color: #4CAF50">**Alors**</span> la qualité audio s'adapte automatiquement
<span style="color: #9E9E9E">**Et**</span> je passe de 48 kbps à 24 kbps Opus
<span style="color: #9E9E9E">**Et**</span> la transition est transparente sans coupure

---

## 22. Consommation de données pendant un live

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live en qualité standard 48 kbps
<span style="color: #9E9E9E">**Et**</span> que j'écoute pendant 1 heure

<span style="color: #4CAF50">**Alors**</span> j'ai consommé environ 21.6 MB de données mobiles
<span style="color: #9E9E9E">**Et**</span> cette consommation est affichée dans les paramètres de l'app

---

## 23. Lecture du replay après la fin du live

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live depuis 30 minutes

<span style="color: #FF9800">**Quand**</span> le créateur arrête le live

<span style="color: #4CAF50">**Alors**</span> je vois le message "Le live est terminé. Le replay sera disponible dans quelques minutes"
<span style="color: #9E9E9E">**Et**</span> le contenu suivant est automatiquement proposé après 2 secondes

---

## 24. Notification de disponibilité du replay

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté un live jusqu'à la fin
<span style="color: #9E9E9E">**Et**</span> que le replay est publié 8 minutes plus tard

<span style="color: #FF9800">**Quand**</span> je rouvre l'application

<span style="color: #4CAF50">**Alors**</span> je vois une notification in-app "Le replay de [Titre] est maintenant disponible"
<span style="color: #9E9E9E">**Et**</span> je peux cliquer pour l'écouter immédiatement

---

## 25. Aucune publicité pendant un live pour utilisateurs gratuits

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur gratuit
<span style="color: #9E9E9E">**Et**</span> que j'écoute un live

<span style="color: #4CAF50">**Alors**</span> aucune publicité n'est insérée pendant le live
<span style="color: #9E9E9E">**Et**</span> la publicité apparaît seulement entre le live et le contenu suivant

---

## 26. Détection de contexte voiture pendant un live

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live
<span style="color: #9E9E9E">**Et**</span> que ma vitesse est supérieure à 10 km/h

<span style="color: #4CAF50">**Alors**</span> l'interface tactile est désactivée pour la sécurité
<span style="color: #9E9E9E">**Et**</span> seules les commandes au volant sont actives (Play/Pause/Suivant)

---

## 27. Détection de contexte piéton pendant un live

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un live
<span style="color: #9E9E9E">**Et**</span> que ma vitesse est inférieure à 5 km/h

<span style="color: #4CAF50">**Alors**</span> l'interface tactile complète est disponible
<span style="color: #9E9E9E">**Et**</span> je peux liker, m'abonner, signaler via l'écran tactile

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant que créateur vérifié
> <span style="color: #9E9E9E">**Et**</span> que j'ai les permissions de diffusion live
## 1. Vérifications pré-live réussies

<span style="color: #2196F3">**Étant donné**</span> que ma connexion upload est supérieure à 1 Mbps
<span style="color: #9E9E9E">**Et**</span> que j'ai autorisé l'accès au microphone
<span style="color: #9E9E9E">**Et**</span> que j'ai défini une zone de diffusion "Île-de-France"

<span style="color: #FF9800">**Quand**</span> je lance les vérifications pré-live

<span style="color: #4CAF50">**Alors**</span> toutes les vérifications sont validées
<span style="color: #9E9E9E">**Et**</span> je peux démarrer le live

---

## 2. Échec pré-live avec connexion insuffisante

<span style="color: #2196F3">**Étant donné**</span> que ma connexion upload est de 0.5 Mbps

<span style="color: #FF9800">**Quand**</span> je lance les vérifications pré-live

<span style="color: #4CAF50">**Alors**</span> je vois un warning "Connexion insuffisante pour garantir une diffusion stable (minimum 1 Mbps)"
<span style="color: #9E9E9E">**Et**</span> je peux choisir de continuer quand même ou d'annuler

---

## 3. Échec pré-live sans autorisation microphone

<span style="color: #2196F3">**Étant donné**</span> que je n'ai pas autorisé l'accès au microphone

<span style="color: #FF9800">**Quand**</span> j'essaie de démarrer un live

<span style="color: #4CAF50">**Alors**</span> je vois le message "Accès au microphone requis pour démarrer un live"
<span style="color: #9E9E9E">**Et**</span> je suis redirigé vers les paramètres système

---

## 4. Échec pré-live sans zone de diffusion définie

<span style="color: #2196F3">**Étant donné**</span> que je n'ai pas défini de zone de diffusion

<span style="color: #FF9800">**Quand**</span> j'essaie de démarrer un live

<span style="color: #4CAF50">**Alors**</span> je vois le message "Veuillez définir une zone de diffusion avant de démarrer"
<span style="color: #9E9E9E">**Et**</span> je suis redirigé vers le formulaire de configuration du live

---

## 5. Démarrage live avec buffer 15 secondes

<span style="color: #2196F3">**Étant donné**</span> que toutes les vérifications pré-live sont validées

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Démarrer live"

<span style="color: #4CAF50">**Alors**</span> je vois le message "Live démarre dans 15s... Testez votre micro"
<span style="color: #9E9E9E">**Et**</span> un compte à rebours de 15 secondes s'affiche
<span style="color: #9E9E9E">**Et**</span> mon flux audio est enregistré pendant ces 15 secondes
<span style="color: #9E9E9E">**Et**</span> le live n'est pas encore visible publiquement

---

## 6. Live devient public après buffer initial

<span style="color: #2196F3">**Étant donné**</span> que j'ai démarré un live
<span style="color: #9E9E9E">**Et**</span> que le buffer de 15 secondes s'est écoulé

<span style="color: #4CAF50">**Alors**</span> le live devient public
<span style="color: #9E9E9E">**Et**</span> les auditeurs peuvent le rejoindre
<span style="color: #9E9E9E">**Et**</span> les abonnés dans la zone reçoivent une notification push

---

## 7. Notification push aux abonnés dans la zone géographique

<span style="color: #2196F3">**Étant donné**</span> que j'ai 1000 abonnés au total
<span style="color: #9E9E9E">**Et**</span> que 300 abonnés sont situés en Île-de-France
<span style="color: #9E9E9E">**Et**</span> que 700 abonnés sont situés hors Île-de-France

<span style="color: #FF9800">**Quand**</span> mon live en Île-de-France devient public

<span style="color: #4CAF50">**Alors**</span> 300 abonnés reçoivent une notification push "🔴 [Mon pseudo] est en direct : [Titre live]"
<span style="color: #9E9E9E">**Et**</span> 700 abonnés ne reçoivent pas de notification

---

## 8. Configuration métadonnées obligatoires pour un live

<span style="color: #FF9800">**Quand**</span> je configure un nouveau live

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #FF9800">**Quand**</span> j'essaie de créer un live avec le titre "Live"

<span style="color: #4CAF50">**Alors**</span> la validation échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Le titre doit contenir entre 5 et 100 caractères"

---

## 10. Validation échouée sans tags

<span style="color: #2196F3">**Étant donné**</span> que j'ai rempli tous les champs sauf les tags

<span style="color: #FF9800">**Quand**</span> j'essaie de démarrer le live

<span style="color: #4CAF50">**Alors**</span> la validation échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Veuillez sélectionner entre 1 et 3 centres d'intérêt"

---

## 11. Limite de durée 8 heures

<span style="color: #2196F3">**Étant donné**</span> que mon live dure depuis 7 heures et 30 minutes

<span style="color: #4CAF50">**Alors**</span> je vois un warning "Votre live se terminera dans 30 min"
<span style="color: #9E9E9E">**Et**</span> le message est affiché de manière non intrusive

---

## 12. Arrêt automatique à 8 heures

<span style="color: #2196F3">**Étant donné**</span> que mon live dure depuis 8 heures

<span style="color: #4CAF50">**Alors**</span> le live s'arrête automatiquement
<span style="color: #9E9E9E">**Et**</span> je vois le message "Durée maximale atteinte (8 heures). Vous pouvez redémarrer un nouveau live si nécessaire"
<span style="color: #9E9E9E">**Et**</span> le processus de traitement post-live démarre

---

## 13. Diffusion contenu interdit - Concert en direct

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un concert en direct depuis une salle
<span style="color: #9E9E9E">**Et**</span> qu'un auditeur signale le contenu pour "Violation droits d'auteur"

<span style="color: #FF9800">**Quand**</span> un modérateur écoute le live
<span style="color: #9E9E9E">**Et**</span> qu'il confirme la violation

<span style="color: #4CAF50">**Alors**</span> le live est arrêté immédiatement
<span style="color: #9E9E9E">**Et**</span> je reçois un Strike 2 (suspension 7 jours)
<span style="color: #9E9E9E">**Et**</span> je vois le message "Votre live a été interrompu pour violation des droits d'auteur"
<span style="color: #9E9E9E">**Et**</span> le replay n'est pas publié

---

## 14. Diffusion contenu interdit - Événement sportif payant

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un match de football avec droits TV
<span style="color: #9E9E9E">**Et**</span> que le contenu est détecté par l'IA audio fingerprint

<span style="color: #FF9800">**Quand**</span> la détection est confirmée

<span style="color: #4CAF50">**Alors**</span> le live est arrêté immédiatement
<span style="color: #9E9E9E">**Et**</span> je reçois un Strike 2 (suspension 7 jours)

---

## 15. Diffusion contenu violent

<span style="color: #2196F3">**Étant donné**</span> que je diffuse du contenu violent (agression physique)
<span style="color: #9E9E9E">**Et**</span> que 5 auditeurs signalent le contenu

<span style="color: #FF9800">**Quand**</span> un modérateur vérifie en temps réel
<span style="color: #9E9E9E">**Et**</span> confirme la violence

<span style="color: #4CAF50">**Alors**</span> le live est coupé immédiatement
<span style="color: #9E9E9E">**Et**</span> mon compte est banni définitivement
<span style="color: #9E9E9E">**Et**</span> les autorités sont notifiées

---

## 16. Détection musique protégée en arrière-plan

<span style="color: #2196F3">**Étant donné**</span> que mon live contient de la musique protégée en fond

<span style="color: #FF9800">**Quand**</span> l'IA audio fingerprint détecte la violation après 2 minutes

<span style="color: #4CAF50">**Alors**</span> 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"
<span style="color: #9E9E9E">**Et**</span> j'ai 30 secondes pour corriger
<span style="color: #9E9E9E">**Et**</span> si je ne corrige pas, le live est arrêté avec Strike 1

---

## 17. Signalement pendant un live

<span style="color: #2196F3">**Étant donné**</span> que je diffuse un live
<span style="color: #9E9E9E">**Et**</span> qu'un auditeur clique sur "Signaler"

<span style="color: #FF9800">**Quand**</span> l'auditeur sélectionne la catégorie "Harcèlement"

<span style="color: #4CAF50">**Alors**</span> le signalement est envoyé en priorité HAUTE
<span style="color: #9E9E9E">**Et**</span> un modérateur peut écouter le live en temps réel
<span style="color: #9E9E9E">**Et**</span> le live continue pendant l'écoute de vérification

---

## 18. Dépassement nombre de lives simultanés autorisés (limite plateforme)

<span style="color: #2196F3">**Étant donné**</span> que la plateforme héberge actuellement 2000 lives simultanés
<span style="color: #9E9E9E">**Et**</span> que c'est la limite de l'infrastructure actuelle

<span style="color: #FF9800">**Quand**</span> j'essaie de démarrer un nouveau live

<span style="color: #4CAF50">**Alors**</span> je vois le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes"
<span style="color: #9E9E9E">**Et**</span> ma demande est mise en file d'attente prioritaire si je suis créateur Premium

---

## 19. Premier live d'un nouveau créateur

<span style="color: #2196F3">**Étant donné**</span> que je n'ai jamais diffusé de live auparavant
<span style="color: #9E9E9E">**Et**</span> que j'ai moins de 3 contenus validés

<span style="color: #FF9800">**Quand**</span> j'essaie de démarrer mon premier live

<span style="color: #4CAF50">**Alors**</span> je vois le message "Les lives sont disponibles après validation de vos 3 premiers contenus"
<span style="color: #9E9E9E">**Et**</span> le bouton "Démarrer live" est désactivé

---

## 20. Créateur avec score de confiance faible

<span style="color: #2196F3">**Étant donné**</span> que j'ai 2 strikes actifs

<span style="color: #FF9800">**Quand**</span> j'essaie de démarrer un live

<span style="color: #4CAF50">**Alors**</span> je vois le message "Fonctionnalité live temporairement indisponible suite à vos sanctions"
<span style="color: #9E9E9E">**Et**</span> je dois attendre la fin de ma suspension

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'application RoadWave est démarrée
> <span style="color: #9E9E9E">**Et**</span> que l'utilisateur "jean@example.com" est connecté
## 1. Recherche full-text basique

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> l'utilisateur recherche "paris"

<span style="color: #4CAF50">**Alors**</span> 2 résultats sont retournés
<span style="color: #9E9E9E">**Et**</span> les résultats incluent "Balade à Paris"
<span style="color: #9E9E9E">**Et**</span> les résultats incluent "Secrets de Montmartre"

---

## 2. Recherche avec stemming français

<span style="color: #2196F3">**Étant donné**</span> un contenu avec le titre "Voyage en Bretagne"

<span style="color: #FF9800">**Quand**</span> l'utilisateur recherche "voyages"

<span style="color: #4CAF50">**Alors**</span> le contenu "Voyage en Bretagne" est trouvé
<span style="color: #9E9E9E">**Et**</span> le stemming a transformé "voyages" en racine "voyag"

---

## 3. 📋 Plan: Stemming français sur différentes formes

<span style="color: #2196F3">**Étant donné**</span> un contenu avec le mot "<mot_original>"

<span style="color: #FF9800">**Quand**</span> l'utilisateur recherche "<recherche>"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> un contenu avec le titre "Découverte de l'Élysée"

<span style="color: #FF9800">**Quand**</span> l'utilisateur recherche "decouverte elysee"

<span style="color: #4CAF50">**Alors**</span> le contenu est trouvé
<span style="color: #9E9E9E">**Et**</span> les accents sont normalisés automatiquement

---

## 5. Champs indexés avec pondération

<span style="color: #2196F3">**Étant donné**</span> les contenus suivants:

    | titre | description | créateur | tags |
    |---|---|---|---|
    | Voyage Paris | Balade sympa | @user1 | Tourisme |
    | Balade Lyon | Voyage en ville | @paris_guide | Voyage |


<span style="color: #FF9800">**Quand**</span> l'utilisateur recherche "paris"

<span style="color: #4CAF50">**Alors**</span> "Voyage Paris" est en première position
<span style="color: #9E9E9E">**Et**</span> "@paris_guide" apparaît en second

---

## 6. Ranking par pertinence et popularité

<span style="color: #2196F3">**Étant donné**</span> les contenus suivants:

    | titre | écoutes | rang_texte |
    |---|---|---|
    | Balade Paris | 50000 | 0.8 |
    | Paris la nuit | 1000 | 0.9 |


<span style="color: #FF9800">**Quand**</span> l'utilisateur recherche "paris"

<span style="color: #4CAF50">**Alors**</span> le score final combine rang_texte × (1 + log(écoutes + 1))
<span style="color: #9E9E9E">**Et**</span> "Balade Paris" est mieux classé grâce à sa popularité

---

## 7. Autocomplete pendant la frappe

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur commence à taper "par"

<span style="color: #FF9800">**Quand**</span> 3 caractères sont saisis

<span style="color: #4CAF50">**Alors**</span> des suggestions apparaissent:

    | suggestion |
    |---|
    | paris |
    | parc naturel |
    | parvis notre-dame |

<span style="color: #9E9E9E">**Et**</span> le top 5 des suggestions est affiché

---

## 8. Historique des 10 dernières recherches

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> l'utilisateur ouvre la barre de recherche

<span style="color: #4CAF50">**Alors**</span> les 10 dernières recherches sont affichées
<span style="color: #9E9E9E">**Et**</span> elles sont triées par date décroissante

---

## 9. Correction automatique si aucun résultat

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur recherche "ballade paris" (faute d'orthographe)
<span style="color: #9E9E9E">**Et**</span> qu'aucun résultat n'est trouvé

<span style="color: #FF9800">**Quand**</span> la page de résultats s'affiche

<span style="color: #4CAF50">**Alors**</span> une suggestion "Essayez plutôt : balade paris" est affichée

---

## 10. Recherches populaires suggérées

<span style="color: #2196F3">**Étant donné**</span> qu'aucun résultat n'est trouvé pour une recherche

<span style="color: #FF9800">**Quand**</span> la page s'affiche

<span style="color: #4CAF50">**Alors**</span> des suggestions populaires sont affichées:

    | suggestion |
    |---|
    | balade paris |
    | audio-guide louvre |
    | visite montmartre |


---

## 11. Saisie d'un lieu avec autocomplete

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur ouvre le filtre "Lieu"

<span style="color: #FF9800">**Quand**</span> il tape "Louv"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur sélectionne "Paris, France"
<span style="color: #9E9E9E">**Et**</span> que les coordonnées sont (48.8566, 2.3522)

<span style="color: #FF9800">**Quand**</span> il définit un rayon de 50 km

<span style="color: #4CAF50">**Alors**</span> la recherche PostGIS utilise ST_DWithin avec 50000 mètres

---

## 13. 📋 Plan: Recherche géographique avec différents rayons

<span style="color: #2196F3">**Étant donné**</span> un contenu à 30 km de Paris

<span style="color: #FF9800">**Quand**</span> l'utilisateur recherche autour de Paris avec un rayon de <rayon>

<span style="color: #4CAF50">**Alors**</span> le contenu est <résultat>

**📊 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)

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur active le GPS
<span style="color: #9E9E9E">**Et**</span> que sa position est (48.8566, 2.3522)

<span style="color: #FF9800">**Quand**</span> il sélectionne "Autour de moi"

<span style="color: #4CAF50">**Alors**</span> la recherche utilise ses coordonnées GPS actuelles
<span style="color: #9E9E9E">**Et**</span> un rayon par défaut de 10 km est appliqué

---

## 15. Curseur de rayon avec limites

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur ouvre le curseur de rayon

<span style="color: #FF9800">**Quand**</span> il ajuste le curseur

<span style="color: #4CAF50">**Alors**</span> les valeurs disponibles vont de 5 km à 500 km
<span style="color: #9E9E9E">**Et**</span> la valeur s'affiche en temps réel "50 km"

---

## 16. Affichage de la distance dans les résultats

<span style="color: #2196F3">**Étant donné**</span> une recherche géographique autour de Paris
<span style="color: #9E9E9E">**Et**</span> un contenu à 2.3 km de distance

<span style="color: #FF9800">**Quand**</span> les résultats sont affichés

<span style="color: #4CAF50">**Alors**</span> la distance "À 2.3 km" est indiquée pour chaque résultat

---

## 17. 📋 Plan: Tri par proximité géographique

<span style="color: #2196F3">**Étant donné**</span> des contenus à différentes distances de Paris:

    | contenu | distance |
    |---|---|
    | Louvre Guide | 0.5 km |
    | Tour Eiffel | 2.0 km |
    | Versailles | 20 km |


<span style="color: #FF9800">**Quand**</span> l'utilisateur trie par "Proximité"

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que l'application est en phase MVP

<span style="color: #FF9800">**Quand**</span> une requête de géocodage est effectuée

<span style="color: #4CAF50">**Alors**</span> l'API publique Nominatim est utilisée
<span style="color: #9E9E9E">**Et**</span> le rate limit de 1 req/s est respecté

---

## 19. Géocodage avec fallback Mapbox

<span style="color: #2196F3">**Étant donné**</span> que Nominatim ne retourne aucun résultat

<span style="color: #FF9800">**Quand**</span> l'application tente un fallback

<span style="color: #4CAF50">**Alors**</span> l'API Mapbox Geocoding est utilisée
<span style="color: #9E9E9E">**Et**</span> le coût de 0.50€ / 1000 requêtes est appliqué

---

## 20. Ouverture du panneau de filtres

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur est sur la page de recherche

<span style="color: #FF9800">**Quand**</span> il clique sur "Filtres"

<span style="color: #4CAF50">**Alors**</span> un panneau latéral s'ouvre
<span style="color: #9E9E9E">**Et**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur ouvre les filtres

<span style="color: #FF9800">**Quand**</span> il sélectionne:

    | type |
    |---|
    | Contenu court |
    | Audio-guide |


<span style="color: #4CAF50">**Alors**</span> seuls ces types de contenus sont recherchés
<span style="color: #9E9E9E">**Et**</span> les podcasts et radios live sont exclus

---

## 22. 📋 Plan: Filtre par durée

<span style="color: #2196F3">**Étant donné**</span> un contenu de <durée> minutes

<span style="color: #FF9800">**Quand**</span> l'utilisateur filtre par "<tranche>"

<span style="color: #4CAF50">**Alors**</span> le contenu est <résultat>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> des contenus avec différentes classifications:

    | contenu | classification |
    |---|---|
    | Conte enfants | Tout public |
    | Podcast news | 13+ |
    | Débat politique | 16+ |


<span style="color: #FF9800">**Quand**</span> l'utilisateur filtre "Tout public"

<span style="color: #4CAF50">**Alors**</span> seul "Conte enfants" est affiché

---

## 24. Filtre par géo-pertinence

<span style="color: #2196F3">**Étant donné**</span> des contenus avec différents types géo:

    | contenu | type_geo |
    |---|---|
    | Guide Louvre | Ancré |
    | Podcast Paris | Contextuel |
    | News nationales | Neutre |


<span style="color: #FF9800">**Quand**</span> l'utilisateur filtre "Ancré, Contextuel"

<span style="color: #4CAF50">**Alors**</span> "Guide Louvre" et "Podcast Paris" sont affichés
<span style="color: #9E9E9E">**Et**</span> "News nationales" est exclu

---

## 25. Filtre par tags (multi-sélection)

<span style="color: #2196F3">**Étant donné**</span> des contenus taggés:

    | contenu | tags |
    |---|---|
    | Voyage en Italie | Voyage, Gastronomie |
    | Histoire de Rome | Voyage, Histoire |
    | Économie italienne | Économie |


<span style="color: #FF9800">**Quand**</span> l'utilisateur sélectionne les tags "Voyage, Histoire"

<span style="color: #4CAF50">**Alors**</span> "Histoire de Rome" est en priorité (2 tags correspondants)
<span style="color: #9E9E9E">**Et**</span> "Voyage en Italie" est affiché (1 tag correspondant)
<span style="color: #9E9E9E">**Et**</span> "Économie italienne" est exclu

---

## 26. 📋 Plan: Filtre par date de publication

<span style="color: #2196F3">**Étant donné**</span> un contenu publié il y a <délai>

<span style="color: #FF9800">**Quand**</span> l'utilisateur filtre par "<période>"

<span style="color: #4CAF50">**Alors**</span> le contenu est <résultat>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> des contenus gratuits et Premium:

    | contenu | type |
    |---|---|
    | Balade Paris | Gratuit |
    | Visite VIP Louvre | Premium |


<span style="color: #FF9800">**Quand**</span> l'utilisateur filtre "Premium uniquement 👑"

<span style="color: #4CAF50">**Alors**</span> seul "Visite VIP Louvre" est affiché

---

## 28. Combinaison de filtres multiples (AND logic)

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur applique les filtres:

    | filtre | valeur |
    |---|---|
    | Type | Audio-guide |
    | Durée | 5-15 min |
    | Tags | Voyage |
    | Classification | Tout public |


<span style="color: #FF9800">**Quand**</span> la recherche est lancée

<span style="color: #4CAF50">**Alors**</span> seuls les contenus respectant TOUS les critères sont affichés

---

## 29. Réinitialisation des filtres

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a appliqué 5 filtres différents

<span style="color: #FF9800">**Quand**</span> il clique sur "Réinitialiser"

<span style="color: #4CAF50">**Alors**</span> tous les filtres sont désactivés
<span style="color: #9E9E9E">**Et**</span> la recherche affiche tous les résultats

---

## 30. Sauvegarde d'une recherche

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a appliqué plusieurs filtres

<span style="color: #FF9800">**Quand**</span> il clique sur "💾 Sauvegarder cette recherche"
<span style="color: #9E9E9E">**Et**</span> qu'il entre le nom "Podcasts voyage Paris"

<span style="color: #4CAF50">**Alors**</span> la recherche est sauvegardée
<span style="color: #9E9E9E">**Et**</span> elle apparaît dans l'onglet "Recherches sauvegardées"

---

## 31. Limite de 5 recherches sauvegardées

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a déjà 5 recherches sauvegardées

<span style="color: #FF9800">**Quand**</span> il tente de sauvegarder une 6ème recherche

<span style="color: #4CAF50">**Alors**</span> un message d'erreur s'affiche
<span style="color: #9E9E9E">**Et**</span> il doit supprimer une recherche existante avant d'en ajouter une nouvelle

---

## 32. Notifications pour recherches sauvegardées

<span style="color: #2196F3">**Étant donné**</span> une recherche sauvegardée "Podcasts voyage Paris"
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur a activé les notifications

<span style="color: #FF9800">**Quand**</span> 3 nouveaux contenus correspondants sont publiés

<span style="color: #4CAF50">**Alors**</span> une notification "3 nouveaux contenus dans 'Podcasts voyage Paris'" est envoyée

---

## 33. 📋 Plan: Options de tri des résultats

<span style="color: #2196F3">**Étant donné**</span> une recherche avec plusieurs résultats

<span style="color: #FF9800">**Quand**</span> l'utilisateur sélectionne le tri "<option>"

<span style="color: #4CAF50">**Alors**</span> les résultats sont triés selon <algorithme>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> un résultat de recherche

<span style="color: #FF9800">**Quand**</span> la page est affichée

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> une page avec 20 résultats de recherche

<span style="color: #FF9800">**Quand**</span> la page se charge

<span style="color: #4CAF50">**Alors**</span> seules les 5 premières images sont chargées
<span style="color: #9E9E9E">**Et**</span> les images suivantes se chargent au scroll

---

## 36. Troncature du titre sur 2 lignes maximum

<span style="color: #2196F3">**Étant donné**</span> un contenu avec un titre de 120 caractères

<span style="color: #FF9800">**Quand**</span> le résultat est affiché

<span style="color: #4CAF50">**Alors**</span> le titre est tronqué après 2 lignes
<span style="color: #9E9E9E">**Et**</span> "..." est ajouté à la fin

---

## 37. Lien cliquable vers le profil créateur

<span style="color: #2196F3">**Étant donné**</span> un résultat de recherche pour "@paris_stories"

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur "@paris_stories"

<span style="color: #4CAF50">**Alors**</span> il est redirigé vers "https://roadwave.fr/@paris_stories"

---

## 38. Menu contextuel d'un résultat [⋮]

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur clique sur [⋮] pour un résultat

<span style="color: #FF9800">**Quand**</span> le menu s'ouvre

<span style="color: #4CAF50">**Alors**</span> les actions suivantes sont disponibles:

    | action |
    |---|
    | Partager |
    | Ajouter à une playlist |
    | Télécharger (offline) |
    | Signaler |


---

## 39. Pagination avec 20 résultats par page

<span style="color: #2196F3">**Étant donné**</span> une recherche retournant 100 résultats

<span style="color: #FF9800">**Quand**</span> la page est affichée

<span style="color: #4CAF50">**Alors**</span> 20 résultats sont chargés initialement
<span style="color: #9E9E9E">**Et**</span> un indicateur "1-20 sur 100 résultats" est visible

---

## 40. Infinite scroll automatique

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur scroll dans les résultats

<span style="color: #FF9800">**Quand**</span> il atteint 80% de la page

<span style="color: #4CAF50">**Alors**</span> les 20 résultats suivants sont chargés automatiquement
<span style="color: #9E9E9E">**Et**</span> un loader est affiché pendant le chargement

---

## 41. Bouton fallback "Charger 20 suivants"

<span style="color: #2196F3">**Étant donné**</span> que l'infinite scroll est désactivé (paramètres)

<span style="color: #FF9800">**Quand**</span> l'utilisateur atteint la fin de la page

<span style="color: #4CAF50">**Alors**</span> un bouton "Charger 20 suivants" est affiché
<span style="color: #9E9E9E">**Et**</span> les résultats se chargent au clic

---

## 42. Basculement entre vue liste et vue carte

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur est sur la page de résultats

<span style="color: #FF9800">**Quand**</span> il clique sur le toggle "Liste / Carte"

<span style="color: #4CAF50">**Alors**</span> la vue carte Leaflet s'affiche
<span style="color: #9E9E9E">**Et**</span> les résultats sont affichés comme markers sur la carte

---

## 43. Affichage de la carte Leaflet

<span style="color: #2196F3">**Étant donné**</span> que la vue carte est activée

<span style="color: #FF9800">**Quand**</span> la carte se charge

<span style="color: #4CAF50">**Alors**</span> la carte utilise les tuiles OpenStreetMap
<span style="color: #9E9E9E">**Et**</span> le centre est la position de recherche (ou GPS utilisateur)
<span style="color: #9E9E9E">**Et**</span> le zoom initial montre tous les résultats

---

## 44. Markers cliquables sur la carte

<span style="color: #2196F3">**Étant donné**</span> que 10 résultats sont affichés sur la carte

<span style="color: #FF9800">**Quand**</span> l'utilisateur clique sur un marker

<span style="color: #4CAF50">**Alors**</span> une popup s'affiche avec:

    | élément |
    |---|
    | Titre |
    | Créateur |
    | Durée |
    | Distance |
    | Bouton ▶️ Écouter |


---

## 45. Clustering des markers proches

<span style="color: #2196F3">**Étant donné**</span> que 50 résultats sont très proches géographiquement

<span style="color: #FF9800">**Quand**</span> la carte est affichée

<span style="color: #4CAF50">**Alors**</span> les markers proches sont groupés en clusters
<span style="color: #9E9E9E">**Et**</span> le nombre de contenus est affiché sur le cluster
<span style="color: #9E9E9E">**Et**</span> le cluster se décompose au zoom

---

## 46. Synchronisation liste / carte

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur est en vue carte

<span style="color: #FF9800">**Quand**</span> il clique sur un marker et écoute le contenu
<span style="color: #9E9E9E">**Et**</span> qu'il rebascule en vue liste

<span style="color: #4CAF50">**Alors**</span> le contenu écouté est marqué dans la liste
<span style="color: #9E9E9E">**Et**</span> la position de scroll est maintenue

---

## 47. Index PostgreSQL full-text pour performances

<span style="color: #2196F3">**Étant donné**</span> que la base contient 100K contenus

<span style="color: #FF9800">**Quand**</span> une recherche full-text est effectuée

<span style="color: #4CAF50">**Alors**</span> l'index GIN sur to_tsvector est utilisé
<span style="color: #9E9E9E">**Et**</span> la requête retourne en moins de 100ms

---

## 48. Index PostGIS GIST pour recherche géo

<span style="color: #2196F3">**Étant donné**</span> une recherche géographique avec rayon 50 km

<span style="color: #FF9800">**Quand**</span> la requête PostGIS ST_DWithin est exécutée

<span style="color: #4CAF50">**Alors**</span> l'index GIST sur la colonne location est utilisé
<span style="color: #9E9E9E">**Et**</span> la requête retourne en moins de 50ms

---

## 49. Index composites pour filtres

<span style="color: #2196F3">**Étant donné**</span> une recherche avec filtres multiples

<span style="color: #FF9800">**Quand**</span> les filtres type, durée, âge, géo, date sont appliqués

<span style="color: #4CAF50">**Alors**</span> l'index composite idx_content_filters est utilisé
<span style="color: #9E9E9E">**Et**</span> les performances restent optimales

---

## 50. Index GIN pour recherche par tags

<span style="color: #2196F3">**Étant donné**</span> une recherche filtrée par tags "Voyage, Histoire"

<span style="color: #FF9800">**Quand**</span> la requête est exécutée

<span style="color: #4CAF50">**Alors**</span> l'index GIN sur la colonne tags est utilisé
<span style="color: #9E9E9E">**Et**</span> la recherche est performante même avec 500K contenus

---

## 51. Aucun résultat trouvé

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur recherche "xyzabc123"

<span style="color: #FF9800">**Quand**</span> aucun résultat n'est trouvé

<span style="color: #4CAF50">**Alors**</span> un message "Aucun résultat pour 'xyzabc123'" s'affiche
<span style="color: #9E9E9E">**Et**</span> des suggestions de recherches populaires sont proposées

---

## 52. Recherche vide

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur clique sur "Rechercher" sans saisir de texte

<span style="color: #FF9800">**Quand**</span> la recherche est lancée

<span style="color: #4CAF50">**Alors**</span> un message "Veuillez entrer au moins 2 caractères" s'affiche

---

## 53. Erreur de géocodage Nominatim

<span style="color: #2196F3">**Étant donné**</span> que l'API Nominatim est indisponible

<span style="color: #FF9800">**Quand**</span> l'utilisateur tente une recherche géographique

<span style="color: #4CAF50">**Alors**</span> un message "Service de localisation temporairement indisponible" s'affiche
<span style="color: #9E9E9E">**Et**</span> la recherche continue sans filtre géographique

---

## 54. GPS désactivé pour "Autour de moi"

<span style="color: #2196F3">**Étant donné**</span> que l'utilisateur a désactivé le GPS

<span style="color: #FF9800">**Quand**</span> il sélectionne "Autour de moi"

<span style="color: #4CAF50">**Alors**</span> un message "Veuillez activer la localisation" s'affiche
<span style="color: #9E9E9E">**Et**</span> un bouton "Activer" ouvre les paramètres système

---

## 55. Timeout de recherche après 10 secondes

<span style="color: #2196F3">**Étant donné**</span> qu'une recherche complexe est lancée

<span style="color: #FF9800">**Quand**</span> la requête dépasse 10 secondes

<span style="color: #4CAF50">**Alors**</span> la recherche est annulée
<span style="color: #9E9E9E">**Et**</span> un message "La recherche a pris trop de temps, veuillez réessayer" s'affiche

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Créateur choisit le type géo-ancré pour un audio-guide

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur connecté

<span style="color: #FF9800">**Quand**</span> je publie un audio-guide de la Tour Eiffel
<span style="color: #9E9E9E">**Et**</span> que je choisis la classification "Géo-ancré"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur connecté

<span style="color: #FF9800">**Quand**</span> je publie une actualité régionale en Bretagne
<span style="color: #9E9E9E">**Et**</span> que je choisis la classification "Géo-contextuel"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur connecté

<span style="color: #FF9800">**Quand**</span> je publie un podcast de philosophie
<span style="color: #9E9E9E">**Et**</span> que je choisis la classification "Géo-neutre"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je crée un contenu audio

<span style="color: #FF9800">**Quand**</span> j'essaie de publier sans sélectionner de type géographique

<span style="color: #4CAF50">**Alors**</span> la publication échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Vous devez sélectionner un type de géo-pertinence"

---

## 5. Modérateur reclassifie un contenu mal catégorisé

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu podcast générique est classifié "Géo-ancré"
<span style="color: #9E9E9E">**Et**</span> que le modérateur examine le contenu

<span style="color: #FF9800">**Quand**</span> le modérateur le reclassifie en "Géo-neutre"

<span style="color: #4CAF50">**Alors**</span> la nouvelle classification est appliquée immédiatement
<span style="color: #9E9E9E">**Et**</span> l'algorithme utilise la pondération géo = 0.2
<span style="color: #9E9E9E">**Et**</span> le créateur reçoit une notification de reclassification

---

## 6. Créateur modifie la classification après publication

<span style="color: #2196F3">**Étant donné**</span> que j'ai publié un contenu classifié "Géo-contextuel"
<span style="color: #9E9E9E">**Et**</span> que je réalise qu'il devrait être "Géo-neutre"

<span style="color: #FF9800">**Quand**</span> je modifie la classification en "Géo-neutre"

<span style="color: #4CAF50">**Alors**</span> la modification est enregistrée
<span style="color: #9E9E9E">**Et**</span> l'algorithme utilise la nouvelle pondération
<span style="color: #9E9E9E">**Et**</span> je vois le message "Classification modifiée avec succès"

---

## 7. Statistiques de classification dans le profil créateur

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur
<span style="color: #9E9E9E">**Et**</span> que j'ai publié 30 contenus:

    | type | nombre |
    |---|---|
    | Géo-ancré | 10 |
    | Géo-contextuel | 15 |
    | Géo-neutre | 5 |


<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> je vois la répartition de mes classifications
<span style="color: #9E9E9E">**Et**</span> des suggestions pour optimiser la portée

---

## 8. Contenu géo-ancré fortement pondéré par la proximité

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide "Géo-ancré" existe à la Tour Eiffel
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est à 100m de la Tour Eiffel

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score

<span style="color: #4CAF50">**Alors**</span> la pondération géo est de 0.7
<span style="color: #9E9E9E">**Et**</span> le score géo est proche de 1 (très proche)
<span style="color: #9E9E9E">**Et**</span> le contenu a un score final élevé

---

## 9. Contenu géo-neutre moins sensible à la distance

<span style="color: #2196F3">**Étant donné**</span> qu'un podcast philosophie "Géo-neutre" existe à Paris
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est à Marseille (750 km)

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score

<span style="color: #4CAF50">**Alors**</span> la pondération géo est de 0.2
<span style="color: #9E9E9E">**Et**</span> le score géo est bas (distance élevée)
<span style="color: #F44336">**Mais**</span> le score intérêts (0.6) peut compenser
<span style="color: #9E9E9E">**Et**</span> le contenu peut quand même être recommandé si intérêts match

---

## 10. Comparaison scores entre types géo pour même distance

<span style="color: #2196F3">**Étant donné**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est à 50 km de Paris

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule les scores

<span style="color: #4CAF50">**Alors**</span> le contenu "Géo-ancré" a le score géo le plus élevé
<span style="color: #9E9E9E">**Et**</span> le contenu "Géo-neutre" a le score géo le plus faible
<span style="color: #F44336">**Mais**</span> peut avoir un score final plus élevé si forte correspondance intérêts

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Créateur tagge son contenu comme "Politique"

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur connecté

<span style="color: #FF9800">**Quand**</span> je publie un contenu sur un débat politique
<span style="color: #9E9E9E">**Et**</span> que je sélectionne le tag "Politique"

<span style="color: #4CAF50">**Alors**</span> le contenu est enregistré avec le tag "Politique"
<span style="color: #9E9E9E">**Et**</span> aucune classification gauche/droite n'est demandée (MVP)

---

## 2. Tag "Politique" au même niveau que les autres tags

<span style="color: #2196F3">**Étant donné**</span> que je crée un contenu

<span style="color: #FF9800">**Quand**</span> je consulte la liste des tags disponibles

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un nouvel utilisateur
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas modifié les paramètres de contenu politique

<span style="color: #FF9800">**Quand**</span> je demande des recommandations

<span style="color: #4CAF50">**Alors**</span> les contenus tagués "Politique" sont inclus normalement
<span style="color: #9E9E9E">**Et**</span> aucun filtrage n'est appliqué

---

## 4. Activer le filtrage "Masquer contenu politique"

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté

<span style="color: #FF9800">**Quand**</span> j'active l'option "Masquer contenu politique" dans les paramètres

<span style="color: #4CAF50">**Alors**</span> tous les contenus tagués "Politique" sont exclus de mes recommandations
<span style="color: #9E9E9E">**Et**</span> je vois le message "Contenu politique masqué"

---

## 5. Filtrage politique actif - aucun contenu politique recommandé

<span style="color: #2196F3">**Étant donné**</span> que j'ai activé "Masquer contenu politique"
<span style="color: #9E9E9E">**Et**</span> qu'il existe 100 contenus dont 20 tagués "Politique"

<span style="color: #FF9800">**Quand**</span> je demande 50 recommandations

<span style="color: #4CAF50">**Alors**</span> je reçois 50 contenus parmi les 80 non-politiques
<span style="color: #9E9E9E">**Et**</span> 0% de contenus politiques sont proposés

---

## 6. Désactiver le filtrage "Masquer contenu politique"

<span style="color: #2196F3">**Étant donné**</span> que j'ai activé "Masquer contenu politique"

<span style="color: #FF9800">**Quand**</span> je désactive cette option dans les paramètres

<span style="color: #4CAF50">**Alors**</span> les contenus politiques sont à nouveau inclus dans mes recommandations
<span style="color: #9E9E9E">**Et**</span> le filtrage est levé immédiatement

---

## 7. Mode Kids filtre automatiquement le contenu politique

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de 14 ans
<span style="color: #9E9E9E">**Et**</span> que le mode Kids est activé

<span style="color: #FF9800">**Quand**</span> je demande des recommandations

<span style="color: #4CAF50">**Alors**</span> tous les contenus tagués "Politique" sont automatiquement exclus
<span style="color: #9E9E9E">**Et**</span> ce indépendamment du paramètre "Masquer contenu politique"

---

## 8. Statistiques créateur sur contenu politique

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur
<span style="color: #9E9E9E">**Et**</span> que j'ai publié 20 contenus dont 5 tagués "Politique"

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> je vois le nombre d'utilisateurs ayant masqué le contenu politique
<span style="color: #9E9E9E">**Et**</span> le taux d'engagement comparé aux autres tags

---

## 9. Recherche avec tag "Politique"

<span style="color: #2196F3">**Étant donné**</span> que je recherche du contenu

<span style="color: #FF9800">**Quand**</span> je filtre par tag "Politique"

<span style="color: #4CAF50">**Alors**</span> seuls les contenus tagués "Politique" sont affichés
<span style="color: #9E9E9E">**Et**</span> ce même si j'ai activé "Masquer contenu politique" (recherche explicite)

---

## 10. Partage de contenu politique avec filtre actif

<span style="color: #2196F3">**Étant donné**</span> que j'ai activé "Masquer contenu politique"
<span style="color: #9E9E9E">**Et**</span> qu'un ami me partage un lien vers un contenu tagué "Politique"

<span style="color: #FF9800">**Quand**</span> j'ouvre le lien

<span style="color: #4CAF50">**Alors**</span> je peux accéder au contenu (partage explicite)
<span style="color: #9E9E9E">**Et**</span> je vois un avertissement "Ce contenu est tagué Politique"

---

## 11. Pas de classification gauche/droite en MVP

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur

<span style="color: #FF9800">**Quand**</span> je publie un contenu tagué "Politique"

<span style="color: #4CAF50">**Alors**</span> aucune option de classification idéologique n'est proposée
<span style="color: #9E9E9E">**Et**</span> je ne peux pas indiquer "Gauche", "Droite", "Centre", etc.

---

## 12. Pas d'équilibrage imposé en MVP

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur écoute majoritairement du contenu politique de gauche

<span style="color: #FF9800">**Quand**</span> l'algorithme génère des recommandations

<span style="color: #4CAF50">**Alors**</span> aucun équilibrage droite/gauche n'est appliqué
<span style="color: #9E9E9E">**Et**</span> les recommandations suivent l'algorithme standard (intérêts, géo, engagement)

---

## 13. Notification post-MVP pour classification avancée

<span style="color: #2196F3">**Étant donné**</span> que RoadWave passe en phase post-MVP
<span style="color: #9E9E9E">**Et**</span> que la classification politique avancée est activée

<span style="color: #FF9800">**Quand**</span> je me connecte

<span style="color: #4CAF50">**Alors**</span> je reçois une notification m'informant des nouvelles options
<span style="color: #9E9E9E">**Et**</span> je peux configurer mes préférences d'équilibrage politique

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que l'application est ouverte (premier plan)
> <span style="color: #9E9E9E">**Et**</span> que le GPS est activé
> <span style="color: #9E9E9E">**Et**</span> que l'utilisateur est en mode voiture (vitesse ≥ 5 km/h)
## 1. Calcul ETA et notification 7 secondes avant le point GPS

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu géolocalisé existe à la Tour Eiffel (48.8584, 2.2945)
<span style="color: #9E9E9E">**Et**</span> que je me déplace à 50 km/h vers ce point
<span style="color: #9E9E9E">**Et**</span> que je suis à 98 mètres du point (ETA = 7 secondes)

<span style="color: #FF9800">**Quand**</span> le système calcule l'ETA

<span style="color: #4CAF50">**Alors**</span> une notification est déclenchée immédiatement
<span style="color: #9E9E9E">**Et**</span> le compteur "7" s'affiche avec l'icône 🏛️
<span style="color: #9E9E9E">**Et**</span> une notification sonore (bip court) est jouée

---

## 2. 📋 Plan: Calcul ETA à différentes vitesses

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu géolocalisé existe à un point GPS
<span style="color: #9E9E9E">**Et**</span> que je me déplace à <vitesse> km/h

<span style="color: #FF9800">**Quand**</span> je suis à <distance> mètres du point

<span style="color: #4CAF50">**Alors**</span> l'ETA calculé est <eta> secondes
<span style="color: #9E9E9E">**Et**</span> la notification est déclenchée : <notification>

**📊 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

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu géolocalisé existe à 30m de ma position
<span style="color: #9E9E9E">**Et**</span> que ma vitesse est 3 km/h (arrêté à un feu rouge)

<span style="color: #FF9800">**Quand**</span> le système détecte cette situation

<span style="color: #4CAF50">**Alors**</span> une notification est déclenchée immédiatement
<span style="color: #9E9E9E">**Et**</span> je n'ai pas besoin d'attendre le calcul ETA

---

## 4. Notification minimaliste sans texte (sécurité routière)

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée est déclenchée

<span style="color: #FF9800">**Quand**</span> la notification s'affiche

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu géolocalisé avec le tag <tag> est disponible

<span style="color: #FF9800">**Quand**</span> la notification s'affiche

<span style="color: #4CAF50">**Alors**</span> l'icône <icone> 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

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée s'affiche

<span style="color: #FF9800">**Quand**</span> le compteur démarre

<span style="color: #4CAF50">**Alors**</span> le compteur affiche "7"
<span style="color: #9E9E9E">**Et**</span> après 1 seconde, il affiche "6"
<span style="color: #9E9E9E">**Et**</span> après 2 secondes, il affiche "5"
<span style="color: #9E9E9E">**Et**</span> après 3 secondes, il affiche "4"
<span style="color: #9E9E9E">**Et**</span> après 4 secondes, il affiche "3"
<span style="color: #9E9E9E">**Et**</span> après 5 secondes, il affiche "2"
<span style="color: #9E9E9E">**Et**</span> après 6 secondes, il affiche "1"
<span style="color: #9E9E9E">**Et**</span> après 7 secondes, la notification disparaît

---

## 7. Notification sonore uniquement en mode CarPlay

<span style="color: #2196F3">**Étant donné**</span> que l'application est connectée à CarPlay
<span style="color: #9E9E9E">**Et**</span> qu'un contenu géolocalisé est détecté (ETA 7s)

<span style="color: #FF9800">**Quand**</span> la notification est déclenchée

<span style="color: #4CAF50">**Alors**</span> seule la notification sonore (bip) est jouée
<span style="color: #9E9E9E">**Et**</span> aucun overlay visuel n'est affiché (icône, compteur)
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut valider via le bouton "Suivant" au volant

---

## 8. Notification sonore uniquement en mode Android Auto

<span style="color: #2196F3">**Étant donné**</span> que l'application est connectée à Android Auto
<span style="color: #9E9E9E">**Et**</span> qu'un contenu géolocalisé est détecté (ETA 7s)

<span style="color: #FF9800">**Quand**</span> la notification est déclenchée

<span style="color: #4CAF50">**Alors**</span> seule la notification sonore (bip) est jouée
<span style="color: #9E9E9E">**Et**</span> aucun overlay visuel n'est affiché
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut valider via le bouton "Suivant" au volant

---

## 9. Notification complète (sonore + visuelle) en mode normal

<span style="color: #2196F3">**Étant donné**</span> que l'application n'est PAS connectée à CarPlay/Android Auto
<span style="color: #9E9E9E">**Et**</span> qu'un contenu géolocalisé est détecté

<span style="color: #FF9800">**Quand**</span> la notification est déclenchée

<span style="color: #4CAF50">**Alors**</span> la notification sonore (bip) est jouée
<span style="color: #9E9E9E">**Et**</span> l'overlay visuel s'affiche (icône + compteur 7→1)

---

## 10. Validation via bouton "Suivant" et décompte 5 secondes

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée est affichée (compteur à 5)
<span style="color: #9E9E9E">**Et**</span> que j'écoute un podcast

<span style="color: #FF9800">**Quand**</span> j'appuie sur le bouton "Suivant"

<span style="color: #4CAF50">**Alors**</span> le compteur bascule à "5" (décompte final)
<span style="color: #9E9E9E">**Et**</span> le contenu actuel continue de jouer normalement
<span style="color: #9E9E9E">**Et**</span> le compteur décrémente: 5→4→3→2→1
<span style="color: #9E9E9E">**Et**</span> après 5 secondes, le contenu géolocalisé démarre (fade in 0.3s)

---

## 11. Transition fluide avec fade out/in

<span style="color: #2196F3">**Étant donné**</span> que le décompte atteint "0"

<span style="color: #FF9800">**Quand**</span> le contenu géolocalisé doit démarrer

<span style="color: #4CAF50">**Alors**</span> le contenu actuel fait un fade out de 0.3s
<span style="color: #9E9E9E">**Et**</span> le contenu géolocalisé fait un fade in de 0.3s
<span style="color: #9E9E9E">**Et**</span> il n'y a pas de silence entre les deux

---

## 12. Contenu actuel se termine pendant le décompte

<span style="color: #2196F3">**Étant donné**</span> que j'ai validé la notification (décompte 5s démarre)
<span style="color: #9E9E9E">**Et**</span> que mon contenu actuel se termine après 2 secondes

<span style="color: #FF9800">**Quand**</span> le contenu actuel se termine

<span style="color: #4CAF50">**Alors**</span> le contenu suivant du buffer démarre immédiatement
<span style="color: #9E9E9E">**Et**</span> le décompte continue (3→2→1)
<span style="color: #9E9E9E">**Et**</span> à la fin du décompte, le contenu géolocalisé remplace le buffer

---

## 13. Ignorance de la notification (pas de clic pendant 7s)

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée s'affiche (compteur 7)

<span style="color: #FF9800">**Quand**</span> 7 secondes s'écoulent sans que j'appuie sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> la notification disparaît automatiquement
<span style="color: #9E9E9E">**Et**</span> le contenu géolocalisé est perdu (pas d'insertion dans la file)
<span style="color: #9E9E9E">**Et**</span> un cooldown de 10 minutes est activé

---

## 14. Quota de 6 contenus géolocalisés par heure

<span style="color: #2196F3">**Étant donné**</span> que j'ai validé 6 notifications géolocalisées dans la dernière heure

<span style="color: #FF9800">**Quand**</span> un 7ème contenu géolocalisé est détecté

<span style="color: #4CAF50">**Alors**</span> aucune notification n'est envoyée
<span style="color: #9E9E9E">**Et**</span> le contenu n'est pas inséré dans la file

---

## 15. Fenêtre glissante de 60 minutes

<span style="color: #2196F3">**Étant donné**</span> que j'ai validé 6 contenus géolocalisés
<span style="color: #9E9E9E">**Et**</span> que le premier contenu a été validé il y a 61 minutes

<span style="color: #FF9800">**Quand**</span> un nouveau contenu géolocalisé est détecté

<span style="color: #4CAF50">**Alors**</span> la notification est envoyée (quota libéré : 5/6)
<span style="color: #9E9E9E">**Et**</span> le compteur horaire est mis à jour

---

## 16. 📋 Plan: Gestion du quota horaire

<span style="color: #2196F3">**Étant donné**</span> que <nb_valides> notifications ont été validées dans la dernière heure

<span style="color: #FF9800">**Quand**</span> un nouveau contenu géolocalisé est détecté

<span style="color: #4CAF50">**Alors**</span> la notification est <action>

**📊 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)

<span style="color: #2196F3">**Étant donné**</span> que j'ai démarré un audio-guide avec 8 séquences
<span style="color: #9E9E9E">**Et**</span> que cet audio-guide compte comme 1 contenu dans le quota

<span style="color: #FF9800">**Quand**</span> toutes les séquences de l'audio-guide sont lues

<span style="color: #4CAF50">**Alors**</span> mon quota reste à 1/6
<span style="color: #9E9E9E">**Et**</span> je peux encore valider 5 contenus géolocalisés simples

---

## 18. Cooldown de 10 minutes après notification ignorée

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée a été ignorée (pas de clic)
<span style="color: #9E9E9E">**Et**</span> qu'un cooldown de 10 minutes est activé

<span style="color: #FF9800">**Quand**</span> 5 minutes s'écoulent
<span style="color: #9E9E9E">**Et**</span> qu'un nouveau contenu géolocalisé est détecté

<span style="color: #4CAF50">**Alors**</span> aucune notification n'est envoyée (cooldown actif)

---

## 19. Cooldown expire après 10 minutes

<span style="color: #2196F3">**Étant donné**</span> qu'un cooldown a été activé il y a 10 minutes

<span style="color: #FF9800">**Quand**</span> un nouveau contenu géolocalisé est détecté

<span style="color: #4CAF50">**Alors**</span> la notification est envoyée (cooldown expiré)

---

## 20. Pas de cooldown si notification validée

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée est affichée

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant" dans les 7 secondes

<span style="color: #4CAF50">**Alors**</span> aucun cooldown n'est activé
<span style="color: #9E9E9E">**Et**</span> la prochaine notification pourra être envoyée normalement

---

## 21. Contenu géolocalisé dans l'historique de navigation

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu du buffer
<span style="color: #9E9E9E">**Et**</span> que j'ai validé un contenu géolocalisé "Tour Eiffel"
<span style="color: #9E9E9E">**Et**</span> que j'ai écouté 42 secondes du contenu géolocalisé

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Suivant" (skip)
<span style="color: #9E9E9E">**Et**</span> que j'appuie ensuite sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> le contenu géolocalisé reprend à 42 secondes

---

## 22. Contenu ignoré n'entre pas dans l'historique

<span style="color: #2196F3">**Étant donné**</span> qu'une notification géolocalisée a été ignorée

<span style="color: #FF9800">**Quand**</span> j'appuie sur "Précédent"

<span style="color: #4CAF50">**Alors**</span> le contenu géolocalisé ignoré n'apparaît PAS dans l'historique
<span style="color: #9E9E9E">**Et**</span> je reviens au contenu d'avant

---

## 23. Skip pendant le décompte annule l'insertion

<span style="color: #2196F3">**Étant donné**</span> que j'ai validé une notification (décompte 5s en cours)
<span style="color: #9E9E9E">**Et**</span> que le compteur affiche "3"

<span style="color: #FF9800">**Quand**</span> j'appuie à nouveau sur "Suivant"

<span style="color: #4CAF50">**Alors**</span> le décompte est annulé
<span style="color: #9E9E9E">**Et**</span> le contenu suivant du buffer démarre
<span style="color: #9E9E9E">**Et**</span> le contenu géolocalisé n'entre PAS dans l'historique

---

## 24. Détection mode piéton (vitesse <5 km/h stable 10s)

<span style="color: #2196F3">**Étant donné**</span> que je suis en mode voiture
<span style="color: #9E9E9E">**Et**</span> que ma vitesse passe à 3 km/h

<span style="color: #FF9800">**Quand**</span> cette vitesse reste stable pendant 10 secondes

<span style="color: #4CAF50">**Alors**</span> le mode piéton est activé automatiquement
<span style="color: #9E9E9E">**Et**</span> les notifications passent en mode push arrière-plan (si permission accordée)

---

## 25. Détection mode voiture (vitesse ≥5 km/h stable 10s)

<span style="color: #2196F3">**Étant donné**</span> que je suis en mode piéton
<span style="color: #9E9E9E">**Et**</span> que ma vitesse passe à 15 km/h

<span style="color: #FF9800">**Quand**</span> cette vitesse reste stable pendant 10 secondes

<span style="color: #4CAF50">**Alors**</span> le mode voiture est activé automatiquement
<span style="color: #9E9E9E">**Et**</span> les notifications passent en mode sonore + icône (app premier plan requise)

---

## 26. Hysteresis pour éviter basculements intempestifs

<span style="color: #2196F3">**Étant donné**</span> que ma vitesse passe de 20 km/h à 3 km/h (arrêt feu rouge)
<span style="color: #9E9E9E">**Et**</span> que ma vitesse remonte à 20 km/h après 8 secondes

<span style="color: #FF9800">**Quand**</span> le système vérifie le mode

<span style="color: #4CAF50">**Alors**</span> aucun basculement n'a lieu (hysteresis de 10s non atteinte)
<span style="color: #9E9E9E">**Et**</span> je reste en mode voiture

---

## 27. 📋 Plan: Effets du basculement voiture → piéton

<span style="color: #2196F3">**Étant donné**</span> que je bascule de voiture à piéton

<span style="color: #FF9800">**Quand**</span> le basculement est effectué

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que je roule à 130 km/h (36.1 m/s)
<span style="color: #9E9E9E">**Et**</span> qu'un contenu géolocalisé est à 252 mètres

<span style="color: #FF9800">**Quand**</span> l'ETA de 7s est atteint
<span style="color: #9E9E9E">**Et**</span> que je valide la notification

<span style="color: #4CAF50">**Alors**</span> le décompte 5s démarre
<span style="color: #9E9E9E">**Et**</span> le contenu géolocalisé démarre encore avant le point GPS (72m avant)

---

## 29. Multiples points géolocalisés proches (route touristique)

<span style="color: #2196F3">**Étant donné**</span> que 3 châteaux sont espacés de 800m chacun
<span style="color: #9E9E9E">**Et**</span> que je valide la notification du Château A

<span style="color: #FF9800">**Quand**</span> j'arrive près du Château B (57s plus tard à 50 km/h)

<span style="color: #4CAF50">**Alors**</span> 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)

<span style="color: #2196F3">**Étant donné**</span> que je me gare à 30m d'un château
<span style="color: #9E9E9E">**Et**</span> que ma vitesse est <1 km/h pendant 2 minutes

<span style="color: #FF9800">**Quand**</span> le mode stationnement est détecté

<span style="color: #4CAF50">**Alors**</span> aucune notification de contenu géolocalisé n'est envoyée
<span style="color: #9E9E9E">**Et**</span> le système bascule automatiquement en mode piéton

---

## 31. Reprise conduite après stationnement

<span style="color: #2196F3">**Étant donné**</span> que je suis en mode stationnement
<span style="color: #9E9E9E">**Et**</span> que ma vitesse passe à 20 km/h pendant 10 secondes

<span style="color: #FF9800">**Quand**</span> le système détecte la reprise de conduite

<span style="color: #4CAF50">**Alors**</span> le mode voiture est réactivé
<span style="color: #9E9E9E">**Et**</span> les notifications géolocalisées reprennent (si quota non atteint)

---

## 32. Contenu géolocalisé simple (1 séquence unique)

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu géolocalisé simple existe à un point GPS

<span style="color: #FF9800">**Quand**</span> la notification est déclenchée (ETA 7s)
<span style="color: #9E9E9E">**Et**</span> que je valide

<span style="color: #4CAF50">**Alors**</span> le contenu démarre après décompte 5s
<span style="color: #9E9E9E">**Et**</span> à la fin du contenu, le buffer normal reprend
<span style="color: #9E9E9E">**Et**</span> ce contenu compte 1/6 dans le quota

---

## 33. Audio-guide multi-séquences (2+ séquences enchaînées)

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide avec 8 séquences existe

<span style="color: #FF9800">**Quand**</span> je démarre l'audio-guide
<span style="color: #9E9E9E">**Et**</span> que les séquences s'enchaînent automatiquement (GPS ou manuel)

<span style="color: #4CAF50">**Alors**</span> l'audio-guide entier compte 1/6 dans le quota
<span style="color: #9E9E9E">**Et**</span> les séquences ne déclenchent PAS de notification avec compteur 7s
<span style="color: #9E9E9E">**Et**</span> elles se déclenchent au point GPS exact (rayon 30m)

---

## 34. GPS désactivé en mode voiture

<span style="color: #2196F3">**Étant donné**</span> que je suis en mode voiture

<span style="color: #FF9800">**Quand**</span> le GPS est désactivé

<span style="color: #4CAF50">**Alors**</span> aucune notification géolocalisée ne peut être envoyée
<span style="color: #9E9E9E">**Et**</span> un message d'erreur s'affiche: "GPS requis pour les contenus géolocalisés"

---

## 35. App en arrière-plan en mode voiture

<span style="color: #2196F3">**Étant donné**</span> que je suis en mode voiture
<span style="color: #9E9E9E">**Et**</span> que l'app passe en arrière-plan

<span style="color: #FF9800">**Quand**</span> un contenu géolocalisé est détecté

<span style="color: #4CAF50">**Alors**</span> aucune notification n'est envoyée (app premier plan requise)
<span style="color: #9E9E9E">**Et**</span> le contenu n'est pas perdu (sera proposé si app rouverte dans le rayon)

---

## 36. Permission "Always Location" refusée (mode piéton indisponible)

<span style="color: #2196F3">**Étant donné**</span> que je refuse la permission "Always Location"

<span style="color: #FF9800">**Quand**</span> ma vitesse passe <5 km/h

<span style="color: #4CAF50">**Alors**</span> le mode piéton n'est PAS activé
<span style="color: #9E9E9E">**Et**</span> le mode voiture reste actif (avec permission "When In Use")
<span style="color: #9E9E9E">**Et**</span> aucune notification arrière-plan n'est envoyée

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Contenu écouté complètement (>80%) - jamais reproposé

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté un contenu à 85%

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> ce contenu n'est jamais reproposé
<span style="color: #9E9E9E">**Et**</span> il est marqué comme "écouté" dans l'historique

---

## 2. Contenu écouté à 80% exactement - jamais reproposé

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté un contenu exactement à 80%

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> ce contenu n'est pas reproposé (seuil >= 80%)

---

## 3. Contenu skippé rapidement (<10s) - ne pas reproposer

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a skippé un contenu après 8 secondes

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> ce contenu n'est pas reproposé (signal négatif fort)
<span style="color: #9E9E9E">**Et**</span> la jauge d'intérêt correspondante est réduite de 0.5%

---

## 4. Contenu skippé exactement à 10s - ne pas reproposer

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a skippé un contenu après exactement 10 secondes

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> ce contenu n'est pas reproposé (seuil < 10s strict)

---

## 5. Contenu partiellement écouté (10-80%) - reproposer avec reprise

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté un contenu à 45%
<span style="color: #9E9E9E">**Et**</span> qu'il est arrivé à la position 2:30 (150 secondes)

<span style="color: #FF9800">**Quand**</span> l'algorithme propose à nouveau ce contenu

<span style="color: #4CAF50">**Alors**</span> le contenu peut être reproposé
<span style="color: #9E9E9E">**Et**</span> la position de reprise est 150 secondes
<span style="color: #9E9E9E">**Et**</span> l'utilisateur voit "Reprendre à 2:30"

---

## 6. Contenu écouté à 11% - reproposition possible

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté un contenu à 11%

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> ce contenu peut être reproposé (>10%)
<span style="color: #9E9E9E">**Et**</span> la position de reprise est sauvegardée

---

## 7. Contenu écouté à 79% - reproposition possible

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté un contenu à 79%

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> ce contenu peut être reproposé (<80%)
<span style="color: #9E9E9E">**Et**</span> l'utilisateur peut terminer l'écoute

---

## 8. Audio-guide avec flag replayable=true

<span style="color: #2196F3">**Étant donné**</span> qu'un audio-guide a le flag "replayable = true"
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur l'a écouté à 95%

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> l'audio-guide peut être reproposé
<span style="color: #9E9E9E">**Et**</span> il est marqué comme "Écouté - Rejouable"

---

## 9. Podcast standard sans flag replayable

<span style="color: #2196F3">**Étant donné**</span> qu'un podcast n'a pas de flag replayable
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur l'a écouté à 90%

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> le podcast n'est jamais reproposé

---

## 10. Stockage dans user_content_history

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur écoute un contenu

<span style="color: #FF9800">**Quand**</span> l'écoute se termine ou est skippée

<span style="color: #4CAF50">**Alors**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté 5000 contenus

<span style="color: #FF9800">**Quand**</span> il consulte son historique

<span style="color: #4CAF50">**Alors**</span> tous les 5000 contenus sont disponibles
<span style="color: #9E9E9E">**Et**</span> aucun contenu n'est supprimé automatiquement

---

## 12. Algorithme considère les 100 derniers pour performance

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté 500 contenus

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> il vérifie uniquement les 100 derniers contenus pour exclusion
<span style="color: #9E9E9E">**Et**</span> cette limite est une optimisation de requête SQL

---

## 13. Export historique complet (RGPD)

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur demande l'export RGPD

<span style="color: #FF9800">**Quand**</span> l'export est généré

<span style="color: #4CAF50">**Alors**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté un podcast à 60% (position 10:00)

<span style="color: #FF9800">**Quand**</span> ce podcast est reproposé par l'algorithme
<span style="color: #9E9E9E">**Et**</span> que je lance la lecture

<span style="color: #4CAF50">**Alors**</span> l'écoute reprend automatiquement à 10:00
<span style="color: #9E9E9E">**Et**</span> je vois une notification "Reprise à 10:00"

---

## 15. Option "Reprendre du début" pour contenu partiellement écouté

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté un podcast à 60%

<span style="color: #FF9800">**Quand**</span> ce podcast est reproposé

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté un contenu il y a 6 mois à 90%

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> ce contenu n'est toujours pas reproposé
<span style="color: #9E9E9E">**Et**</span> l'historique n'a pas de limite temporelle

---

## 17. Nouveau contenu du même créateur après écoute complète

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté un contenu de "Créateur A" à 90%
<span style="color: #9E9E9E">**Et**</span> que "Créateur A" publie un nouveau contenu

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> le nouveau contenu peut être recommandé
<span style="color: #9E9E9E">**Et**</span> seul l'ancien contenu est exclu (pas tout le créateur)

---

## 18. Statistiques personnelles d'historique

<span style="color: #2196F3">**Étant donné**</span> que je consulte mon profil

<span style="color: #FF9800">**Quand**</span> j'accède à la section "Historique"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je consulte mon historique

<span style="color: #FF9800">**Quand**</span> je filtre par "Dernière semaine"

<span style="color: #4CAF50">**Alors**</span> seuls les contenus écoutés dans les 7 derniers jours sont affichés
<span style="color: #9E9E9E">**Et**</span> je peux exporter cette sélection

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Création d'un compte média vérifié

<span style="color: #2196F3">**Étant donné**</span> que je représente Le Monde

<span style="color: #FF9800">**Quand**</span> je crée un compte média
<span style="color: #9E9E9E">**Et**</span> que je fournis les justificatifs (SIRET, documents officiels)

<span style="color: #4CAF50">**Alors**</span> mon compte est créé en attente de vérification
<span style="color: #9E9E9E">**Et**</span> l'équipe RoadWave examine ma demande sous 48-72h

---

## 2. Validation compte média par l'équipe RoadWave

<span style="color: #2196F3">**Étant donné**</span> qu'un compte média "Le Parisien" est en attente

<span style="color: #FF9800">**Quand**</span> l'équipe RoadWave valide le compte

<span style="color: #4CAF50">**Alors**</span> le compte reçoit le badge vérifié ✓
<span style="color: #9E9E9E">**Et**</span> le média peut publier sans validation des 3 premiers contenus
<span style="color: #9E9E9E">**Et**</span> je vois le message "Compte média vérifié avec succès"

---

## 3. Badge vérifié visible sur profil média

<span style="color: #2196F3">**Étant donné**</span> que "France Inter" a un compte vérifié

<span style="color: #FF9800">**Quand**</span> un utilisateur consulte le profil

<span style="color: #4CAF50">**Alors**</span> il voit le badge ✓ à côté du nom
<span style="color: #9E9E9E">**Et**</span> une mention "Média vérifié"

---

## 4. Pas de validation des 3 premiers contenus pour médias

<span style="color: #2196F3">**Étant donné**</span> que je suis un média vérifié

<span style="color: #FF9800">**Quand**</span> je publie mon premier contenu

<span style="color: #4CAF50">**Alors**</span> le contenu est publié immédiatement sans validation
<span style="color: #9E9E9E">**Et**</span> il est visible pour tous les utilisateurs
<span style="color: #9E9E9E">**Et**</span> je ne passe pas par la modération initiale

---

## 5. Modération a posteriori uniquement

<span style="color: #2196F3">**Étant donné**</span> que "Libération" publie un contenu

<span style="color: #FF9800">**Quand**</span> le contenu est publié

<span style="color: #4CAF50">**Alors**</span> il est immédiatement disponible
<span style="color: #F44336">**Mais**</span> peut être signalé et modéré a posteriori
<span style="color: #9E9E9E">**Et**</span> suit les mêmes règles de modération que les créateurs

---

## 6. Publication flash info géolocalisé

<span style="color: #2196F3">**Étant donné**</span> que je suis "Ouest-France" (média régional)

<span style="color: #FF9800">**Quand**</span> je publie un flash info sur un événement à Rennes
<span style="color: #9E9E9E">**Et**</span> que je le géolocalise en Bretagne (géo-contextuel)

<span style="color: #4CAF50">**Alors**</span> le contenu est publié immédiatement
<span style="color: #9E9E9E">**Et**</span> il est recommandé aux utilisateurs en Bretagne

---

## 7. Publication chronique thématique

<span style="color: #2196F3">**Étant donné**</span> que je suis "France Culture"

<span style="color: #FF9800">**Quand**</span> je publie une chronique philosophie (géo-neutre)

<span style="color: #4CAF50">**Alors**</span> le contenu est disponible partout en France
<span style="color: #9E9E9E">**Et**</span> suit l'algorithme de recommandation standard

---

## 8. Publication édito politique

<span style="color: #2196F3">**Étant donné**</span> que je suis "Le Figaro"

<span style="color: #FF9800">**Quand**</span> je publie un édito politique
<span style="color: #9E9E9E">**Et**</span> que je le tague "Politique"

<span style="color: #4CAF50">**Alors**</span> le contenu est publié immédiatement
<span style="color: #9E9E9E">**Et**</span> la classification politique MVP s'applique (pas gauche/droite)
<span style="color: #9E9E9E">**Et**</span> les utilisateurs ayant activé "Masquer politique" ne le voient pas

---

## 9. Formats de contenu autorisés pour médias

<span style="color: #2196F3">**Étant donné**</span> que je suis un média vérifié

<span style="color: #FF9800">**Quand**</span> je publie du contenu

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis "RTL"

<span style="color: #FF9800">**Quand**</span> je publie un contenu sensible

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis un média vérifié
<span style="color: #9E9E9E">**Et**</span> que mes contenus génèrent des écoutes

<span style="color: #FF9800">**Quand**</span> le mois se termine

<span style="color: #4CAF50">**Alors**</span> je reçois 3€ / 1000 écoutes complètes (même taux que créateurs)
<span style="color: #9E9E9E">**Et**</span> le paiement suit les mêmes règles (seuil 50€, mensuel)

---

## 12. Sponsoring direct non géré par plateforme

<span style="color: #2196F3">**Étant donné**</span> que je suis "Europe 1"
<span style="color: #9E9E9E">**Et**</span> que je veux intégrer un sponsor dans mon contenu

<span style="color: #FF9800">**Quand**</span> je mentionne le sponsor dans l'audio

<span style="color: #4CAF50">**Alors**</span> c'est autorisé (sponsoring éditorial)
<span style="color: #F44336">**Mais**</span> RoadWave ne gère pas la transaction
<span style="color: #9E9E9E">**Et**</span> je gère la relation sponsor directement

---

## 13. Médias peuvent avoir plusieurs comptes créateurs

<span style="color: #2196F3">**Étant donné**</span> que je suis "Le Monde"

<span style="color: #FF9800">**Quand**</span> je veux créer des sous-comptes par rubrique

<span style="color: #4CAF50">**Alors**</span> je peux créer:

    | compte | description |
    |---|---|
    | @lemonde_politique | Actualité politique |
    | @lemonde_economie | Économie et entreprises |
    | @lemonde_culture | Culture et spectacles |

<span style="color: #9E9E9E">**Et**</span> tous sont liés au compte média principal

---

## 14. Médias régionaux privilégiés localement

<span style="color: #2196F3">**Étant donné**</span> que "Sud-Ouest" publie du contenu géo-contextuel en Nouvelle-Aquitaine
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est à Bordeaux

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule les recommandations

<span style="color: #4CAF50">**Alors**</span> le contenu de "Sud-Ouest" a un score géo élevé
<span style="color: #9E9E9E">**Et**</span> il est privilégié pour l'audience locale

---

## 15. Médias nationaux accessibles partout

<span style="color: #2196F3">**Étant donné**</span> que "France Inter" publie un podcast géo-neutre

<span style="color: #FF9800">**Quand**</span> des utilisateurs à Paris, Lyon, Marseille demandent des recommandations

<span style="color: #4CAF50">**Alors**</span> le podcast est accessible partout sans distinction géographique
<span style="color: #9E9E9E">**Et**</span> suit l'algorithme de recommandation standard

---

## 16. Statistiques détaillées pour médias

<span style="color: #2196F3">**Étant donné**</span> que je suis un média vérifié

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je suis "Libération"

<span style="color: #FF9800">**Quand**</span> je clique sur "Exporter analytics"

<span style="color: #4CAF50">**Alors**</span> je reçois un CSV avec données détaillées
<span style="color: #9E9E9E">**Et**</span> je peux analyser les données avec mes outils internes

---

## 18. Contact prioritaire équipe RoadWave

<span style="color: #2196F3">**Étant donné**</span> que je suis un média vérifié

<span style="color: #FF9800">**Quand**</span> j'ai un problème technique ou question

<span style="color: #4CAF50">**Alors**</span> je peux contacter le support média prioritaire
<span style="color: #9E9E9E">**Et**</span> j'obtiens une réponse sous 24h (vs 48-72h standard)

---

## 19. Médias peuvent programmer la publication

<span style="color: #2196F3">**Étant donné**</span> que je suis "France Culture"

<span style="color: #FF9800">**Quand**</span> je prépare un contenu à l'avance

<span style="color: #4CAF50">**Alors**</span> je peux programmer la publication pour une date/heure future
<span style="color: #9E9E9E">**Et**</span> le contenu sera publié automatiquement au moment choisi

---

## 20. API dédiée pour médias (post-MVP)

<span style="color: #2196F3">**Étant donné**</span> que je suis un grand média avec beaucoup de contenus

<span style="color: #FF9800">**Quand**</span> RoadWave développe l'API médias

<span style="color: #4CAF50">**Alors**</span> je peux automatiser la publication via API
<span style="color: #9E9E9E">**Et**</span> intégrer RoadWave dans mon workflow de production

---

## 21. Signalement d'un contenu média traité en priorité

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu de "Le Monde" est signalé

<span style="color: #FF9800">**Quand**</span> le signalement arrive en modération

<span style="color: #4CAF50">**Alors**</span> il est traité avec la même priorité qu'un créateur standard
<span style="color: #9E9E9E">**Et**</span> le badge vérifié ne donne pas d'immunité modération

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Activation manuelle du mode Kids

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de 14 ans
<span style="color: #9E9E9E">**Et**</span> que le mode Kids n'est pas activé par défaut

<span style="color: #FF9800">**Quand**</span> j'active le mode Kids dans les paramètres

<span style="color: #4CAF50">**Alors**</span> le mode Kids est activé sur mon compte
<span style="color: #9E9E9E">**Et**</span> je vois le message "Mode Kids activé - Contenus filtrés pour 13-15 ans"

---

## 2. Parent active le mode Kids pour son enfant

<span style="color: #2196F3">**Étant donné**</span> que je suis le parent d'un utilisateur de 13 ans
<span style="color: #9E9E9E">**Et**</span> que j'ai accès au compte de mon enfant

<span style="color: #FF9800">**Quand**</span> j'active le mode Kids

<span style="color: #4CAF50">**Alors**</span> le mode Kids est activé sur le compte enfant
<span style="color: #9E9E9E">**Et**</span> seuls les contenus "Tous publics" sont accessibles

---

## 3. Filtrage contenu - uniquement "Tous publics"

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé sur mon compte
<span style="color: #9E9E9E">**Et**</span> qu'il existe des contenus avec les classifications:

    | classification | nombre |
    |---|---|
    | Tous publics | 100 |
    | 13+ | 50 |
    | 16+ | 30 |
    | 18+ | 20 |


<span style="color: #FF9800">**Quand**</span> je demande des recommandations

<span style="color: #4CAF50">**Alors**</span> seuls les 100 contenus "Tous publics" sont proposés
<span style="color: #9E9E9E">**Et**</span> les contenus 13+, 16+, 18+ sont exclus

---

## 4. Exclusion automatique du contenu politique

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé
<span style="color: #9E9E9E">**Et**</span> qu'il existe 20 contenus "Tous publics" dont 5 tagués "Politique"

<span style="color: #FF9800">**Quand**</span> je demande des recommandations

<span style="color: #4CAF50">**Alors**</span> seuls les 15 contenus non-politiques sont proposés
<span style="color: #9E9E9E">**Et**</span> les 5 contenus politiques sont automatiquement exclus

---

## 5. Pas de publicité en mode Kids

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé
<span style="color: #9E9E9E">**Et**</span> que je suis un utilisateur gratuit

<span style="color: #FF9800">**Quand**</span> j'écoute du contenu

<span style="color: #4CAF50">**Alors**</span> aucune publicité n'est diffusée
<span style="color: #9E9E9E">**Et**</span> je n'ai pas d'insertion publicitaire (règle 1/5 désactivée)

---

## 6. Publicité validée manuellement en mode Kids (post-MVP)

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé
<span style="color: #9E9E9E">**Et**</span> qu'une publicité a été validée manuellement pour le mode Kids

<span style="color: #FF9800">**Quand**</span> j'écoute du contenu

<span style="color: #4CAF50">**Alors**</span> cette publicité peut être diffusée
<span style="color: #F44336">**Mais**</span> la fréquence reste inférieure au mode standard

---

## 7. Interface standard même en mode Kids

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé

<span style="color: #FF9800">**Quand**</span> j'ouvre l'application

<span style="color: #4CAF50">**Alors**</span> l'interface est identique au mode normal
<span style="color: #9E9E9E">**Et**</span> seul le filtrage de contenu est actif (pas d'UI enfant)

---

## 8. Désactivation du mode Kids

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé

<span style="color: #FF9800">**Quand**</span> je désactive le mode Kids dans les paramètres

<span style="color: #4CAF50">**Alors**</span> tous les contenus sont à nouveau accessibles selon mon âge
<span style="color: #9E9E9E">**Et**</span> je vois le message "Mode Kids désactivé"

---

## 9. Utilisateur 16 ans ne peut pas activer le mode Kids 13-15 ans

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de 16 ans

<span style="color: #FF9800">**Quand**</span> j'essaie d'activer le mode Kids

<span style="color: #4CAF50">**Alors**</span> l'activation réussit
<span style="color: #9E9E9E">**Et**</span> le mode Kids filtre les contenus 16+ et 18+ (pas seulement 13+)
<span style="color: #9E9E9E">**Et**</span> je vois uniquement les contenus "Tous publics"

---

## 10. Tentative d'accès direct à contenu 16+ en mode Kids

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé
<span style="color: #9E9E9E">**Et**</span> qu'un ami me partage un contenu 16+

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder au contenu via le lien

<span style="color: #4CAF50">**Alors**</span> l'accès est refusé
<span style="color: #9E9E9E">**Et**</span> je vois le message "Ce contenu n'est pas accessible en mode Kids"

---

## 11. Recherche en mode Kids filtre automatiquement

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé

<span style="color: #FF9800">**Quand**</span> je recherche "débat"

<span style="color: #4CAF50">**Alors**</span> seuls les contenus "Tous publics" apparaissent dans les résultats
<span style="color: #9E9E9E">**Et**</span> les contenus 13+, 16+, 18+ sont exclus de la recherche

---

## 12. Audio-guide en mode Kids

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé
<span style="color: #9E9E9E">**Et**</span> qu'un audio-guide "Tous publics" existe au musée du Louvre

<span style="color: #FF9800">**Quand**</span> je suis à proximité du Louvre

<span style="color: #4CAF50">**Alors**</span> l'audio-guide est proposé normalement
<span style="color: #9E9E9E">**Et**</span> toutes les séquences sont accessibles

---

## 13. Statistiques créateur - audience mode Kids

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur
<span style="color: #9E9E9E">**Et**</span> que mes contenus "Tous publics" sont écoutés par des utilisateurs mode Kids

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> je vois le pourcentage d'écoutes en mode Kids
<span style="color: #9E9E9E">**Et**</span> je peux adapter mes contenus en conséquence

---

## 14. Notification lors de l'activation du mode Kids

<span style="color: #FF9800">**Quand**</span> j'active le mode Kids

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le mode Kids est activé

<span style="color: #FF9800">**Quand**</span> je consulte mon profil

<span style="color: #4CAF50">**Alors**</span> je vois un badge "Mode Kids actif 🛡️"
<span style="color: #9E9E9E">**Et**</span> je peux le désactiver en un clic

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté en tant qu'admin
## 1. Accès au dashboard admin

<span style="color: #FF9800">**Quand**</span> j'accède au dashboard admin

<span style="color: #4CAF50">**Alors**</span> je vois tous les paramètres configurables de l'algorithme
<span style="color: #9E9E9E">**Et**</span> je vois les valeurs actuelles et par défaut

---

## 2. Modifier le poids géo pour contenu ancré

<span style="color: #2196F3">**Étant donné**</span> que le poids_geo_ancre est à 0.7 (défaut)

<span style="color: #FF9800">**Quand**</span> je modifie le poids_geo_ancre à 0.8
<span style="color: #9E9E9E">**Et**</span> que je sauvegarde

<span style="color: #4CAF50">**Alors**</span> la nouvelle valeur est appliquée immédiatement
<span style="color: #9E9E9E">**Et**</span> tous les nouveaux calculs utilisent 0.8
<span style="color: #9E9E9E">**Et**</span> je vois le message "Paramètre mis à jour avec succès"

---

## 3. Validation des plages de valeurs

<span style="color: #FF9800">**Quand**</span> j'essaie de configurer poids_geo_ancre à 1.5 (hors plage 0.5-1.0)

<span style="color: #4CAF50">**Alors**</span> la modification échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Valeur hors plage autorisée (0.5 - 1.0)"

---

## 4. 📋 Plan: Modification de tous les paramètres configurables

<span style="color: #FF9800">**Quand**</span> je modifie "<parametre>" à "<nouvelle_valeur>"

<span style="color: #4CAF50">**Alors**</span> la modification est appliquée immédiatement
<span style="color: #9E9E9E">**Et**</span> la nouvelle valeur respecte la plage "<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

<span style="color: #2196F3">**Étant donné**</span> que le poids_geo_ancre est modifié de 0.7 à 0.8

<span style="color: #FF9800">**Quand**</span> la modification est appliquée

<span style="color: #4CAF50">**Alors**</span> aucun recalcul batch n'est lancé (économie CPU)
<span style="color: #9E9E9E">**Et**</span> seuls les nouveaux calculs utilisent la valeur 0.8

---

## 6. Versioning des configurations

<span style="color: #2196F3">**Étant donné**</span> que je modifie plusieurs paramètres

<span style="color: #FF9800">**Quand**</span> je sauvegarde la configuration

<span style="color: #4CAF50">**Alors**</span> une nouvelle version est créée (ex: v1.2.3)
<span style="color: #9E9E9E">**Et**</span> je peux voir l'historique des versions
<span style="color: #9E9E9E">**Et**</span> je peux comparer deux versions

---

## 7. Rollback en 1 clic

<span style="color: #2196F3">**Étant donné**</span> que la configuration actuelle est v1.2.3
<span style="color: #9E9E9E">**Et**</span> que la version précédente était v1.2.2

<span style="color: #FF9800">**Quand**</span> je clique sur "Restaurer v1.2.2"

<span style="color: #4CAF50">**Alors**</span> tous les paramètres de v1.2.2 sont réappliqués
<span style="color: #9E9E9E">**Et**</span> je vois le message "Configuration restaurée à v1.2.2"

---

## 8. Créer une variante A/B testing

<span style="color: #FF9800">**Quand**</span> je crée une nouvelle variante "Test engagement élevé"
<span style="color: #9E9E9E">**Et**</span> que je configure:

    | parametre | valeur |
    |---|---|
    | poids_engagement | 0.4 |
    | poids_geo_ancre | 0.6 |

<span style="color: #9E9E9E">**Et**</span> que je lance le test A/B

<span style="color: #4CAF50">**Alors**</span> 50% des utilisateurs reçoivent la Config A (défaut)
<span style="color: #9E9E9E">**Et**</span> 50% des utilisateurs reçoivent la Config B (test)

---

## 9. Split utilisateurs aléatoire pour A/B test

<span style="color: #2196F3">**Étant donné**</span> qu'un test A/B est actif

<span style="color: #FF9800">**Quand**</span> 1000 nouveaux utilisateurs se connectent

<span style="color: #4CAF50">**Alors**</span> environ 500 sont assignés à la Config A
<span style="color: #9E9E9E">**Et**</span> environ 500 sont assignés à la Config B
<span style="color: #9E9E9E">**Et**</span> l'assignation est aléatoire et équilibrée

---

## 10. Utilisateur reste dans la même variante

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur est assigné à la Config B

<span style="color: #FF9800">**Quand**</span> il se reconnecte plusieurs fois

<span style="color: #4CAF50">**Alors**</span> il reste toujours dans la Config B
<span style="color: #9E9E9E">**Et**</span> il ne change pas de variante pendant le test

---

## 11. Métriques comparatives A/B testing

<span style="color: #2196F3">**Étant donné**</span> qu'un test A/B est actif depuis 7 jours

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard A/B testing

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un test A/B est actif

<span style="color: #FF9800">**Quand**</span> je consulte le dashboard

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un test A/B montre que Config B est meilleure

<span style="color: #FF9800">**Quand**</span> je clique sur "Appliquer Config B pour tous"

<span style="color: #4CAF50">**Alors**</span> la Config B devient la configuration par défaut
<span style="color: #9E9E9E">**Et**</span> tous les utilisateurs utilisent maintenant Config B
<span style="color: #9E9E9E">**Et**</span> l'ancien test est archivé

---

## 14. Audit engagement global

<span style="color: #FF9800">**Quand**</span> je consulte la section "Audit engagement"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que plusieurs modifications de config ont été faites

<span style="color: #FF9800">**Quand**</span> je consulte les graphiques d'évolution

<span style="color: #4CAF50">**Alors**</span> je vois l'impact de chaque changement de config
<span style="color: #9E9E9E">**Et**</span> je peux corréler changements config avec métriques

---

## 16. Export CSV pour analyse externe

<span style="color: #FF9800">**Quand**</span> je clique sur "Exporter données"

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le taux de complétion moyen est à 70%

<span style="color: #FF9800">**Quand**</span> une nouvelle config fait baisser le taux à 55%

<span style="color: #4CAF50">**Alors**</span> je reçois une alerte email "Baisse critique du taux de complétion"
<span style="color: #9E9E9E">**Et**</span> je peux rollback rapidement

---

## 18. Prévisualisation impact avant application

<span style="color: #2196F3">**Étant donné**</span> que je modifie poids_geo_ancre de 0.7 à 0.9

<span style="color: #FF9800">**Quand**</span> je clique sur "Prévisualiser impact"

<span style="color: #4CAF50">**Alors**</span> je vois une simulation sur échantillon de 1000 utilisateurs
<span style="color: #9E9E9E">**Et**</span> je vois l'estimation d'impact sur les métriques clés

---

## 19. Notes et commentaires sur modifications config

<span style="color: #FF9800">**Quand**</span> je modifie une configuration

<span style="color: #4CAF50">**Alors**</span> je peux ajouter une note "Test pour améliorer contenu local"
<span style="color: #9E9E9E">**Et**</span> cette note est visible dans l'historique des versions
<span style="color: #9E9E9E">**Et**</span> l'équipe peut comprendre le contexte des changements

---

## 20. Permissions admin pour modification config

<span style="color: #2196F3">**Étant donné**</span> que je suis un admin junior

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier un paramètre critique

<span style="color: #4CAF50">**Alors**</span> l'accès est refusé
<span style="color: #9E9E9E">**Et**</span> je vois "Permission admin senior requise"
<span style="color: #9E9E9E">**Et**</span> seuls les admins seniors peuvent modifier les paramètres

---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
> <span style="color: #9E9E9E">**Et**</span> que je suis connecté
## 1. Accès aux paramètres de personnalisation

<span style="color: #FF9800">**Quand**</span> j'ouvre les paramètres de personnalisation

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le curseur Géolocalisation est au centre (défaut)

<span style="color: #FF9800">**Quand**</span> je déplace le curseur vers "Local" (gauche)

<span style="color: #4CAF50">**Alors**</span> l'algorithme privilégie fortement les contenus proches
<span style="color: #9E9E9E">**Et**</span> la pondération géographique augmente
<span style="color: #9E9E9E">**Et**</span> je vois le message "Recommandations locales privilégiées"

---

## 3. Modifier le curseur Géolocalisation vers National

<span style="color: #2196F3">**Étant donné**</span> que le curseur Géolocalisation est au centre

<span style="color: #FF9800">**Quand**</span> je déplace le curseur vers "National" (droite)

<span style="color: #4CAF50">**Alors**</span> l'algorithme privilégie la découverte nationale
<span style="color: #9E9E9E">**Et**</span> la pondération géographique diminue
<span style="color: #9E9E9E">**Et**</span> je reçois des contenus de toute la France

---

## 4. Curseur Découverte à 0% - aucun aléatoire

<span style="color: #FF9800">**Quand**</span> je règle le curseur Découverte à 0%

<span style="color: #4CAF50">**Alors**</span> 0% de contenus aléatoires dans mes recommandations
<span style="color: #9E9E9E">**Et**</span> 100% de contenus calculés selon score combiné
<span style="color: #9E9E9E">**Et**</span> je vois le message "Personnalisation maximale"

---

## 5. Curseur Découverte à 10% - défaut équilibré

<span style="color: #FF9800">**Quand**</span> je règle le curseur Découverte à 10%

<span style="color: #4CAF50">**Alors**</span> 10% de contenus aléatoires
<span style="color: #9E9E9E">**Et**</span> 90% de contenus calculés
<span style="color: #9E9E9E">**Et**</span> je vois le message "Équilibre découverte/personnalisation"

---

## 6. Curseur Découverte à 30% - découverte élevée

<span style="color: #FF9800">**Quand**</span> je règle le curseur Découverte à 30%

<span style="color: #4CAF50">**Alors**</span> 30% de contenus aléatoires
<span style="color: #9E9E9E">**Et**</span> 70% de contenus calculés
<span style="color: #9E9E9E">**Et**</span> je vois le message "Découverte élevée activée"

---

## 7. Curseur Découverte à 50% - découverte maximale

<span style="color: #FF9800">**Quand**</span> je règle le curseur Découverte à 50%

<span style="color: #4CAF50">**Alors**</span> 50% de contenus aléatoires
<span style="color: #9E9E9E">**Et**</span> 50% de contenus calculés
<span style="color: #9E9E9E">**Et**</span> je vois le message "Découverte maximale (équivaut à national)"

---

## 8. Créer un profil personnalisé "Trajet quotidien"

<span style="color: #FF9800">**Quand**</span> je crée un nouveau profil nommé "🚗 Trajet quotidien"
<span style="color: #9E9E9E">**Et**</span> que je configure:

    | parametre | valeur |
    |---|---|
    | Géolocalisation | Local |
    | Découverte | 5% |
    | Politique | Masquer |

<span style="color: #9E9E9E">**Et**</span> que je sauvegarde

<span style="color: #4CAF50">**Alors**</span> le profil "🚗 Trajet quotidien" est créé
<span style="color: #9E9E9E">**Et**</span> je peux l'activer en un clic

---

## 9. Créer un profil "Road trip"

<span style="color: #FF9800">**Quand**</span> je crée un profil "🛣️ Road trip"
<span style="color: #9E9E9E">**Et**</span> que je configure:

    | parametre | valeur |
    |---|---|
    | Géolocalisation | Régional |
    | Découverte | 30% |
    | Politique | Équilibré |


<span style="color: #4CAF50">**Alors**</span> le profil est sauvegardé
<span style="color: #9E9E9E">**Et**</span> je peux switcher entre profils facilement

---

## 10. Créer un profil "Enfants"

<span style="color: #FF9800">**Quand**</span> je crée un profil "👶 Enfants"
<span style="color: #9E9E9E">**Et**</span> que j'active le Mode Kids

<span style="color: #4CAF50">**Alors**</span> tous les paramètres sont adaptés pour enfants:

    | parametre | valeur |
    |---|---|
    | Mode Kids | Activé |
    | Politique | Masquer (forcé) |
    | Publicité | Aucune |


---

## 11. Activer un profil existant

<span style="color: #2196F3">**Étant donné**</span> que j'ai créé un profil "🚗 Trajet quotidien"

<span style="color: #FF9800">**Quand**</span> je clique sur "Activer" pour ce profil

<span style="color: #4CAF50">**Alors**</span> tous les paramètres du profil sont appliqués
<span style="color: #9E9E9E">**Et**</span> je vois le message "Profil 'Trajet quotidien' activé"
<span style="color: #9E9E9E">**Et**</span> l'algorithme utilise ces paramètres immédiatement

---

## 12. Synchronisation profils entre devices

<span style="color: #2196F3">**Étant donné**</span> que j'ai créé 3 profils sur mon iPhone

<span style="color: #FF9800">**Quand**</span> je me connecte sur mon iPad

<span style="color: #4CAF50">**Alors**</span> mes 3 profils sont automatiquement synchronisés
<span style="color: #9E9E9E">**Et**</span> je peux les utiliser sur l'iPad

---

## 13. Modification d'un profil synchronisée

<span style="color: #2196F3">**Étant donné**</span> que j'ai un profil "Road trip" sur iPhone

<span style="color: #FF9800">**Quand**</span> je modifie ce profil sur iPhone

<span style="color: #4CAF50">**Alors**</span> la modification est synchronisée sur tous mes devices
<span style="color: #9E9E9E">**Et**</span> le profil est mis à jour partout en temps réel

---

## 14. Pas de partage de profils entre utilisateurs

<span style="color: #2196F3">**Étant donné**</span> que j'ai créé des profils personnalisés
<span style="color: #9E9E9E">**Et**</span> que ma conjointe a un compte RoadWave

<span style="color: #FF9800">**Quand**</span> elle se connecte sur son compte

<span style="color: #4CAF50">**Alors**</span> elle ne voit pas mes profils
<span style="color: #9E9E9E">**Et**</span> chaque utilisateur a ses propres profils

---

## 15. Auto-switch selon contexte (détection trajet récurrent)

<span style="color: #2196F3">**Étant donné**</span> que j'utilise toujours le profil "Trajet quotidien"
<span style="color: #9E9E9E">**Et**</span> que je pars de chez moi vers mon travail tous les matins à 8h

<span style="color: #FF9800">**Quand**</span> le système détecte ce trajet récurrent

<span style="color: #4CAF50">**Alors**</span> le profil "Trajet quotidien" est activé automatiquement
<span style="color: #9E9E9E">**Et**</span> je reçois une notification "Profil 'Trajet quotidien' activé"

---

## 16. Désactiver l'auto-switch

<span style="color: #2196F3">**Étant donné**</span> que l'auto-switch de profil est actif

<span style="color: #FF9800">**Quand**</span> je désactive cette option dans les paramètres

<span style="color: #4CAF50">**Alors**</span> les profils ne changent plus automatiquement
<span style="color: #9E9E9E">**Et**</span> je dois les activer manuellement

---

## 17. Blocage modification si vitesse GPS >10 km/h

<span style="color: #2196F3">**Étant donné**</span> que je conduis à 50 km/h

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier un curseur

<span style="color: #4CAF50">**Alors**</span> la modification est bloquée
<span style="color: #9E9E9E">**Et**</span> je vois le message "Modification impossible pendant la conduite"
<span style="color: #9E9E9E">**Et**</span> je dois m'arrêter ou être passager pour modifier

---

## 18. Modification possible si vitesse <10 km/h

<span style="color: #2196F3">**Étant donné**</span> que je suis arrêté à un feu rouge (5 km/h)

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier un curseur

<span style="color: #4CAF50">**Alors**</span> la modification est autorisée
<span style="color: #9E9E9E">**Et**</span> je peux ajuster les paramètres

---

## 19. Warning au lancement app

<span style="color: #FF9800">**Quand**</span> je lance l'application pour la première fois

<span style="color: #4CAF50">**Alors**</span> je vois un warning "Configurez vos préférences avant de prendre la route"
<span style="color: #9E9E9E">**Et**</span> un bouton "Configurer maintenant"
<span style="color: #9E9E9E">**Et**</span> je peux accéder rapidement aux paramètres

---

## 20. Modification uniquement app arrêtée ou mode passager

<span style="color: #2196F3">**Étant donné**</span> que je suis passager dans une voiture
<span style="color: #9E9E9E">**Et**</span> que le mode passager est activé

<span style="color: #FF9800">**Quand**</span> j'essaie de modifier les paramètres

<span style="color: #4CAF50">**Alors**</span> la modification est autorisée
<span style="color: #9E9E9E">**Et**</span> le blocage vitesse GPS ne s'applique pas

---

## 21. Statistiques d'utilisation des profils

<span style="color: #2196F3">**Étant donné**</span> que j'utilise plusieurs profils

<span style="color: #FF9800">**Quand**</span> je consulte mes statistiques

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai créé un profil "Test"

<span style="color: #FF9800">**Quand**</span> je supprime ce profil

<span style="color: #4CAF50">**Alors**</span> le profil est définitivement supprimé
<span style="color: #9E9E9E">**Et**</span> je vois le message "Profil 'Test' supprimé"
<span style="color: #9E9E9E">**Et**</span> il disparaît de tous mes devices

---

## 23. Limite de profils par utilisateur

<span style="color: #2196F3">**Étant donné**</span> que j'ai créé 10 profils

<span style="color: #FF9800">**Quand**</span> j'essaie de créer un 11ème profil

<span style="color: #4CAF50">**Alors**</span> la création échoue
<span style="color: #9E9E9E">**Et**</span> je vois le message "Maximum 10 profils par utilisateur"

---

## 24. Dupliquer un profil existant

<span style="color: #2196F3">**Étant donné**</span> que j'ai un profil "Trajet quotidien"

<span style="color: #FF9800">**Quand**</span> je clique sur "Dupliquer"

<span style="color: #4CAF50">**Alors**</span> un nouveau profil "Trajet quotidien (copie)" est créé
<span style="color: #9E9E9E">**Et**</span> il a les mêmes paramètres que l'original
<span style="color: #9E9E9E">**Et**</span> je peux le modifier indépendamment

---

## 25. Réinitialiser un profil aux valeurs par défaut

<span style="color: #2196F3">**Étant donné**</span> que j'ai modifié un profil

<span style="color: #FF9800">**Quand**</span> je clique sur "Réinitialiser"

<span style="color: #4CAF50">**Alors**</span> tous les paramètres reviennent aux valeurs par défaut:

    | parametre | valeur défaut |
    |---|---|
    | Géolocalisation | Équilibré |
    | Découverte | 10% |
    | Politique | Équilibré |


---





<div style="page-break-after: always;"></div>


# 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**
>
> <span style="color: #2196F3">**Étant donné**</span> que l'API RoadWave est disponible
## 1. Calcul du score géographique linéaire

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu existe à Paris
<span style="color: #9E9E9E">**Et**</span> que la distance_max_km est configurée à 200 km

<span style="color: #FF9800">**Quand**</span> un utilisateur est à 50 km du contenu

<span style="color: #4CAF50">**Alors**</span> le score_geo = 1 - (50 / 200) = 0.75

---

## 2. Score géo à distance nulle (sur place)

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu existe à un point GPS précis

<span style="color: #FF9800">**Quand**</span> un utilisateur est exactement au même point (0 km)

<span style="color: #4CAF50">**Alors**</span> le score_geo = 1.0 (maximum)

---

## 3. Score géo à distance_max (200 km)

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu existe à Paris

<span style="color: #FF9800">**Quand**</span> un utilisateur est à 200 km du contenu

<span style="color: #4CAF50">**Alors**</span> le score_geo = 1 - (200 / 200) = 0.0

---

## 4. Score géo au-delà de distance_max

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu existe à Paris

<span style="color: #FF9800">**Quand**</span> un utilisateur est à 250 km du contenu (au-delà de 200 km max)

<span style="color: #4CAF50">**Alors**</span> le score_geo = 0.0 (minimum)
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a les jauges suivantes:

    | categorie | niveau |
    |---|---|
    | Automobile | 80% |
    | Voyage | 60% |
    | Musique | 40% |

<span style="color: #9E9E9E">**Et**</span> qu'un contenu est tagué "Automobile" et "Voyage"

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_interets

<span style="color: #4CAF50">**Alors**</span> score_interets = (0.8 + 0.6) / 2 = 0.7

---

## 6. Score d'intérêts avec un seul tag

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a la jauge "Économie" à 90%
<span style="color: #9E9E9E">**Et**</span> qu'un contenu est tagué uniquement "Économie"

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_interets

<span style="color: #4CAF50">**Alors**</span> score_interets = 0.9

---

## 7. Score d'intérêts avec tags non matchés

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a des jauges "Sport" et "Politique" élevées
<span style="color: #9E9E9E">**Et**</span> qu'un contenu est tagué "Musique" et "Philosophie"
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur n'a pas ces catégories

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_interets

<span style="color: #4CAF50">**Alors**</span> score_interets = 0.5 (neutre par défaut pour catégories inconnues)

---

## 8. Calcul du score d'engagement avec métriques

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a:

    | metrique | valeur |
    |---|---|
    | ecoutes | 1000 |
    | ecoutes_completes | 700 |
    | likes | 300 |
    | abonnements_apres | 50 |


<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_engagement

<span style="color: #4CAF50">**Alors**</span> taux_completion = 700 / 1000 = 0.7
<span style="color: #9E9E9E">**Et**</span> ratio_likes = 300 / 1000 = 0.3
<span style="color: #9E9E9E">**Et**</span> ratio_abonnements = 50 / 1000 = 0.05
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a seulement 30 écoutes

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_engagement

<span style="color: #4CAF50">**Alors**</span> score_engagement = 0.5 (neutre par défaut)
<span style="color: #9E9E9E">**Et**</span> le contenu n'est pas pénalisé pour manque de données

---

## 10. Contenu avec exactement 50 écoutes - calcul réel

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu a exactement 50 écoutes
<span style="color: #9E9E9E">**Et**</span> des métriques d'engagement complètes

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_engagement

<span style="color: #4CAF50">**Alors**</span> le score est calculé normalement (pas de seuil neutre)

---

## 11. Bonus aléatoire - 10% des recommandations

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur demande 10 recommandations
<span style="color: #9E9E9E">**Et**</span> que la part_aleatoire_global est à 10%

<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> 1 contenu sur 10 est tiré aléatoirement
<span style="color: #9E9E9E">**Et**</span> 9 contenus sont calculés avec le score combiné
<span style="color: #9E9E9E">**Et**</span> le contenu aléatoire n'est pas dans l'historique déjà écouté

---

## 12. Curseur utilisateur découverte à 0% - aucun aléatoire

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur configure le curseur découverte à 0%

<span style="color: #FF9800">**Quand**</span> l'utilisateur demande 20 recommandations

<span style="color: #4CAF50">**Alors**</span> les 20 contenus sont calculés avec le score combiné
<span style="color: #9E9E9E">**Et**</span> aucun contenu aléatoire n'est proposé

---

## 13. Curseur utilisateur découverte à 50% - découverte max

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur configure le curseur découverte à 50%

<span style="color: #FF9800">**Quand**</span> l'utilisateur demande 20 recommandations

<span style="color: #4CAF50">**Alors**</span> 10 contenus sont tirés aléatoirement
<span style="color: #9E9E9E">**Et**</span> 10 contenus sont calculés avec le score combiné

---

## 14. Score final combiné pour contenu géo-ancré

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_final

<span style="color: #4CAF50">**Alors**</span> score_final = (0.9 × 0.7) + (0.6 × 0.1) + (0.45 × 0.2)
<span style="color: #9E9E9E">**Et**</span> score_final = 0.63 + 0.06 + 0.09 = 0.78

---

## 15. Score final combiné pour contenu géo-neutre

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_final

<span style="color: #4CAF50">**Alors**</span> score_final = (0.3 × 0.2) + (0.9 × 0.6) + (0.6 × 0.2)
<span style="color: #9E9E9E">**Et**</span> score_final = 0.06 + 0.54 + 0.12 = 0.72
<span style="color: #9E9E9E">**Et**</span> le contenu peut être recommandé malgré la distance

---

## 16. Contenu viral lointain peut être recommandé

<span style="color: #2196F3">**Étant donné**</span> qu'un contenu viral existe à Paris
<span style="color: #9E9E9E">**Et**</span> qu'il a un score_engagement très élevé de 0.95
<span style="color: #9E9E9E">**Et**</span> qu'un utilisateur est à Marseille (score_geo = 0.1)

<span style="color: #FF9800">**Quand**</span> l'algorithme calcule le score_final

<span style="color: #4CAF50">**Alors**</span> le score_engagement élevé compense le score_geo faible
<span style="color: #9E9E9E">**Et**</span> le contenu peut apparaître dans les recommandations

---

## 17. Ordre de recommandation par score décroissant

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> l'utilisateur demande des recommandations

<span style="color: #4CAF50">**Alors**</span> 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%

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur a écouté les contenus suivants:

    | contenu | completion |
    |---|---|
    | Contenu A | 85% |
    | Contenu B | 95% |
    | Contenu C | 30% |


<span style="color: #FF9800">**Quand**</span> l'algorithme génère les recommandations

<span style="color: #4CAF50">**Alors**</span> "Contenu A" et "Contenu B" ne sont jamais proposés
<span style="color: #F44336">**Mais**</span> "Contenu C" peut être reproposé

---

## 19. Pré-calcul de 5 contenus suivants

<span style="color: #2196F3">**Étant donné**</span> qu'un utilisateur écoute un contenu

<span style="color: #FF9800">**Quand**</span> l'algorithme prépare les contenus suivants

<span style="color: #4CAF50">**Alors**</span> 5 contenus sont pré-calculés selon le score
<span style="color: #9E9E9E">**Et**</span> ces contenus sont mis en cache pour performance

---

## 20. Recalcul si déplacement >10 km

<span style="color: #2196F3">**Étant donné**</span> que 5 contenus suivants sont pré-calculés
<span style="color: #9E9E9E">**Et**</span> que l'utilisateur se déplace de 12 km

<span style="color: #FF9800">**Quand**</span> l'utilisateur demande le contenu suivant

<span style="color: #4CAF50">**Alors**</span> l'algorithme recalcule les scores avec la nouvelle position
<span style="color: #9E9E9E">**Et**</span> propose de nouveaux contenus plus pertinents géographiquement

---

## 21. Recalcul après 10 minutes d'inactivité

<span style="color: #2196F3">**Étant donné**</span> que 5 contenus suivants sont pré-calculés
<span style="color: #9E9E9E">**Et**</span> que 11 minutes se sont écoulées sans action

<span style="color: #FF9800">**Quand**</span> l'utilisateur demande le contenu suivant

<span style="color: #4CAF50">**Alors**</span> l'algorithme recalcule les scores
<span style="color: #9E9E9E">**Et**</span> prend en compte les nouveaux contenus publiés

---





<div style="page-break-after: always;"></div>


# Anonymisation des données GPS après 24h
**18 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur avec le GPS activé
> <span style="color: #9E9E9E">**Et**</span> que j'utilise l'application depuis plusieurs jours
## 1. Conservation des données GPS précises pendant 24h

<span style="color: #2196F3">**Étant donné**</span> que j'écoute un contenu à la position GPS 48.8566, 2.3522 (Paris, Tour Eiffel)
<span style="color: #9E9E9E">**Et**</span> qu'il est 10:00 le 2025-01-20

<span style="color: #FF9800">**Quand**</span> l'événement d'écoute est enregistré en base de données

<span style="color: #4CAF50">**Alors**</span> les coordonnées précises 48.8566, 2.3522 sont stockées
<span style="color: #9E9E9E">**Et**</span> le champ `anonymized` est à `false`
<span style="color: #9E9E9E">**Et**</span> le champ `created_at` contient "2025-01-20 10:00:00"
<span style="color: #9E9E9E">**Et**</span> ces données précises servent à la recommandation personnalisée

---

## 2. Conversion en geohash après 24h

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté un contenu le 2025-01-20 à 10:00 à la position 48.8566, 2.3522

<span style="color: #FF9800">**Quand**</span> le job quotidien d'anonymisation s'exécute le 2025-01-21 à 02:00

<span style="color: #4CAF50">**Alors**</span> les coordonnées précises sont converties en geohash précision 5
<span style="color: #9E9E9E">**Et**</span> le geohash correspond à une zone d'environ 5km²
<span style="color: #9E9E9E">**Et**</span> les coordonnées originales 48.8566, 2.3522 sont supprimées définitivement
<span style="color: #9E9E9E">**Et**</span> le champ `anonymized` passe à `true`
<span style="color: #9E9E9E">**Et**</span> il est impossible de retrouver la position précise d'origine

---

## 3. Requête SQL d'anonymisation (PostGIS)

<span style="color: #2196F3">**Étant donné**</span> que le job quotidien d'anonymisation s'exécute

<span style="color: #FF9800">**Quand**</span> la requête SQL suivante est exécutée:

<span style="color: #4CAF50">**Alors**</span> toutes les positions vieilles de plus de 24h sont anonymisées
<span style="color: #9E9E9E">**Et**</span> le processus est automatique et irréversible
<span style="color: #9E9E9E">**Et**</span> les données sont conformes RGPD

---

## 4. Précision du geohash niveau 5

<span style="color: #2196F3">**Étant donné**</span> qu'une position GPS est convertie en geohash précision 5

<span style="color: #FF9800">**Quand**</span> on analyse la zone couverte

<span style="color: #4CAF50">**Alors**</span> la zone fait environ 5km² (4.9km × 4.9km)
<span style="color: #9E9E9E">**Et**</span> cette précision est suffisante pour des analytics agrégées
<span style="color: #9E9E9E">**Et**</span> cette précision ne permet pas d'identifier un individu (conformité CNIL)

---

## 5. Exemple de conversion Paris

<span style="color: #2196F3">**Étant donné**</span> que ma position précise est 48.8566, 2.3522 (Tour Eiffel)

<span style="color: #FF9800">**Quand**</span> la conversion en geohash précision 5 est appliquée

<span style="color: #4CAF50">**Alors**</span> le geohash généré est "u09wh"
<span style="color: #9E9E9E">**Et**</span> ce geohash couvre une zone de ~5km² autour de la Tour Eiffel
<span style="color: #9E9E9E">**Et**</span> toutes les positions dans cette zone partagent le même geohash
<span style="color: #9E9E9E">**Et**</span> il est impossible de distinguer deux utilisateurs dans cette zone

---

## 6. Conservation de l'historique personnel utilisateur

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> j'ouvre mon historique personnel dans "Profil > Mes trajets"

<span style="color: #4CAF50">**Alors**</span> je vois mes trajets avec les positions précises intégrales
<span style="color: #9E9E9E">**Et**</span> ces données ne sont pas anonymisées tant que mon compte est actif
<span style="color: #9E9E9E">**Et**</span> seul moi peut accéder à ces données
<span style="color: #9E9E9E">**Et**</span> elles ne sont pas utilisées pour des analytics globales

---

## 7. Anonymisation pour analytics globales uniquement

<span style="color: #2196F3">**Étant donné**</span> que RoadWave génère des analytics agrégées

<span style="color: #FF9800">**Quand**</span> l'équipe analyse les zones géographiques populaires

<span style="color: #4CAF50">**Alors**</span> seules les données anonymisées (geohash) sont utilisées
<span style="color: #9E9E9E">**Et**</span> les positions précises de l'historique personnel ne sont jamais agrégées
<span style="color: #9E9E9E">**Et**</span> les heatmaps de trafic utilisent uniquement les geohash ~5km²

---

## 8. Planification du job d'anonymisation

<span style="color: #2196F3">**Étant donné**</span> que le système est en production

<span style="color: #FF9800">**Quand**</span> on consulte les jobs planifiés (cron)

<span style="color: #4CAF50">**Alors**</span> un job "anonymize_gps_data" est configuré
<span style="color: #9E9E9E">**Et**</span> le job s'exécute tous les jours à 02:00 (heure creuse)
<span style="color: #9E9E9E">**Et**</span> le job traite toutes les positions vieilles de plus de 24h
<span style="color: #9E9E9E">**Et**</span> un log est généré pour traçabilité

---

## 9. Exécution du job avec métriques

<span style="color: #2196F3">**Étant donné**</span> que le job d'anonymisation s'exécute le 2025-01-21 à 02:00

<span style="color: #FF9800">**Quand**</span> le job se termine

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le rapport est loggé dans Sentry/Grafana
<span style="color: #9E9E9E">**Et**</span> une alerte est envoyée si le job échoue

---

## 10. Performances du job d'anonymisation

<span style="color: #2196F3">**Étant donné**</span> que 100 000 positions doivent être anonymisées

<span style="color: #FF9800">**Quand**</span> le job s'exécute

<span style="color: #4CAF50">**Alors**</span> le traitement se fait en moins de 30 secondes
<span style="color: #9E9E9E">**Et**</span> la requête PostGIS est optimisée avec index
<span style="color: #9E9E9E">**Et**</span> aucun impact sur les performances de l'application en production

---

## 11. Impossibilité de réidentification

<span style="color: #2196F3">**Étant donné**</span> qu'une position a été anonymisée en geohash "u09wh"

<span style="color: #FF9800">**Quand**</span> un attaquant tente de retrouver la position précise d'origine

<span style="color: #4CAF50">**Alors**</span> il est impossible de déterminer la position exacte
<span style="color: #9E9E9E">**Et**</span> des milliers de positions précises correspondent au même geohash
<span style="color: #9E9E9E">**Et**</span> il n'y a aucune traçabilité vers la position originale
<span style="color: #9E9E9E">**Et**</span> cette anonymisation est irréversible

---

## 12. Conformité CNIL - données véritablement anonymisées

<span style="color: #2196F3">**Étant donné**</span> que les positions sont converties en geohash précision 5

<span style="color: #FF9800">**Quand**</span> un auditeur CNIL vérifie la conformité

<span style="color: #4CAF50">**Alors**</span> les données sont considérées comme véritablement anonymisées
<span style="color: #9E9E9E">**Et**</span> elles ne sont plus considérées comme des données personnelles
<span style="color: #9E9E9E">**Et**</span> aucun consentement n'est requis pour leur traitement analytique
<span style="color: #9E9E9E">**Et**</span> elles peuvent être conservées indéfiniment

---

## 13. Heatmap de trafic avec données anonymisées

<span style="color: #2196F3">**Étant donné**</span> que RoadWave génère une heatmap des zones populaires

<span style="color: #FF9800">**Quand**</span> on analyse les données utilisées

<span style="color: #4CAF50">**Alors**</span> seules les positions anonymisées (geohash) sont agrégées
<span style="color: #9E9E9E">**Et**</span> la heatmap montre des zones de ~5km²
<span style="color: #9E9E9E">**Et**</span> aucune position précise n'est révélée
<span style="color: #9E9E9E">**Et**</span> cette analyse ne nécessite pas de consentement utilisateur (données anonymes)

---

## 14. Statistiques géographiques par département

<span style="color: #2196F3">**Étant donné**</span> que RoadWave analyse l'utilisation par département

<span style="color: #FF9800">**Quand**</span> les statistiques sont générées

<span style="color: #4CAF50">**Alors**</span> les données anonymisées sont agrégées par département
<span style="color: #9E9E9E">**Et**</span> les résultats montrent: "Paris (75): 12 500 écoutes, Lyon (69): 8 300 écoutes"
<span style="color: #9E9E9E">**Et**</span> aucune donnée personnelle n'est révélée
<span style="color: #9E9E9E">**Et**</span> les statistiques sont RGPD-compliant

---

## 15. Coût de la solution d'anonymisation

<span style="color: #2196F3">**Étant donné**</span> que PostGIS est utilisé pour l'anonymisation GPS

<span style="color: #FF9800">**Quand**</span> on calcule le coût de la solution

<span style="color: #4CAF50">**Alors**</span> le coût est de 0€ (PostGIS inclus dans PostgreSQL)
<span style="color: #9E9E9E">**Et**</span> aucune librairie tierce n'est nécessaire
<span style="color: #9E9E9E">**Et**</span> la solution est entièrement maîtrisée (self-hosted)

---

## 16. Anonymisation respecte les positions en cours de session

<span style="color: #2196F3">**Étant donné**</span> que je suis en train d'écouter du contenu actuellement
<span style="color: #9E9E9E">**Et**</span> que certaines de mes positions ont plus de 24h

<span style="color: #FF9800">**Quand**</span> le job d'anonymisation s'exécute

<span style="color: #4CAF50">**Alors**</span> mes positions de plus de 24h sont anonymisées
<span style="color: #F44336">**Mais**</span> ma position actuelle (session en cours) reste précise
<span style="color: #9E9E9E">**Et**</span> la recommandation continue de fonctionner normalement

---

## 17. Suppression de compte et anonymisation GPS

<span style="color: #2196F3">**Étant donné**</span> que je demande la suppression de mon compte

<span style="color: #FF9800">**Quand**</span> le compte est supprimé (après grace period de 30j)

<span style="color: #4CAF50">**Alors**</span> toutes mes positions GPS (précises et anonymisées) sont supprimées
<span style="color: #9E9E9E">**Et**</span> mon historique personnel de trajets est supprimé
<span style="color: #9E9E9E">**Et**</span> aucune donnée GPS ne subsiste, même anonymisée

---

## 18. Export de données avant anonymisation

<span style="color: #2196F3">**Étant donné**</span> que je demande un export de mes données
<span style="color: #9E9E9E">**Et**</span> que certaines de mes positions ont été anonymisées

<span style="color: #FF9800">**Quand**</span> l'export est généré

<span style="color: #4CAF50">**Alors**</span> les positions précises de mon historique personnel sont incluses
<span style="color: #F44336">**Mais**</span> les positions déjà anonymisées (>24h, analytics) apparaissent en geohash
<span style="color: #9E9E9E">**Et**</span> l'export précise quelles données ont été anonymisées et pourquoi

---





<div style="page-break-after: always;"></div>


# Conformité administrative RGPD (Registre, Breach, DPO)
**22 scénarios** (21 standards, 1 plan)

---

## 1. Registre des traitements en Markdown versionné Git

<span style="color: #2196F3">**Étant donné**</span> que RoadWave doit tenir un registre des traitements

<span style="color: #FF9800">**Quand**</span> on consulte la documentation

<span style="color: #4CAF50">**Alors**</span> un fichier `docs/rgpd/registre-traitements.md` existe
<span style="color: #9E9E9E">**Et**</span> le fichier est versionné dans Git
<span style="color: #9E9E9E">**Et**</span> l'historique des modifications est traçable via Git
<span style="color: #9E9E9E">**Et**</span> chaque traitement est documenté dans une section dédiée

---

## 2. Contenu obligatoire pour chaque traitement

<span style="color: #2196F3">**Étant donné**</span> que le registre des traitements contient le traitement "Géolocalisation utilisateurs"

<span style="color: #FF9800">**Quand**</span> on lit la section correspondante

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que le registre des traitements est complet

<span style="color: #FF9800">**Quand**</span> on liste tous les traitements

<span style="color: #4CAF50">**Alors**</span> le traitement "<traitement>" est documenté avec la base légale "<base_legale>"

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que le registre des traitements existe

<span style="color: #FF9800">**Quand**</span> on consulte l'historique Git

<span style="color: #4CAF50">**Alors**</span> une mise à jour est effectuée au moins tous les 3 mois
<span style="color: #9E9E9E">**Et**</span> chaque mise à jour a un commit avec message explicite
<span style="color: #9E9E9E">**Et**</span> un tag Git marque chaque review trimestrielle
<span style="color: #9E9E9E">**Et**</span> les modifications sont traçables (auteur, date, changements)

---

## 5. Mise à jour immédiate si nouveau traitement

<span style="color: #2196F3">**Étant donné**</span> qu'une nouvelle fonctionnalité nécessite un traitement de données

<span style="color: #FF9800">**Quand**</span> la fonctionnalité est développée

<span style="color: #4CAF50">**Alors**</span> le registre est mis à jour AVANT le déploiement en production
<span style="color: #9E9E9E">**Et**</span> le nouveau traitement est documenté complètement
<span style="color: #9E9E9E">**Et**</span> un commit Git enregistre l'ajout
<span style="color: #9E9E9E">**Et**</span> le DPO valide la conformité RGPD du nouveau traitement

---

## 6. Migration future vers interface admin PostgreSQL

<span style="color: #2196F3">**Étant donné**</span> que RoadWave dépasse 100 000 utilisateurs

<span style="color: #FF9800">**Quand**</span> la complexité du registre augmente

<span style="color: #4CAF50">**Alors**</span> une interface admin PostgreSQL est développée
<span style="color: #9E9E9E">**Et**</span> le registre Markdown est migré vers la base de données
<span style="color: #9E9E9E">**Et**</span> l'historique Git est conservé pour audit
<span style="color: #9E9E9E">**Et**</span> l'interface permet une gestion plus efficace des traitements

---

## 7. Détection automatique d'événements critiques

<span style="color: #2196F3">**Étant donné**</span> que le système de monitoring est actif

<span style="color: #FF9800">**Quand**</span> un événement critique se produit

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> les alertes permettent une réaction rapide

---

## 8. Runbook de procédure breach disponible

<span style="color: #2196F3">**Étant donné**</span> qu'une violation de données potentielle est détectée

<span style="color: #FF9800">**Quand**</span> l'équipe consulte la documentation

<span style="color: #4CAF50">**Alors**</span> un runbook `docs/rgpd/procedure-breach.md` existe
<span style="color: #9E9E9E">**Et**</span> le runbook contient une checklist 72h CNIL
<span style="color: #9E9E9E">**Et**</span> chaque étape est clairement documentée
<span style="color: #9E9E9E">**Et**</span> les contacts d'urgence sont listés

---

## 9. Checklist 72h en cas de breach

<span style="color: #2196F3">**Étant donné**</span> qu'une violation de données est confirmée

<span style="color: #FF9800">**Quand**</span> l'équipe suit la procédure breach

<span style="color: #4CAF50">**Alors**</span> 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é |

<span style="color: #9E9E9E">**Et**</span> chaque étape est documentée pour audit

---

## 10. Évaluation de la gravité du breach

<span style="color: #2196F3">**Étant donné**</span> qu'une violation de données est détectée

<span style="color: #FF9800">**Quand**</span> l'équipe évalue la gravité

<span style="color: #4CAF50">**Alors**</span> 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é |

<span style="color: #9E9E9E">**Et**</span> si le risque est élevé, la CNIL est notifiée sous 72h

---

## 11. Notification CNIL dans les 72h

<span style="color: #2196F3">**Étant donné**</span> qu'un breach avec risque élevé est confirmé à 10:00 le 2025-01-20

<span style="color: #FF9800">**Quand**</span> la gravité est évaluée comme nécessitant une notification

<span style="color: #4CAF50">**Alors**</span> la CNIL est notifiée avant 10:00 le 2025-01-23 (72h)
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> un email pré-rédigé (template) est utilisé pour gagner du temps

---

## 12. Notification des utilisateurs si risque élevé

<span style="color: #2196F3">**Étant donné**</span> qu'un breach impacte 5000 utilisateurs
<span style="color: #9E9E9E">**Et**</span> que le risque est élevé (mots de passe non chiffrés exposés)

<span style="color: #FF9800">**Quand**</span> la CNIL est notifiée

<span style="color: #4CAF50">**Alors**</span> les utilisateurs impactés sont notifiés dans les 72h
<span style="color: #9E9E9E">**Et**</span> l'email contient:
<span style="color: #9E9E9E">**Et**</span> un lien de réinitialisation de mot de passe est inclus

---

## 13. Aucune notification si risque faible

<span style="color: #2196F3">**Étant donné**</span> qu'un breach mineur est détecté (logs techniques exposés, aucune donnée personnelle)

<span style="color: #FF9800">**Quand**</span> l'équipe évalue la gravité

<span style="color: #4CAF50">**Alors**</span> le risque est jugé faible
<span style="color: #9E9E9E">**Et**</span> aucune notification CNIL n'est requise (Article 33.1 RGPD)
<span style="color: #9E9E9E">**Et**</span> aucune notification utilisateur n'est envoyée
<span style="color: #9E9E9E">**Et**</span> un log interne est créé pour traçabilité

---

## 14. Monitoring proactif pour éviter découverte tardive

<span style="color: #2196F3">**Étant donné**</span> que Sentry et Grafana sont configurés

<span style="color: #FF9800">**Quand**</span> un comportement anormal est détecté

<span style="color: #4CAF50">**Alors**</span> une alerte est envoyée en temps réel
<span style="color: #9E9E9E">**Et**</span> l'équipe peut réagir avant qu'un breach majeur ne se produise
<span style="color: #9E9E9E">**Et**</span> les logs sont analysés quotidiennement pour détecter des anomalies
<span style="color: #9E9E9E">**Et**</span> cette approche proactive limite les risques de découverte tardive

---

## 15. Fondateur = DPO temporaire (MVP)

<span style="color: #2196F3">**Étant donné**</span> que RoadWave est en phase MVP
<span style="color: #9E9E9E">**Et**</span> que l'entreprise a moins de 250 employés

<span style="color: #FF9800">**Quand**</span> on vérifie l'obligation légale d'avoir un DPO

<span style="color: #4CAF50">**Alors**</span> le DPO n'est pas obligatoire selon le RGPD Article 37
<span style="color: #9E9E9E">**Et**</span> le fondateur assume temporairement le rôle de DPO
<span style="color: #9E9E9E">**Et**</span> le fondateur suit la formation CNIL gratuite (4h)

---

## 16. Formation CNIL du DPO temporaire

<span style="color: #2196F3">**Étant donné**</span> que le fondateur est DPO temporaire

<span style="color: #FF9800">**Quand**</span> on vérifie sa formation

<span style="color: #4CAF50">**Alors**</span> le fondateur a suivi la formation CNIL en ligne (4h)
<span style="color: #9E9E9E">**Et**</span> le fondateur a obtenu la certification "Atelier RGPD" (gratuit)
<span style="color: #9E9E9E">**Et**</span> le certificat est conservé pour audit
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je consulte les mentions légales de RoadWave

<span style="color: #FF9800">**Quand**</span> je cherche le contact du DPO

<span style="color: #4CAF50">**Alors**</span> l'email "dpo@roadwave.fr" est clairement affiché
<span style="color: #9E9E9E">**Et**</span> cet email est également dans les CGU
<span style="color: #9E9E9E">**Et**</span> le délai de réponse garanti est de 1 mois maximum (RGPD Article 12.3)
<span style="color: #9E9E9E">**Et**</span> une adresse postale est également fournie

---

## 18. Demande d'exercice de droits RGPD au DPO

<span style="color: #2196F3">**Étant donné**</span> que je veux exercer mon droit d'accès à mes données

<span style="color: #FF9800">**Quand**</span> j'envoie un email à dpo@roadwave.fr

<span style="color: #4CAF50">**Alors**</span> je reçois un accusé de réception dans les 48h
<span style="color: #9E9E9E">**Et**</span> ma demande est traitée dans un délai maximum de 1 mois
<span style="color: #9E9E9E">**Et**</span> si le délai dépasse 1 mois, je suis informé de la prolongation (max 2 mois supplémentaires)
<span style="color: #9E9E9E">**Et**</span> la réponse est complète et conforme au RGPD

---

## 19. Types de demandes gérées par le DPO

<span style="color: #2196F3">**Étant donné**</span> que je contacte le DPO

<span style="color: #FF9800">**Quand**</span> j'envoie une demande

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> chaque demande reçoit une réponse personnalisée

---

## 20. Migration vers DPO externe si croissance

<span style="color: #2196F3">**Étant donné**</span> que RoadWave dépasse 100 000 utilisateurs

<span style="color: #FF9800">**Quand**</span> la charge de travail DPO augmente

<span style="color: #4CAF50">**Alors**</span> un DPO externe mutualisé est engagé
<span style="color: #9E9E9E">**Et**</span> le coût est d'environ 200€/mois
<span style="color: #9E9E9E">**Et**</span> le DPO externe a les certifications CNIL requises
<span style="color: #9E9E9E">**Et**</span> un contrat de sous-traitance RGPD est signé

---

## 21. Recrutement DPO interne si >10 employés

<span style="color: #2196F3">**Étant donné**</span> que RoadWave a plus de 10 employés

<span style="color: #FF9800">**Quand**</span> l'entreprise se structure

<span style="color: #4CAF50">**Alors**</span> un DPO interne peut être recruté
<span style="color: #9E9E9E">**Et**</span> le DPO interne a une certification CNIL (AFCDP ou équivalent)
<span style="color: #9E9E9E">**Et**</span> le DPO est indépendant et ne peut être licencié pour ses fonctions
<span style="color: #9E9E9E">**Et**</span> le DPO a un accès direct à la direction

---

## 22. Récapitulatif des coûts RGPD

<span style="color: #2196F3">**Étant donné**</span> que toutes les mesures RGPD sont en place

<span style="color: #FF9800">**Quand**</span> on calcule le coût total mensuel

<span style="color: #4CAF50">**Alors**</span> 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€ |

<span style="color: #9E9E9E">**Et**</span> le coût total est d'environ 5€/mois
<span style="color: #9E9E9E">**Et**</span> cette conformité est 100% opensource et maîtrisée

---





<div style="page-break-after: always;"></div>


# Gestion du consentement RGPD
**16 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un nouvel utilisateur
> <span style="color: #9E9E9E">**Et**</span> que j'accède à l'application pour la première fois
## 1. Affichage du banner de consentement au premier lancement web

<span style="color: #2196F3">**Étant donné**</span> que j'accède à l'application web pour la première fois

<span style="color: #FF9800">**Quand**</span> la page se charge

<span style="color: #4CAF50">**Alors**</span> un banner RGPD Tarteaucitron.js s'affiche
<span style="color: #9E9E9E">**Et**</span> le banner est en français
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le banner est customisé aux couleurs de RoadWave

---

## 2. Granularité des consentements

<span style="color: #2196F3">**Étant donné**</span> que le banner RGPD est affiché

<span style="color: #FF9800">**Quand**</span> je clique sur "Personnaliser"

<span style="color: #4CAF50">**Alors**</span> je vois les catégories de consentements suivantes:

    | catégorie | type | requis |
    |---|---|---|
    | Fonctionnel | Nécessaire | oui |
    | Analytique | Optionnel | non |
    | Marketing | Optionnel | non |

<span style="color: #9E9E9E">**Et**</span> chaque catégorie a une description claire de son usage
<span style="color: #9E9E9E">**Et**</span> je peux accepter ou refuser chaque catégorie individuellement

---

## 3. Consentement géolocalisation précise - obligatoire

<span style="color: #2196F3">**Étant donné**</span> que je suis sur l'application mobile
<span style="color: #9E9E9E">**Et**</span> que l'onboarding est terminé

<span style="color: #FF9800">**Quand**</span> l'application a besoin d'accéder à ma position précise

<span style="color: #4CAF50">**Alors**</span> un écran de demande de consentement s'affiche
<span style="color: #9E9E9E">**Et**</span> le message explique clairement l'usage:
<span style="color: #9E9E9E">**Et**</span> je peux accepter ou refuser
<span style="color: #9E9E9E">**Et**</span> si je refuse, l'application bascule en mode dégradé (GeoIP uniquement)

---

## 4. Double consentement GPS - banner app + permission OS

<span style="color: #2196F3">**Étant donné**</span> que je veux activer la géolocalisation précise

<span style="color: #FF9800">**Quand**</span> j'accepte le consentement dans l'application

<span style="color: #4CAF50">**Alors**</span> l'application demande également la permission au système d'exploitation
<span style="color: #9E9E9E">**Et**</span> sur iOS, la popup système s'affiche: "Autoriser RoadWave à accéder à votre position ?"
<span style="color: #9E9E9E">**Et**</span> sur Android, la popup système s'affiche avec les options "Toujours autoriser / Autoriser seulement pendant l'utilisation / Refuser"
<span style="color: #9E9E9E">**Et**</span> les deux consentements (app + OS) doivent être acceptés pour activer le GPS précis

---

## 5. Enregistrement du consentement en base de données

<span style="color: #2196F3">**Étant donné**</span> que j'ai accepté les consentements suivants:

    | type | accepté |
    |---|---|
    | Fonctionnel | oui |
    | Analytique | oui |
    | Marketing | non |
    | GPS précis | oui |


<span style="color: #FF9800">**Quand**</span> je valide mes choix

<span style="color: #4CAF50">**Alors**</span> un enregistrement est créé dans la table `user_consents`
<span style="color: #9E9E9E">**Et**</span> 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] |

<span style="color: #9E9E9E">**Et**</span> chaque type de consentement a un enregistrement séparé

---

## 6. Versioning des consentements

<span style="color: #2196F3">**Étant donné**</span> que j'ai accepté le consentement "Analytique" version 1 le 2025-01-01
<span style="color: #9E9E9E">**Et**</span> que les CGU sont mises à jour le 2025-06-01

<span style="color: #FF9800">**Quand**</span> je me connecte après la mise à jour

<span style="color: #4CAF50">**Alors**</span> un nouveau consentement version 2 m'est demandé
<span style="color: #9E9E9E">**Et**</span> mon ancien consentement version 1 reste dans l'historique
<span style="color: #9E9E9E">**Et**</span> je dois accepter la nouvelle version pour continuer à utiliser les analytics

---

## 7. Historique complet conservé pour preuve légale

<span style="color: #2196F3">**Étant donné**</span> 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 |


<span style="color: #FF9800">**Quand**</span> un auditeur CNIL consulte mon historique de consentements

<span style="color: #4CAF50">**Alors**</span> tous les enregistrements sont conservés
<span style="color: #9E9E9E">**Et**</span> l'historique prouve que chaque consentement a été donné librement
<span style="color: #9E9E9E">**Et**</span> les timestamps permettent de prouver la conformité à tout moment

---

## 8. Consentement analytique - optionnel

<span style="color: #2196F3">**Étant donné**</span> que je refuse le consentement "Analytique"

<span style="color: #FF9800">**Quand**</span> j'utilise l'application

<span style="color: #4CAF50">**Alors**</span> aucun cookie Matomo `_pk_id` n'est déposé
<span style="color: #9E9E9E">**Et**</span> aucune donnée d'usage n'est envoyée à Matomo
<span style="color: #9E9E9E">**Et**</span> l'application fonctionne normalement sans analytics

---

## 9. Consentement notifications push - optionnel

<span style="color: #2196F3">**Étant donné**</span> que je refuse le consentement "Notifications push"

<span style="color: #FF9800">**Quand**</span> un créateur que je suis publie un nouveau contenu

<span style="color: #4CAF50">**Alors**</span> je ne reçois pas de notification push
<span style="color: #F44336">**Mais**</span> je peux voir le nouveau contenu dans l'application
<span style="color: #9E9E9E">**Et**</span> l'application fonctionne normalement

---

## 10. Consentement GPS précis - requis pour fonctionnalités géo

<span style="color: #2196F3">**Étant donné**</span> que je refuse le consentement "GPS précis"

<span style="color: #FF9800">**Quand**</span> j'utilise l'application

<span style="color: #4CAF50">**Alors**</span> je peux accéder aux contenus nationaux
<span style="color: #F44336">**Mais**</span> les contenus géolocalisés précis (Ancré, Contextuel) ne sont pas disponibles
<span style="color: #9E9E9E">**Et**</span> les audio-guides nécessitent l'activation du GPS
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai accepté le consentement "Analytique"
<span style="color: #9E9E9E">**Et**</span> que j'utilise l'application depuis 3 mois

<span style="color: #FF9800">**Quand**</span> j'ouvre "Paramètres > Confidentialité > Gérer mes consentements"

<span style="color: #4CAF50">**Alors**</span> je vois la liste de tous mes consentements actuels
<span style="color: #9E9E9E">**Et**</span> je peux révoquer le consentement "Analytique"

<span style="color: #FF9800">**Quand**</span> je révoque le consentement

<span style="color: #4CAF50">**Alors**</span> un nouvel enregistrement est créé avec `accepted = false`
<span style="color: #9E9E9E">**Et**</span> le cookie Matomo est supprimé immédiatement
<span style="color: #9E9E9E">**Et**</span> les analytics sont désactivées à partir de ce moment

---

## 12. Acceptation d'un consentement précédemment refusé

<span style="color: #2196F3">**Étant donné**</span> que j'avais refusé le consentement "GPS précis"

<span style="color: #FF9800">**Quand**</span> j'ouvre "Paramètres > Confidentialité > Gérer mes consentements"
<span style="color: #9E9E9E">**Et**</span> que je clique sur "Activer la géolocalisation précise"

<span style="color: #4CAF50">**Alors**</span> un nouvel enregistrement est créé avec `accepted = true`
<span style="color: #9E9E9E">**Et**</span> la permission OS est demandée si ce n'est pas déjà fait
<span style="color: #9E9E9E">**Et**</span> l'application bascule en mode géolocalisation précise
<span style="color: #9E9E9E">**Et**</span> les contenus géolocalisés deviennent disponibles immédiatement

---

## 13. Export de l'historique des consentements pour audit

<span style="color: #2196F3">**Étant donné**</span> qu'un contrôle CNIL est en cours

<span style="color: #FF9800">**Quand**</span> l'équipe RoadWave exporte l'historique des consentements

<span style="color: #4CAF50">**Alors**</span> 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é |

<span style="color: #9E9E9E">**Et**</span> l'export est au format CSV pour analyse
<span style="color: #9E9E9E">**Et**</span> les données prouvent la conformité RGPD

---

## 14. Conformité recommandations CNIL

<span style="color: #2196F3">**Étant donné**</span> que le système de consentement est implémenté

<span style="color: #FF9800">**Quand**</span> un auditeur CNIL vérifie la conformité

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que l'application web utilise Tarteaucitron.js

<span style="color: #FF9800">**Quand**</span> je consulte les sources JavaScript chargées

<span style="color: #4CAF50">**Alors**</span> le script Tarteaucitron.js est hébergé sur les serveurs RoadWave
<span style="color: #9E9E9E">**Et**</span> aucun script tiers (CDN externe) n'est chargé
<span style="color: #9E9E9E">**Et**</span> le code source de Tarteaucitron.js est vérifiable
<span style="color: #9E9E9E">**Et**</span> aucune donnée n'est envoyée à un tiers lors de l'affichage du banner

---

## 16. Coût de la solution - 0€

<span style="color: #2196F3">**Étant donné**</span> que Tarteaucitron.js est opensource
<span style="color: #9E9E9E">**Et**</span> que PostgreSQL est utilisé pour le backend

<span style="color: #FF9800">**Quand**</span> on calcule le coût de la solution de consentement

<span style="color: #4CAF50">**Alors**</span> le coût est de 0€
<span style="color: #9E9E9E">**Et**</span> la solution est entièrement maîtrisée (self-hosted)
<span style="color: #9E9E9E">**Et**</span> aucune dépendance à un service SaaS tiers

---





<div style="page-break-after: always;"></div>


# Durée de conservation des données et purge automatique
**19 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que le système de purge automatique est actif
## 1. Auditeur inactif depuis 5 ans - suppression automatique

<span style="color: #2196F3">**Étant donné**</span> que je suis un auditeur (sans contenu créé)
<span style="color: #9E9E9E">**Et**</span> que je ne me suis pas connecté depuis le 2020-01-01
<span style="color: #9E9E9E">**Et**</span> que la date actuelle est 2025-01-02 (>5 ans)

<span style="color: #FF9800">**Quand**</span> le job de purge automatique s'exécute

<span style="color: #4CAF50">**Alors**</span> mon compte est automatiquement supprimé
<span style="color: #9E9E9E">**Et**</span> toutes mes données personnelles sont effacées
<span style="color: #9E9E9E">**Et**</span> aucune trace ne subsiste dans la base de données

---

## 2. Créateur avec contenus actifs - conservation indéfinie

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur
<span style="color: #9E9E9E">**Et**</span> que j'ai créé 10 contenus qui reçoivent encore des écoutes
<span style="color: #9E9E9E">**Et**</span> que je ne me suis pas connecté depuis 6 ans

<span style="color: #FF9800">**Quand**</span> le job de purge automatique s'exécute

<span style="color: #4CAF50">**Alors**</span> mon compte n'est pas supprimé
<span style="color: #9E9E9E">**Et**</span> mes données personnelles sont conservées tant que mes contenus sont écoutés
<span style="color: #9E9E9E">**Et**</span> mes contenus continuent d'être diffusés normalement

---

## 3. Créateur inactif sans écoutes - suppression automatique

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur
<span style="color: #9E9E9E">**Et**</span> que j'ai créé 5 contenus
<span style="color: #9E9E9E">**Et**</span> que je ne me suis pas connecté depuis 5 ans (depuis 2020-01-01)
<span style="color: #9E9E9E">**Et**</span> que mes contenus n'ont reçu aucune écoute depuis 2 ans (depuis 2023-01-01)
<span style="color: #9E9E9E">**Et**</span> que la date actuelle est 2025-01-02

<span style="color: #FF9800">**Quand**</span> le job de purge automatique s'exécute

<span style="color: #4CAF50">**Alors**</span> mon compte est automatiquement supprimé
<span style="color: #9E9E9E">**Et**</span> mes contenus sont anonymisés (créateur = "Utilisateur supprimé")
<span style="color: #9E9E9E">**Et**</span> les fichiers audio restent disponibles mais anonymisés

---

## 4. Notifications par email avant purge

<span style="color: #2196F3">**Étant donné**</span> que je suis inactif depuis 4 ans et 9 mois

<span style="color: #FF9800">**Quand**</span> le système détecte que je suis éligible à la purge dans 90 jours

<span style="color: #4CAF50">**Alors**</span> je reçois un email avec le sujet "Votre compte RoadWave sera supprimé dans 90 jours"
<span style="color: #9E9E9E">**Et**</span> l'email contient:
<span style="color: #9E9E9E">**Et**</span> un lien de connexion est inclus dans l'email

---

## 5. Rappels à 90j, 30j et 7j avant suppression

<span style="color: #2196F3">**Étant donné**</span> que je suis éligible à la purge automatique

<span style="color: #FF9800">**Quand**</span> les délais s'écoulent

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> chaque email contient un lien de connexion pour réactiver le compte
<span style="color: #9E9E9E">**Et**</span> les notifications push sont également envoyées si activées

---

## 6. Connexion annule la suppression programmée

<span style="color: #2196F3">**Étant donné**</span> que je suis éligible à la purge dans 15 jours
<span style="color: #9E9E9E">**Et**</span> que j'ai reçu plusieurs emails d'avertissement

<span style="color: #FF9800">**Quand**</span> je me connecte à mon compte

<span style="color: #4CAF50">**Alors**</span> la suppression programmée est annulée immédiatement
<span style="color: #9E9E9E">**Et**</span> le compteur d'inactivité est remis à zéro
<span style="color: #9E9E9E">**Et**</span> je reçois un email de confirmation: "Votre compte a été réactivé"
<span style="color: #9E9E9E">**Et**</span> je peux continuer à utiliser l'application normalement

---

## 7. Exécution quotidienne du job de purge

<span style="color: #2196F3">**Étant donné**</span> que le système est en production

<span style="color: #FF9800">**Quand**</span> on consulte les jobs planifiés

<span style="color: #4CAF50">**Alors**</span> un job "purge_inactive_accounts" est configuré
<span style="color: #9E9E9E">**Et**</span> le job s'exécute tous les jours à 03:00 (heure creuse)
<span style="color: #9E9E9E">**Et**</span> le job identifie les comptes éligibles à la purge
<span style="color: #9E9E9E">**Et**</span> le job traite les suppressions automatiques

---

## 8. Critères d'éligibilité à la purge

<span style="color: #2196F3">**Étant donné**</span> que le job de purge s'exécute

<span style="color: #FF9800">**Quand**</span> le système identifie les comptes éligibles

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> seuls les comptes remplissant tous les critères sont supprimés

---

## 9. Métriques du job de purge

<span style="color: #2196F3">**Étant donné**</span> que le job de purge s'exécute le 2025-01-15

<span style="color: #FF9800">**Quand**</span> le job se termine

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le rapport est loggé pour audit

---

## 10. Contenus de comptes purgés conservés anonymement

<span style="color: #2196F3">**Étant donné**</span> que mon compte créateur est purgé automatiquement

<span style="color: #FF9800">**Quand**</span> la suppression est effective

<span style="color: #4CAF50">**Alors**</span> mes contenus créés sont conservés indéfiniment
<span style="color: #9E9E9E">**Et**</span> les contenus sont anonymisés (créateur = "Utilisateur supprimé")
<span style="color: #9E9E9E">**Et**</span> les fichiers audio restent sur le CDN
<span style="color: #9E9E9E">**Et**</span> les statistiques d'écoute sont préservées
<span style="color: #9E9E9E">**Et**</span> les utilisateurs peuvent toujours écouter mes contenus

---

## 11. Créateur inactif mais contenus populaires - pas de purge

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur inactif depuis 6 ans
<span style="color: #F44336">**Mais**</span> que mes contenus reçoivent 500+ écoutes par mois

<span style="color: #FF9800">**Quand**</span> le job de purge s'exécute

<span style="color: #4CAF50">**Alors**</span> mon compte n'est pas supprimé
<span style="color: #9E9E9E">**Et**</span> je continue de recevoir les emails d'avertissement tous les 6 mois
<span style="color: #9E9E9E">**Et**</span> mes contenus continuent d'être diffusés
<span style="color: #9E9E9E">**Et**</span> je peux me reconnecter à tout moment

---

## 12. Qu'est-ce qu'une "écoute" pour le calcul d'inactivité

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur

<span style="color: #FF9800">**Quand**</span> le système calcule si mes contenus sont "actifs"

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> au moins 1 écoute valide dans les 2 dernières années maintient le compte actif

---

## 13. Conformité principe de minimisation

<span style="color: #2196F3">**Étant donné**</span> que le système de purge automatique est en place

<span style="color: #FF9800">**Quand**</span> un auditeur RGPD vérifie la conformité

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le délai de 5 ans est conforme aux standards de l'industrie

---

## 14. Actions qui réinitialisent le compteur d'inactivité

<span style="color: #2196F3">**Étant donné**</span> que je suis inactif depuis 4 ans

<span style="color: #FF9800">**Quand**</span> 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 |


<span style="color: #4CAF50">**Alors**</span> le compteur d'inactivité est remis à zéro
<span style="color: #9E9E9E">**Et**</span> la suppression programmée est annulée
<span style="color: #9E9E9E">**Et**</span> je ne suis plus éligible à la purge pour 5 ans

---

## 15. Traçabilité des suppressions automatiques

<span style="color: #2196F3">**Étant donné**</span> qu'un compte est supprimé automatiquement

<span style="color: #FF9800">**Quand**</span> la suppression est effective

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le log est conservé 5 ans pour audit RGPD
<span style="color: #9E9E9E">**Et**</span> l'user_id est pseudonymisé pour anonymat

---

## 16. Compte Premium inactif - pas de privilège spécial

<span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur Premium
<span style="color: #9E9E9E">**Et**</span> que je suis inactif depuis 5 ans

<span style="color: #FF9800">**Quand**</span> le job de purge s'exécute

<span style="color: #4CAF50">**Alors**</span> mon compte est supprimé comme un compte gratuit
<span style="color: #9E9E9E">**Et**</span> l'abonnement Premium ne prolonge pas la durée de conservation
<span style="color: #9E9E9E">**Et**</span> aucun remboursement n'est effectué (compte inactif depuis 5 ans)

---

## 17. Compte avec signalements de modération - purge différée

<span style="color: #2196F3">**Étant donné**</span> que je suis éligible à la purge
<span style="color: #F44336">**Mais**</span> que j'ai des signalements de modération en cours

<span style="color: #FF9800">**Quand**</span> le job de purge s'exécute

<span style="color: #4CAF50">**Alors**</span> ma purge est différée de 90 jours
<span style="color: #9E9E9E">**Et**</span> les signalements sont traités en priorité
<span style="color: #9E9E9E">**Et**</span> si les signalements aboutissent à un ban, le compte est supprimé immédiatement
<span style="color: #9E9E9E">**Et**</span> si les signalements sont infondés, la purge automatique reprend son cours

---

## 18. Pourquoi 5 ans d'inactivité

<span style="color: #2196F3">**Étant donné**</span> que le délai de purge est fixé à 5 ans

<span style="color: #FF9800">**Quand**</span> on justifie ce choix

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je consulte les CGU de RoadWave

<span style="color: #FF9800">**Quand**</span> je lis la section "Conservation des données"

<span style="color: #4CAF50">**Alors**</span> la politique de purge automatique est clairement expliquée:
<span style="color: #9E9E9E">**Et**</span> les utilisateurs sont informés dès l'inscription

---





<div style="page-break-after: always;"></div>


# Cookies et analytics avec Matomo self-hosted
**20 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur de l'application web RoadWave
## 1. Cookies strictement nécessaires - pas de consentement requis

<span style="color: #2196F3">**Étant donné**</span> que j'accède à l'application web

<span style="color: #FF9800">**Quand**</span> je me connecte

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ces cookies sont essentiels au fonctionnement de l'application
<span style="color: #9E9E9E">**Et**</span> ils sont exemptés de consentement selon l'article 82 de la loi Informatique et Libertés

---

## 2. Cookie analytique Matomo - consentement requis

<span style="color: #2196F3">**Étant donné**</span> que j'ai accepté le consentement "Analytique"

<span style="color: #FF9800">**Quand**</span> je navigue sur l'application web

<span style="color: #4CAF50">**Alors**</span> le cookie `_pk_id` est déposé
<span style="color: #9E9E9E">**Et**</span> la durée de conservation est de 13 mois
<span style="color: #9E9E9E">**Et**</span> ce cookie sert à Matomo pour analytics
<span style="color: #9E9E9E">**Et**</span> mon IP est automatiquement anonymisée (2 derniers octets)

---

## 3. Refus du consentement analytique - pas de cookie Matomo

<span style="color: #2196F3">**Étant donné**</span> que j'ai refusé le consentement "Analytique"

<span style="color: #FF9800">**Quand**</span> je navigue sur l'application web

<span style="color: #4CAF50">**Alors**</span> aucun cookie `_pk_id` n'est déposé
<span style="color: #9E9E9E">**Et**</span> aucune donnée d'usage n'est collectée
<span style="color: #9E9E9E">**Et**</span> l'application fonctionne normalement sans analytics

---

## 4. Matomo hébergé sur les serveurs RoadWave

<span style="color: #2196F3">**Étant donné**</span> que RoadWave utilise Matomo pour les analytics

<span style="color: #FF9800">**Quand**</span> on analyse l'infrastructure

<span style="color: #4CAF50">**Alors**</span> Matomo est installé sur les serveurs RoadWave (Docker)
<span style="color: #9E9E9E">**Et**</span> aucune donnée n'est envoyée à un service tiers
<span style="color: #9E9E9E">**Et**</span> toutes les données restent dans l'UE
<span style="color: #9E9E9E">**Et**</span> l'accès à Matomo est restreint à l'équipe RoadWave

---

## 5. IP anonymisées automatiquement

<span style="color: #2196F3">**Étant donné**</span> que Matomo collecte des données d'usage

<span style="color: #FF9800">**Quand**</span> une requête est enregistrée

<span style="color: #4CAF50">**Alors**</span> l'adresse IP est automatiquement anonymisée
<span style="color: #9E9E9E">**Et**</span> les 2 derniers octets sont remplacés par des zéros
<span style="color: #9E9E9E">**Et**</span> une IP 192.168.1.100 devient 192.168.0.0
<span style="color: #9E9E9E">**Et**</span> cette anonymisation est irréversible
<span style="color: #9E9E9E">**Et**</span> elle est conforme aux recommandations CNIL

---

## 6. Configuration Matomo conforme RGPD

<span style="color: #2196F3">**Étant donné**</span> que Matomo est configuré pour RoadWave

<span style="color: #FF9800">**Quand**</span> on vérifie les paramètres

<span style="color: #4CAF50">**Alors**</span> 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é |

<span style="color: #9E9E9E">**Et**</span> la configuration est RGPD-compliant

---

## 7. Aucun tracker tiers utilisé

<span style="color: #2196F3">**Étant donné**</span> que j'accède à l'application web

<span style="color: #FF9800">**Quand**</span> j'inspecte les requêtes réseau avec les DevTools

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> toutes les requêtes analytics vont uniquement vers matomo.roadwave.fr

---

## 8. Conformité zéro cookie tiers

<span style="color: #2196F3">**Étant donné**</span> que j'analyse les cookies déposés sur roadwave.fr

<span style="color: #FF9800">**Quand**</span> je consulte la liste des cookies

<span style="color: #4CAF50">**Alors**</span> tous les cookies sont first-party (domaine roadwave.fr)
<span style="color: #9E9E9E">**Et**</span> aucun cookie tiers (third-party) n'est présent
<span style="color: #9E9E9E">**Et**</span> cette politique respecte les recommandations CNIL 2020

---

## 9. Alternative Plausible SaaS (EU-hosted)

<span style="color: #2196F3">**Étant donné**</span> que RoadWave pourrait utiliser Plausible au lieu de Matomo

<span style="color: #FF9800">**Quand**</span> on compare les deux solutions

<span style="color: #4CAF50">**Alors**</span> 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) |

<span style="color: #F44336">**Mais**</span> Matomo self-hosted reste le choix prioritaire (0€, contrôle total)

---

## 10. Aucun transfert de données hors UE

<span style="color: #2196F3">**Étant donné**</span> que Matomo est self-hosted

<span style="color: #FF9800">**Quand**</span> on analyse les flux de données

<span style="color: #4CAF50">**Alors**</span> aucune donnée d'analytics n'est transférée hors de l'UE
<span style="color: #9E9E9E">**Et**</span> les serveurs sont localisés en France
<span style="color: #9E9E9E">**Et**</span> aucun transfert vers les US (pas de Privacy Shield / DPF requis)
<span style="color: #9E9E9E">**Et**</span> la souveraineté des données est garantie

---

## 11. Matomo self-hosted - coût estimé

<span style="color: #2196F3">**Étant donné**</span> que Matomo est hébergé sur l'infrastructure RoadWave

<span style="color: #FF9800">**Quand**</span> on calcule le coût mensuel

<span style="color: #4CAF50">**Alors**</span> 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) |

<span style="color: #9E9E9E">**Et**</span> ce coût est marginal comparé à un SaaS tiers (9-50€/mois)

---

## 12. Respect du signal Do Not Track (DNT)

<span style="color: #2196F3">**Étant donné**</span> que mon navigateur envoie le header "DNT: 1"

<span style="color: #FF9800">**Quand**</span> j'accède à l'application web

<span style="color: #4CAF50">**Alors**</span> Matomo détecte le signal DNT
<span style="color: #9E9E9E">**Et**</span> aucune donnée d'usage n'est collectée
<span style="color: #9E9E9E">**Et**</span> aucun cookie `_pk_id` n'est déposé
<span style="color: #9E9E9E">**Et**</span> l'application fonctionne normalement
<span style="color: #9E9E9E">**Et**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que Matomo collecte des données d'usage

<span style="color: #FF9800">**Quand**</span> les logs atteignent 25 mois d'ancienneté

<span style="color: #4CAF50">**Alors**</span> un job automatique supprime les anciens logs
<span style="color: #9E9E9E">**Et**</span> seules les données agrégées (rapports) sont conservées
<span style="color: #9E9E9E">**Et**</span> les données brutes (logs) sont supprimées définitivement
<span style="color: #9E9E9E">**Et**</span> cette politique respecte le principe de minimisation RGPD

---

## 14. Données collectées par Matomo

<span style="color: #2196F3">**Étant donné**</span> que j'ai accepté le consentement "Analytique"

<span style="color: #FF9800">**Quand**</span> je navigue sur l'application web

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> aucune donnée personnelle identifiable n'est collectée

---

## 15. User ID hashé pour analytics

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté à l'application
<span style="color: #9E9E9E">**Et**</span> que j'ai accepté le consentement "Analytique"

<span style="color: #FF9800">**Quand**</span> Matomo enregistre mes actions

<span style="color: #4CAF50">**Alors**</span> mon user_id est hashé (SHA-256)
<span style="color: #9E9E9E">**Et**</span> le hash est 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
<span style="color: #9E9E9E">**Et**</span> il est impossible de retrouver mon user_id original depuis ce hash
<span style="color: #9E9E9E">**Et**</span> ce processus garantit l'anonymat

---

## 16. Conformité recommandations CNIL sur les cookies

<span style="color: #2196F3">**Étant donné**</span> que RoadWave utilise Matomo self-hosted

<span style="color: #FF9800">**Quand**</span> un auditeur CNIL vérifie la conformité

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que Tarteaucitron.js gère les consentements

<span style="color: #FF9800">**Quand**</span> je personnalise mes consentements

<span style="color: #4CAF50">**Alors**</span> je vois l'option "Analytique (Matomo)"
<span style="color: #9E9E9E">**Et**</span> une description est affichée:
<span style="color: #9E9E9E">**Et**</span> je peux activer ou désactiver Matomo indépendamment
<span style="color: #9E9E9E">**Et**</span> si je désactive, le cookie `_pk_id` est supprimé immédiatement

---

## 18. Analytics sur application mobile

<span style="color: #2196F3">**Étant donné**</span> que j'utilise l'application mobile

<span style="color: #FF9800">**Quand**</span> j'accepte le consentement "Analytique"

<span style="color: #4CAF50">**Alors**</span> l'app utilise le SDK Matomo Mobile
<span style="color: #9E9E9E">**Et**</span> les données sont envoyées à la même instance Matomo self-hosted
<span style="color: #9E9E9E">**Et**</span> les mêmes règles d'anonymisation s'appliquent
<span style="color: #9E9E9E">**Et**</span> aucun SDK tiers (Google Analytics, Firebase) n'est utilisé

---

## 19. Refus analytics sur mobile

<span style="color: #2196F3">**Étant donné**</span> que j'ai refusé le consentement "Analytique" sur mobile

<span style="color: #FF9800">**Quand**</span> j'utilise l'application

<span style="color: #4CAF50">**Alors**</span> aucune donnée d'usage n'est collectée
<span style="color: #9E9E9E">**Et**</span> le SDK Matomo est désactivé
<span style="color: #9E9E9E">**Et**</span> l'application fonctionne normalement sans différence d'UX

---

## 20. Matomo opensource et auditable

<span style="color: #2196F3">**Étant donné**</span> que Matomo est opensource

<span style="color: #FF9800">**Quand**</span> on consulte le code source

<span style="color: #4CAF50">**Alors**</span> le code est disponible publiquement sur GitHub
<span style="color: #9E9E9E">**Et**</span> le code peut être audité par des experts indépendants
<span style="color: #9E9E9E">**Et**</span> aucune backdoor ou collecte cachée n'est possible
<span style="color: #9E9E9E">**Et**</span> cette transparence renforce la confiance utilisateur

---





<div style="page-break-after: always;"></div>


# Mode dégradé avec GeoIP (sans GPS précis)
**20 scénarios** (19 standards, 1 plan)

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un nouvel utilisateur
> <span style="color: #9E9E9E">**Et**</span> que je lance l'application pour la première fois
## 1. 📋 Plan: Trois niveaux de géolocalisation disponibles

<span style="color: #2196F3">**Étant donné**</span> que j'utilise le niveau de géolocalisation "<niveau>"

<span style="color: #FF9800">**Quand**</span> le système détermine ma position

<span style="color: #4CAF50">**Alors**</span> la technologie utilisée est "<technologie>"
<span style="color: #9E9E9E">**Et**</span> les contenus accessibles sont "<contenus>"
<span style="color: #9E9E9E">**Et**</span> le consentement RGPD est "<consentement>"

**📊 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

<span style="color: #2196F3">**Étant donné**</span> que je lance l'application pour la première fois
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas encore accepté le GPS précis

<span style="color: #FF9800">**Quand**</span> l'application démarre

<span style="color: #4CAF50">**Alors**</span> le système utilise automatiquement GeoIP basé sur mon adresse IP
<span style="color: #9E9E9E">**Et**</span> ma position est détectée au niveau ville: "Paris, France"
<span style="color: #9E9E9E">**Et**</span> aucun consentement n'est requis (GeoIP ne collecte pas de données personnelles)
<span style="color: #9E9E9E">**Et**</span> je peux accéder aux contenus régionaux et de ville

---

## 3. Détection de ville avec MaxMind GeoLite2

<span style="color: #2196F3">**Étant donné**</span> que mon adresse IP est 93.184.216.34

<span style="color: #FF9800">**Quand**</span> le système utilise GeoIP MaxMind GeoLite2

<span style="color: #4CAF50">**Alors**</span> ma ville est détectée: "Paris"
<span style="color: #9E9E9E">**Et**</span> ma région est détectée: "Île-de-France"
<span style="color: #9E9E9E">**Et**</span> mon pays est détecté: "France"
<span style="color: #9E9E9E">**Et**</span> la précision est d'environ 80% au niveau ville
<span style="color: #9E9E9E">**Et**</span> aucune coordonnée GPS précise n'est révélée

---

## 4. Banner d'invitation à activer le GPS

<span style="color: #2196F3">**Étant donné**</span> que j'utilise l'application en mode GeoIP

<span style="color: #FF9800">**Quand**</span> je suis sur l'écran principal

<span style="color: #4CAF50">**Alors**</span> un banner discret s'affiche en haut:
<span style="color: #9E9E9E">**Et**</span> le banner n'est pas intrusif (pas de popup modale)
<span style="color: #9E9E9E">**Et**</span> je peux le fermer temporairement avec un bouton X
<span style="color: #9E9E9E">**Et**</span> le banner réapparaît tous les 7 jours si je ne l'active pas

---

## 5. Upgrade volontaire vers GPS depuis le banner

<span style="color: #2196F3">**Étant donné**</span> que le banner d'invitation au GPS est affiché

<span style="color: #FF9800">**Quand**</span> je clique sur "Activer"

<span style="color: #4CAF50">**Alors**</span> un écran de consentement GPS s'affiche
<span style="color: #9E9E9E">**Et**</span> l'écran explique les avantages:
<span style="color: #9E9E9E">**Et**</span> je peux accepter ou refuser
<span style="color: #9E9E9E">**Et**</span> si j'accepte, la permission OS est demandée

---

## 6. Contenus disponibles en mode Pays (aucune géoloc)

<span style="color: #2196F3">**Étant donné**</span> que je n'autorise aucune géolocalisation

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu à me proposer

<span style="color: #4CAF50">**Alors**</span> seuls les contenus "National" sont disponibles
<span style="color: #9E9E9E">**Et**</span> les contenus géolocalisés (Ancré, Contextuel) ne sont pas proposés
<span style="color: #9E9E9E">**Et**</span> je vois un message: "Activez la géolocalisation pour plus de contenu local"

---

## 7. Contenus disponibles en mode Ville (GeoIP)

<span style="color: #2196F3">**Étant donné**</span> que j'utilise le mode GeoIP et que je suis détecté à Paris

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu à me proposer

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> je reçois des recommandations pertinentes pour Paris

---

## 8. Tous contenus disponibles en mode Précis (GPS)

<span style="color: #2196F3">**Étant donné**</span> que j'ai activé la géolocalisation précise

<span style="color: #FF9800">**Quand**</span> le système recherche du contenu à me proposer

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'utilise le mode GeoIP

<span style="color: #FF9800">**Quand**</span> un auditeur CNIL vérifie la conformité

<span style="color: #4CAF50">**Alors**</span> GeoIP n'est pas considéré comme une donnée personnelle
<span style="color: #9E9E9E">**Et**</span> l'adresse IP n'est pas conservée après détection de la ville
<span style="color: #9E9E9E">**Et**</span> seule la ville est stockée (non identifiant)
<span style="color: #9E9E9E">**Et**</span> aucun consentement n'est requis conformément au RGPD

---

## 10. Base de données MaxMind self-hosted

<span style="color: #2196F3">**Étant donné**</span> que RoadWave utilise MaxMind GeoLite2

<span style="color: #FF9800">**Quand**</span> on analyse l'infrastructure

<span style="color: #4CAF50">**Alors**</span> la base de données GeoLite2 est hébergée sur les serveurs RoadWave
<span style="color: #9E9E9E">**Et**</span> aucune requête n'est envoyée à un service tiers
<span style="color: #9E9E9E">**Et**</span> la base de données est mise à jour automatiquement chaque mois
<span style="color: #9E9E9E">**Et**</span> le coût est de 0€ (GeoLite2 est gratuit)

---

## 11. Mise à jour mensuelle de la base GeoIP

<span style="color: #2196F3">**Étant donné**</span> que MaxMind publie des mises à jour mensuelles

<span style="color: #FF9800">**Quand**</span> le 1er du mois arrive

<span style="color: #4CAF50">**Alors**</span> un job automatique télécharge la nouvelle base GeoLite2
<span style="color: #9E9E9E">**Et**</span> la base est mise à jour sans interruption de service
<span style="color: #9E9E9E">**Et**</span> un log est créé pour traçabilité
<span style="color: #9E9E9E">**Et**</span> si la mise à jour échoue, une alerte est envoyée

---

## 12. UX acceptable en mode GeoIP

<span style="color: #2196F3">**Étant donné**</span> que j'utilise le mode GeoIP à Paris

<span style="color: #FF9800">**Quand**</span> je parcours l'application

<span style="color: #4CAF50">**Alors**</span> je peux écouter du contenu pertinent pour Paris et l'Île-de-France
<span style="color: #9E9E9E">**Et**</span> l'expérience est satisfaisante même sans GPS précis
<span style="color: #9E9E9E">**Et**</span> je ne suis pas bloqué dans l'utilisation de l'application
<span style="color: #9E9E9E">**Et**</span> je peux choisir d'activer le GPS quand je le souhaite

---

## 13. Incitation progressive à activer le GPS

<span style="color: #2196F3">**Étant donné**</span> que j'utilise le mode GeoIP depuis 2 semaines
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas activé le GPS

<span style="color: #FF9800">**Quand**</span> je consulte un audio-guide dans les résultats de recherche

<span style="color: #4CAF50">**Alors**</span> un message s'affiche:
<span style="color: #9E9E9E">**Et**</span> si je clique "Plus tard", je peux continuer à utiliser l'app normalement
<span style="color: #9E9E9E">**Et**</span> l'incitation reste douce et non intrusive

---

## 14. Upgrade GeoIP vers GPS

<span style="color: #2196F3">**Étant donné**</span> que j'utilise le mode GeoIP

<span style="color: #FF9800">**Quand**</span> j'active la géolocalisation précise

<span style="color: #4CAF50">**Alors**</span> le système bascule immédiatement en mode GPS
<span style="color: #9E9E9E">**Et**</span> les contenus hyperlocaux deviennent disponibles
<span style="color: #9E9E9E">**Et**</span> mon feed se rafraîchit avec du contenu plus précis
<span style="color: #9E9E9E">**Et**</span> un toast de confirmation s'affiche: "Géolocalisation activée"

---

## 15. Downgrade GPS vers GeoIP

<span style="color: #2196F3">**Étant donné**</span> que j'utilise le mode GPS précis

<span style="color: #FF9800">**Quand**</span> je désactive la géolocalisation dans les paramètres OS

<span style="color: #4CAF50">**Alors**</span> le système bascule automatiquement en mode GeoIP
<span style="color: #9E9E9E">**Et**</span> les contenus hyperlocaux ne sont plus proposés
<span style="color: #9E9E9E">**Et**</span> un banner s'affiche: "Géolocalisation désactivée. Seul le contenu régional est disponible."
<span style="color: #9E9E9E">**Et**</span> l'application continue de fonctionner normalement

---

## 16. Détection automatique au démarrage de l'app

<span style="color: #2196F3">**Étant donné**</span> que j'ouvre l'application

<span style="color: #FF9800">**Quand**</span> l'app vérifie les permissions de géolocalisation

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le mode est appliqué sans interaction utilisateur

---

## 17. Précision acceptable pour la plupart des cas

<span style="color: #2196F3">**Étant donné**</span> que j'habite à Lyon
<span style="color: #9E9E9E">**Et**</span> que mon IP est une IP résidentielle standard

<span style="color: #FF9800">**Quand**</span> le système utilise GeoIP pour me localiser

<span style="color: #4CAF50">**Alors**</span> la ville détectée est "Lyon" (correct à 80%)
<span style="color: #9E9E9E">**Et**</span> dans 20% des cas, la ville peut être légèrement erronée (banlieue proche)
<span style="color: #9E9E9E">**Et**</span> cette précision est suffisante pour proposer du contenu régional pertinent

---

## 18. GeoIP avec VPN ou proxy

<span style="color: #2196F3">**Étant donné**</span> que j'utilise un VPN avec une IP sortante à Paris
<span style="color: #F44336">**Mais**</span> que je suis physiquement à Lyon

<span style="color: #FF9800">**Quand**</span> le système utilise GeoIP

<span style="color: #4CAF50">**Alors**</span> la ville détectée est "Paris" (IP du VPN)
<span style="color: #9E9E9E">**Et**</span> les contenus proposés sont pour Paris
<span style="color: #9E9E9E">**Et**</span> je peux activer le GPS précis pour corriger la localisation

---

## 19. Pas de donnée personnelle collectée avec GeoIP

<span style="color: #2196F3">**Étant donné**</span> que j'utilise le mode GeoIP

<span style="color: #FF9800">**Quand**</span> le système détermine ma ville via mon IP

<span style="color: #4CAF50">**Alors**</span> l'adresse IP n'est pas conservée après détection
<span style="color: #9E9E9E">**Et**</span> seule la ville "Paris" est stockée en base de données
<span style="color: #9E9E9E">**Et**</span> la ville seule n'est pas une donnée personnelle (RGPD)
<span style="color: #9E9E9E">**Et**</span> aucun consentement n'est donc requis

---

## 20. Solution GeoIP gratuite et self-hosted

<span style="color: #2196F3">**Étant donné**</span> que RoadWave utilise MaxMind GeoLite2

<span style="color: #FF9800">**Quand**</span> on calcule le coût de la solution

<span style="color: #4CAF50">**Alors**</span> le coût est de 0€
<span style="color: #9E9E9E">**Et**</span> la solution est opensource
<span style="color: #9E9E9E">**Et**</span> la base de données est hébergée sur les serveurs RoadWave
<span style="color: #9E9E9E">**Et**</span> aucun coût SaaS tiers

---





<div style="page-break-after: always;"></div>


# Portabilité des données (Article 20 RGPD)
**22 scénarios** (21 standards, 1 plan)

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur connecté
> <span style="color: #9E9E9E">**Et**</span> que j'ai utilisé l'application depuis 6 mois
## 1. Demande d'export depuis les paramètres

<span style="color: #2196F3">**Étant donné**</span> que je suis dans "Paramètres > Confidentialité"

<span style="color: #FF9800">**Quand**</span> je clique sur "Exporter mes données"

<span style="color: #4CAF50">**Alors**</span> une page d'information s'affiche expliquant:
<span style="color: #9E9E9E">**Et**</span> un bouton "Confirmer l'export" est disponible

---

## 2. Confirmation et démarrage de l'export

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Confirmer l'export"

<span style="color: #FF9800">**Quand**</span> la demande est validée

<span style="color: #4CAF50">**Alors**</span> un message de confirmation s'affiche:
<span style="color: #9E9E9E">**Et**</span> un worker background démarre la génération de l'export
<span style="color: #9E9E9E">**Et**</span> le statut de l'export est "En cours de génération"
<span style="color: #9E9E9E">**Et**</span> je peux voir le statut dans "Paramètres > Confidentialité > Mes exports"

---

## 3. Contenu de l'archive ZIP

<span style="color: #2196F3">**Étant donné**</span> que mon export est généré

<span style="color: #FF9800">**Quand**</span> je télécharge et ouvre l'archive

<span style="color: #4CAF50">**Alors**</span> l'archive a la structure suivante:
<span style="color: #9E9E9E">**Et**</span> tous les fichiers sont inclus

---

## 4. Contenu du fichier export.json

<span style="color: #2196F3">**Étant donné**</span> que j'ouvre le fichier export.json

<span style="color: #FF9800">**Quand**</span> j'analyse le contenu

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> le JSON est formaté de manière lisible (indentation)
<span style="color: #9E9E9E">**Et**</span> toutes les dates sont au format ISO 8601

---

## 5. Contenu du fichier index.html

<span style="color: #2196F3">**Étant donné**</span> que j'ouvre le fichier index.html dans un navigateur

<span style="color: #FF9800">**Quand**</span> la page se charge

<span style="color: #4CAF50">**Alors**</span> je vois un site web stylé avec navigation
<span style="color: #9E9E9E">**Et**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> la navigation est intuitive (menu latéral)
<span style="color: #9E9E9E">**Et**</span> le design est responsive (mobile/desktop)

---

## 6. Fichiers audio inclus dans l'export

<span style="color: #2196F3">**Étant donné**</span> que j'ai créé 5 contenus audio

<span style="color: #FF9800">**Quand**</span> mon export est généré

<span style="color: #4CAF50">**Alors**</span> le dossier `audio/` contient mes 5 fichiers
<span style="color: #9E9E9E">**Et**</span> les fichiers sont au format Opus original
<span style="color: #9E9E9E">**Et**</span> chaque fichier est nommé: `content-[id].opus`
<span style="color: #9E9E9E">**Et**</span> les fichiers audio correspondent aux métadonnées dans export.json

---

## 7. Fichier README.txt explicatif

<span style="color: #2196F3">**Étant donné**</span> que j'ouvre le fichier README.txt

<span style="color: #FF9800">**Quand**</span> je lis le contenu

<span style="color: #4CAF50">**Alors**</span> le fichier explique:

---

## 8. 📋 Plan: Données de profil exportées

<span style="color: #2196F3">**Étant donné**</span> que mon export est généré

<span style="color: #FF9800">**Quand**</span> j'ouvre export.json et lis la section "profile"

<span style="color: #4CAF50">**Alors**</span> 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é

<span style="color: #2196F3">**Étant donné**</span> que j'ai écouté 150 contenus depuis 6 mois

<span style="color: #FF9800">**Quand**</span> mon export est généré

<span style="color: #4CAF50">**Alors**</span> la section "listening_history" contient 150 entrées
<span style="color: #9E9E9E">**Et**</span> 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] |

<span style="color: #9E9E9E">**Et**</span> les contenus sont triés par date décroissante

---

## 10. Centres d'intérêt exportés

<span style="color: #2196F3">**Étant donné**</span> que mes jauges d'intérêt sont:

    | catégorie | valeur |
    |---|---|
    | Automobile | 78% |
    | Voyage | 65% |
    | Musique | 52% |
    | Politique | 30% |


<span style="color: #FF9800">**Quand**</span> mon export est généré

<span style="color: #4CAF50">**Alors**</span> la section "interest_gauges" contient ces valeurs
<span style="color: #9E9E9E">**Et**</span> chaque jauge indique la date de dernière modification

---

## 11. Historique des consentements exporté

<span style="color: #2196F3">**Étant donné**</span> que j'ai modifié mes consentements plusieurs fois

<span style="color: #FF9800">**Quand**</span> mon export est généré

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> l'historique complet est visible

---

## 12. Génération asynchrone pour éviter timeout

<span style="color: #2196F3">**Étant donné**</span> que j'ai beaucoup de données (500 contenus créés, 10 000 écoutes)

<span style="color: #FF9800">**Quand**</span> je demande un export

<span style="color: #4CAF50">**Alors**</span> la génération se fait en arrière-plan via un worker
<span style="color: #9E9E9E">**Et**</span> la page web ne timeout pas
<span style="color: #9E9E9E">**Et**</span> je peux continuer à utiliser l'application pendant la génération
<span style="color: #9E9E9E">**Et**</span> je reçois un email quand l'export est prêt

---

## 13. Délai de génération conforme RGPD

<span style="color: #2196F3">**Étant donné**</span> que je demande un export le 2025-01-20 à 10:00

<span style="color: #FF9800">**Quand**</span> le worker génère l'export

<span style="color: #4CAF50">**Alors**</span> l'export est disponible maximum 48h plus tard (avant le 2025-01-22 à 10:00)
<span style="color: #9E9E9E">**Et**</span> la plupart des exports sont prêts en moins de 6h
<span style="color: #9E9E9E">**Et**</span> le délai respecte l'article 20 du RGPD

---

## 14. Email de notification avec lien de téléchargement

<span style="color: #2196F3">**Étant donné**</span> que mon export est terminé

<span style="color: #FF9800">**Quand**</span> le worker finalise la génération

<span style="color: #4CAF50">**Alors**</span> je reçois un email avec le sujet "Votre export de données RoadWave est prêt"
<span style="color: #9E9E9E">**Et**</span> l'email contient:
<span style="color: #9E9E9E">**Et**</span> le lien de téléchargement est sécurisé (token unique)

---

## 15. Lien de téléchargement expire après 7 jours

<span style="color: #2196F3">**Étant donné**</span> que mon export a été généré le 2025-01-20
<span style="color: #9E9E9E">**Et**</span> que je reçois le lien de téléchargement

<span style="color: #FF9800">**Quand**</span> j'essaie d'accéder au lien le 2025-01-28 (8 jours plus tard)

<span style="color: #4CAF50">**Alors**</span> le lien est expiré
<span style="color: #9E9E9E">**Et**</span> je reçois un message "Ce lien a expiré. Veuillez demander un nouvel export."
<span style="color: #9E9E9E">**Et**</span> je peux demander un nouvel export si nécessaire

---

## 16. Limite de 1 export par mois

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé un export le 2025-01-15

<span style="color: #FF9800">**Quand**</span> j'essaie de demander un nouvel export le 2025-01-20

<span style="color: #4CAF50">**Alors**</span> je reçois un message d'erreur:
<span style="color: #9E9E9E">**Et**</span> le bouton "Confirmer l'export" est désactivé
<span style="color: #9E9E9E">**Et**</span> la date du prochain export possible est affichée

---

## 17. Nouvel export possible après 1 mois

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé un export le 2025-01-15

<span style="color: #FF9800">**Quand**</span> la date atteint le 2025-02-15

<span style="color: #4CAF50">**Alors**</span> je peux demander un nouvel export
<span style="color: #9E9E9E">**Et**</span> le bouton "Confirmer l'export" est actif
<span style="color: #9E9E9E">**Et**</span> aucune limite ne s'applique

---

## 18. Lien de téléchargement sécurisé avec token unique

<span style="color: #2196F3">**Étant donné**</span> que mon export est prêt

<span style="color: #FF9800">**Quand**</span> je reçois le lien de téléchargement

<span style="color: #4CAF50">**Alors**</span> le lien contient un token unique et non devinable
<span style="color: #9E9E9E">**Et**</span> le format du lien est: `https://roadwave.fr/exports/download/[token_unique]`
<span style="color: #9E9E9E">**Et**</span> le token est valide uniquement pour mon compte
<span style="color: #9E9E9E">**Et**</span> le token expire après 7 jours ou après 3 téléchargements

---

## 19. Vérification de l'authentification avant téléchargement

<span style="color: #2196F3">**Étant donné**</span> que je reçois le lien d'export

<span style="color: #FF9800">**Quand**</span> je clique sur le lien

<span style="color: #4CAF50">**Alors**</span> le système vérifie que je suis connecté
<span style="color: #9E9E9E">**Et**</span> si je ne suis pas connecté, je suis redirigé vers la page de connexion
<span style="color: #9E9E9E">**Et**</span> après connexion, le téléchargement démarre automatiquement
<span style="color: #9E9E9E">**Et**</span> seul le propriétaire du compte peut télécharger l'export

---

## 20. Conformité portabilité des données

<span style="color: #2196F3">**Étant donné**</span> que mon export est généré

<span style="color: #FF9800">**Quand**</span> un auditeur RGPD vérifie la conformité

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que je demande un export de mes données

<span style="color: #FF9800">**Quand**</span> l'export est généré et téléchargé

<span style="color: #4CAF50">**Alors**</span> aucun coût n'est facturé
<span style="color: #9E9E9E">**Et**</span> l'export est entièrement gratuit
<span style="color: #9E9E9E">**Et**</span> aucune inscription Premium n'est requise
<span style="color: #9E9E9E">**Et**</span> le droit à la portabilité est accessible à tous les utilisateurs

---

## 22. Suivi du statut de génération

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé un export

<span style="color: #FF9800">**Quand**</span> j'ouvre "Paramètres > Confidentialité > Mes exports"

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> la date de demande est affichée
<span style="color: #9E9E9E">**Et**</span> la taille estimée de l'archive est visible

---





<div style="page-break-after: always;"></div>


# Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)
**21 scénarios**

---

> **Contexte commun à tous les scénarios**
>
> <span style="color: #2196F3">**Étant donné**</span> que je suis un utilisateur connecté
> <span style="color: #9E9E9E">**Et**</span> que j'ai utilisé l'application depuis plusieurs mois
## 1. Demande de suppression depuis les paramètres

<span style="color: #2196F3">**Étant donné**</span> que je suis dans "Paramètres > Compte"

<span style="color: #FF9800">**Quand**</span> je clique sur "Supprimer mon compte"

<span style="color: #4CAF50">**Alors**</span> une page d'avertissement s'affiche avec le message:
<span style="color: #9E9E9E">**Et**</span> deux boutons sont disponibles: "Annuler" et "Confirmer la suppression"

---

## 2. Confirmation de suppression avec mot de passe

<span style="color: #2196F3">**Étant donné**</span> que je clique sur "Confirmer la suppression"

<span style="color: #FF9800">**Quand**</span> un formulaire de confirmation s'affiche

<span style="color: #4CAF50">**Alors**</span> je dois entrer mon mot de passe pour confirmer
<span style="color: #9E9E9E">**Et**</span> je dois cocher "Je comprends que cette action est définitive"
<span style="color: #9E9E9E">**Et**</span> un captcha peut être requis pour éviter les suppressions automatisées

<span style="color: #FF9800">**Quand**</span> je valide le formulaire

<span style="color: #4CAF50">**Alors**</span> la suppression est initiée

---

## 3. Compte désactivé immédiatement après confirmation

<span style="color: #2196F3">**Étant donné**</span> que j'ai confirmé la suppression de mon compte

<span style="color: #FF9800">**Quand**</span> la demande est traitée

<span style="color: #4CAF50">**Alors**</span> mon compte est désactivé immédiatement
<span style="color: #9E9E9E">**Et**</span> je suis déconnecté de toutes mes sessions
<span style="color: #9E9E9E">**Et**</span> je ne peux plus me reconnecter
<span style="color: #9E9E9E">**Et**</span> si j'essaie de me connecter, je reçois le message:

---

## 4. Contenus cachés pendant le grace period

<span style="color: #2196F3">**Étant donné**</span> que mon compte est en cours de suppression

<span style="color: #FF9800">**Quand**</span> un autre utilisateur recherche mes contenus

<span style="color: #4CAF50">**Alors**</span> mes contenus ne sont plus diffusés dans l'application
<span style="color: #9E9E9E">**Et**</span> mes contenus n'apparaissent plus dans les recherches
<span style="color: #9E9E9E">**Et**</span> mes contenus ne sont plus recommandés
<span style="color: #F44336">**Mais**</span> mes contenus ne sont pas encore supprimés définitivement

---

## 5. Email de confirmation envoyé immédiatement

<span style="color: #2196F3">**Étant donné**</span> que j'ai confirmé la suppression de mon compte

<span style="color: #FF9800">**Quand**</span> la demande est traitée

<span style="color: #4CAF50">**Alors**</span> je reçois un email avec le sujet "Confirmation de suppression de votre compte RoadWave"
<span style="color: #9E9E9E">**Et**</span> l'email contient:
<span style="color: #9E9E9E">**Et**</span> le lien d'annulation est valide 30 jours

---

## 6. Annulation de la suppression dans les 30 jours

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé la suppression de mon compte le 2025-01-20
<span style="color: #9E9E9E">**Et**</span> que je reçois l'email de confirmation

<span style="color: #FF9800">**Quand**</span> je clique sur le lien "Annuler la suppression" le 2025-02-05 (16 jours plus tard)

<span style="color: #4CAF50">**Alors**</span> mon compte est réactivé immédiatement
<span style="color: #9E9E9E">**Et**</span> je peux me reconnecter normalement
<span style="color: #9E9E9E">**Et**</span> mes contenus redeviennent visibles dans l'application
<span style="color: #9E9E9E">**Et**</span> toutes mes données sont restaurées
<span style="color: #9E9E9E">**Et**</span> je reçois un email de confirmation: "Votre compte a été réactivé"

---

## 7. Lien d'annulation expire après 30 jours

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé la suppression de mon compte le 2025-01-20

<span style="color: #FF9800">**Quand**</span> j'essaie de cliquer sur le lien d'annulation le 2025-02-25 (36 jours plus tard)

<span style="color: #4CAF50">**Alors**</span> le lien est expiré
<span style="color: #9E9E9E">**Et**</span> je reçois un message "Ce lien a expiré. Votre compte a été définitivement supprimé."
<span style="color: #9E9E9E">**Et**</span> la suppression effective a déjà eu lieu

---

## 8. Suppression effective sans annulation

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé la suppression de mon compte le 2025-01-20
<span style="color: #9E9E9E">**Et**</span> que je n'ai pas cliqué sur le lien d'annulation

<span style="color: #FF9800">**Quand**</span> la date atteint le 2025-02-19 (30 jours plus tard)

<span style="color: #4CAF50">**Alors**</span> un job automatique exécute la suppression définitive
<span style="color: #9E9E9E">**Et**</span> toutes mes données personnelles sont supprimées

---

## 9. Liste des données supprimées définitivement

<span style="color: #2196F3">**Étant donné**</span> que la suppression effective est exécutée

<span style="color: #FF9800">**Quand**</span> le job de suppression se termine

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ces suppressions sont irréversibles

---

## 10. Anonymisation des contenus créés

<span style="color: #2196F3">**Étant donné**</span> que j'ai créé 10 contenus audio

<span style="color: #FF9800">**Quand**</span> la suppression effective est exécutée

<span style="color: #4CAF50">**Alors**</span> mes contenus audio restent disponibles dans l'application
<span style="color: #9E9E9E">**Et**</span> le nom du créateur devient "Utilisateur supprimé"
<span style="color: #9E9E9E">**Et**</span> mon pseudo n'est plus visible
<span style="color: #9E9E9E">**Et**</span> les métadonnées (titre, description, tags, géolocalisation) sont conservées
<span style="color: #9E9E9E">**Et**</span> les fichiers audio restent sur le CDN
<span style="color: #9E9E9E">**Et**</span> les statistiques d'écoute sont conservées

---

## 11. Justification de l'anonymisation (intérêt légitime)

<span style="color: #2196F3">**Étant donné**</span> que mes contenus sont conservés anonymement

<span style="color: #FF9800">**Quand**</span> un auditeur RGPD vérifie la conformité

<span style="color: #4CAF50">**Alors**</span> la conservation est justifiée par l'intérêt légitime de la communauté
<span style="color: #9E9E9E">**Et**</span> les contenus ne contiennent plus de données personnelles identifiables
<span style="color: #9E9E9E">**Et**</span> la suppression complète nuirait à l'expérience des autres utilisateurs
<span style="color: #9E9E9E">**Et**</span> cette pratique est conforme au RGPD si anonymisation réelle

---

## 12. Contenu anonymisé visible pour les autres utilisateurs

<span style="color: #2196F3">**Étant donné**</span> que mon compte a été supprimé
<span style="color: #9E9E9E">**Et**</span> que mes contenus ont été anonymisés

<span style="color: #FF9800">**Quand**</span> un utilisateur consulte un de mes anciens contenus

<span style="color: #4CAF50">**Alors**</span> le créateur affiché est "Utilisateur supprimé"
<span style="color: #9E9E9E">**Et**</span> le profil du créateur n'est plus accessible
<span style="color: #9E9E9E">**Et**</span> le contenu reste écoutable normalement
<span style="color: #9E9E9E">**Et**</span> les likes et statistiques sont conservés

---

## 13. Suppression de mes likes avec conservation des compteurs

<span style="color: #2196F3">**Étant donné**</span> que j'avais liké 50 contenus

<span style="color: #FF9800">**Quand**</span> la suppression effective est exécutée

<span style="color: #4CAF50">**Alors**</span> mes likes sont supprimés de la base de données
<span style="color: #F44336">**Mais**</span> les compteurs de likes sur les contenus sont préservés
<span style="color: #9E9E9E">**Et**</span> les créateurs ne perdent pas leurs statistiques
<span style="color: #9E9E9E">**Et**</span> seule la relation "user X a liké content Y" est supprimée

---

## 14. Suppression de mes abonnements

<span style="color: #2196F3">**Étant donné**</span> que je suivais 20 créateurs

<span style="color: #FF9800">**Quand**</span> la suppression effective est exécutée

<span style="color: #4CAF50">**Alors**</span> mes abonnements sont supprimés
<span style="color: #9E9E9E">**Et**</span> les compteurs d'abonnés des créateurs sont décrémentés de 1
<span style="color: #9E9E9E">**Et**</span> les créateurs ne reçoivent pas de notification de désabonnement

---

## 15. Révocation de tous les tokens immédiatement

<span style="color: #2196F3">**Étant donné**</span> que je suis connecté sur 3 appareils (mobile, tablette, web)

<span style="color: #FF9800">**Quand**</span> je demande la suppression de mon compte

<span style="color: #4CAF50">**Alors**</span> tous mes tokens d'authentification sont révoqués immédiatement
<span style="color: #9E9E9E">**Et**</span> je suis déconnecté de tous mes appareils
<span style="color: #9E9E9E">**Et**</span> toute tentative de reconnexion échoue

---

## 16. Rappels par email pendant le grace period

<span style="color: #2196F3">**Étant donné**</span> que j'ai demandé la suppression de mon compte le 2025-01-20

<span style="color: #FF9800">**Quand**</span> le grace period s'écoule

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> chaque email contient le lien d'annulation

---

## 17. Conformité droit à l'effacement

<span style="color: #2196F3">**Étant donné**</span> que la suppression de mon compte est complète

<span style="color: #FF9800">**Quand**</span> un auditeur RGPD vérifie la conformité

<span style="color: #4CAF50">**Alors**</span> 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

<span style="color: #2196F3">**Étant donné**</span> que j'ai un abonnement Premium actif

<span style="color: #FF9800">**Quand**</span> je demande la suppression de mon compte

<span style="color: #4CAF50">**Alors**</span> mon abonnement est annulé immédiatement
<span style="color: #9E9E9E">**Et**</span> aucun remboursement n'est effectué (conformément aux CGV)
<span style="color: #9E9E9E">**Et**</span> je reçois un email de confirmation d'annulation de l'abonnement
<span style="color: #9E9E9E">**Et**</span> le reste du processus de suppression se déroule normalement

---

## 19. Suppression d'un compte créateur avec revenus en attente

<span style="color: #2196F3">**Étant donné**</span> que je suis un créateur avec 75€ de revenus en attente de paiement

<span style="color: #FF9800">**Quand**</span> je demande la suppression de mon compte

<span style="color: #4CAF50">**Alors**</span> un message m'informe:
<span style="color: #9E9E9E">**Et**</span> je peux choisir "Recevoir le paiement et attendre" ou "Renoncer au paiement"
<span style="color: #9E9E9E">**Et**</span> si je choisis "Recevoir le paiement", la suppression est repoussée de 7 jours

---

## 20. Suppression avec signalements de modération en cours

<span style="color: #2196F3">**Étant donné**</span> que j'ai 2 signalements en cours de traitement

<span style="color: #FF9800">**Quand**</span> je demande la suppression de mon compte

<span style="color: #4CAF50">**Alors**</span> les signalements sont automatiquement clôturés
<span style="color: #9E9E9E">**Et**</span> les contenus signalés sont masqués immédiatement
<span style="color: #9E9E9E">**Et**</span> aucune sanction n'est appliquée (compte déjà en suppression)

---

## 21. Log de la suppression pour traçabilité

<span style="color: #2196F3">**Étant donné**</span> que la suppression effective est exécutée

<span style="color: #FF9800">**Quand**</span> le job de suppression se termine

<span style="color: #4CAF50">**Alors**</span> 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 |

<span style="color: #9E9E9E">**Et**</span> ce log est conservé 5 ans pour audit RGPD
<span style="color: #9E9E9E">**Et**</span> l'user_id est pseudonymisé pour anonymat

---





<div style="page-break-after: always;"></div>

