30529 lines
1.7 MiB
30529 lines
1.7 MiB
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Documentation RoadWave</title>
|
||
<style>
|
||
@page {
|
||
size: A4;
|
||
margin: 2cm;
|
||
@bottom-center {
|
||
content: counter(page);
|
||
}
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||
font-size: 11pt;
|
||
line-height: 1.6;
|
||
color: #333;
|
||
max-width: 100%;
|
||
}
|
||
|
||
h1 {
|
||
color: #1a237e;
|
||
border-bottom: 3px solid #3f51b5;
|
||
padding-bottom: 10px;
|
||
page-break-after: avoid;
|
||
font-size: 24pt;
|
||
}
|
||
|
||
h2 {
|
||
color: #303f9f;
|
||
border-bottom: 1px solid #7986cb;
|
||
padding-bottom: 5px;
|
||
margin-top: 30px;
|
||
page-break-after: avoid;
|
||
font-size: 16pt;
|
||
}
|
||
|
||
h3 {
|
||
color: #3949ab;
|
||
page-break-after: avoid;
|
||
font-size: 13pt;
|
||
}
|
||
|
||
/* Couleurs Gherkin */
|
||
span[style*="#2196F3"] { color: #1565c0 !important; font-weight: bold; } /* Étant donné - Bleu */
|
||
span[style*="#FF9800"] { color: #e65100 !important; font-weight: bold; } /* Quand - Orange */
|
||
span[style*="#4CAF50"] { color: #2e7d32 !important; font-weight: bold; } /* Alors - Vert */
|
||
span[style*="#9E9E9E"] { color: #616161 !important; } /* Et - Gris */
|
||
span[style*="#F44336"] { color: #c62828 !important; font-weight: bold; } /* Mais - Rouge */
|
||
|
||
table {
|
||
border-collapse: collapse;
|
||
width: 100%;
|
||
margin: 15px 0;
|
||
font-size: 10pt;
|
||
}
|
||
|
||
th, td {
|
||
border: 1px solid #ddd;
|
||
padding: 8px;
|
||
text-align: left;
|
||
}
|
||
|
||
th {
|
||
background-color: #3f51b5;
|
||
color: white;
|
||
}
|
||
|
||
tr:nth-child(even) {
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
blockquote {
|
||
border-left: 4px solid #3f51b5;
|
||
margin: 15px 0;
|
||
padding: 10px 20px;
|
||
background-color: #e8eaf6;
|
||
font-style: italic;
|
||
}
|
||
|
||
code {
|
||
background-color: #f5f5f5;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
font-family: "Fira Code", "Consolas", monospace;
|
||
font-size: 10pt;
|
||
}
|
||
|
||
pre {
|
||
background-color: #263238;
|
||
color: #aed581;
|
||
padding: 15px;
|
||
border-radius: 5px;
|
||
overflow-x: auto;
|
||
font-size: 9pt;
|
||
}
|
||
|
||
pre code {
|
||
background-color: transparent;
|
||
padding: 0;
|
||
color: inherit;
|
||
}
|
||
|
||
hr {
|
||
border: none;
|
||
border-top: 1px solid #e0e0e0;
|
||
margin: 30px 0;
|
||
}
|
||
|
||
a {
|
||
color: #1976d2;
|
||
text-decoration: none;
|
||
}
|
||
|
||
/* Info box (contexte) */
|
||
.info-box {
|
||
background-color: #e3f2fd;
|
||
border-left: 4px solid #2196f3;
|
||
padding: 15px;
|
||
margin: 15px 0;
|
||
}
|
||
|
||
/* Page breaks */
|
||
.page-break {
|
||
page-break-after: always;
|
||
}
|
||
|
||
/* Cover page */
|
||
.cover {
|
||
text-align: center;
|
||
padding-top: 200px;
|
||
}
|
||
|
||
.cover h1 {
|
||
font-size: 36pt;
|
||
border: none;
|
||
}
|
||
|
||
/* TOC */
|
||
.toc {
|
||
page-break-after: always;
|
||
}
|
||
|
||
.toc ul {
|
||
list-style: none;
|
||
padding-left: 20px;
|
||
}
|
||
|
||
.toc li {
|
||
margin: 5px 0;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1 id="documentation-roadwave">Documentation RoadWave</h1>
|
||
<hr />
|
||
<h2 id="table-des-matieres">Table des matières</h2>
|
||
<ul>
|
||
<li><a href="#roadwave">RoadWave</a></li>
|
||
<li><a href="#roadwave---architecture-technique">RoadWave - Architecture Technique</a></li>
|
||
<li><a href="#adr-001--langage-backend">ADR-001 : Langage Backend</a></li>
|
||
<li><a href="#adr-002--protocole-de-streaming">ADR-002 : Protocole de Streaming</a></li>
|
||
<li><a href="#adr-003--codec-audio">ADR-003 : Codec Audio</a></li>
|
||
<li><a href="#adr-004--cdn">ADR-004 : CDN</a></li>
|
||
<li><a href="#adr-005--base-de-données">ADR-005 : Base de Données</a></li>
|
||
<li><a href="#adr-006--chiffrement">ADR-006 : Chiffrement</a></li>
|
||
<li><a href="#adr-007--tests-et-spécifications-exécutables">ADR-007 : Tests et Spécifications Exécutables</a></li>
|
||
<li><a href="#adr-008--authentification-et-gestion-didentité">ADR-008 : Authentification et Gestion d'Identité</a></li>
|
||
<li><a href="#adr-009--solution-de-paiement-et-gestion-des-abonnements">ADR-009 : Solution de Paiement et Gestion des Abonnements</a></li>
|
||
<li><a href="#adr-010--commandes-au-volant-et-likes">ADR-010 : Commandes au volant et likes</a></li>
|
||
<li><a href="#adr-011--conformité-app-stores-et-plateformes-auto">ADR-011 : Conformité App Stores et Plateformes Auto</a></li>
|
||
<li><a href="#adr-012--architecture-backend">ADR-012 : Architecture Backend</a></li>
|
||
<li><a href="#adr-013--orm-et-accès-données">ADR-013 : ORM et Accès Données</a></li>
|
||
<li><a href="#adr-014--frontend-mobile">ADR-014 : Frontend Mobile</a></li>
|
||
<li><a href="#adr-015--stratégie-tests">ADR-015 : Stratégie Tests</a></li>
|
||
<li><a href="#règles-métier-roadwave">Règles métier RoadWave</a></li>
|
||
<li><a href="#annexe--fonctionnalités-reportées-post-mvp">Annexe : Fonctionnalités reportées Post-MVP</a></li>
|
||
<li><a href="#audio-guides-multi-séquences-pour-piétons">Audio-guides multi-séquences pour piétons</a></li>
|
||
<li><a href="#impact-des-abonnements-sur-lalgorithme">Impact des abonnements sur l'algorithme</a></li>
|
||
<li><a href="#limites-dabonnements-et-désabonnement">Limites d'abonnements et désabonnement</a></li>
|
||
<li><a href="#notifications-contextuelles-selon-le-mode-de-déplacement">Notifications contextuelles selon le mode de déplacement</a></li>
|
||
<li><a href="#création-daudio-guide-multi-séquences">Création d'audio-guide multi-séquences</a></li>
|
||
<li><a href="#intégration-audio-guides-avec-autres-fonctionnalités">Intégration audio-guides avec autres fonctionnalités</a></li>
|
||
<li><a href="#audio-guide-mode-piéton-navigation-manuelle">Audio-guide mode piéton (navigation manuelle)</a></li>
|
||
<li><a href="#audio-guide-mode-voiture-gps-automatique">Audio-guide mode voiture (GPS automatique)</a></li>
|
||
<li><a href="#audio-guides-modes-vélo-et-transport">Audio-guides modes vélo et transport</a></li>
|
||
<li><a href="#audio-guides-premium-et-monétisation">Audio-guides Premium et monétisation</a></li>
|
||
<li><a href="#sauvegarde-et-reprise-de-progression-audio-guide">Sauvegarde et reprise de progression audio-guide</a></li>
|
||
<li><a href="#classification-des-contenus-par-âge">Classification des contenus par âge</a></li>
|
||
<li><a href="#connexion-utilisateur">Connexion utilisateur</a></li>
|
||
<li><a href="#inscription-utilisateur">Inscription utilisateur</a></li>
|
||
<li><a href="#récupération-de-compte">Récupération de compte</a></li>
|
||
<li><a href="#gestion-des-sessions-et-tokens">Gestion des sessions et tokens</a></li>
|
||
<li><a href="#authentification-à-deux-facteurs-2fa">Authentification à deux facteurs (2FA)</a></li>
|
||
<li><a href="#vérification-demail">Vérification d'email</a></li>
|
||
<li><a href="#métadonnées-et-publication-de-contenu">Métadonnées et publication de contenu</a></li>
|
||
<li><a href="#modification-et-suppression-de-contenu">Modification et suppression de contenu</a></li>
|
||
<li><a href="#upload-et-encodage-de-contenu-audio">Upload et encodage de contenu audio</a></li>
|
||
<li><a href="#validation-des-3-premiers-contenus">Validation des 3 premiers contenus</a></li>
|
||
<li><a href="#élargissement-automatique-de-zone-quand-aucun-contenu-nest-disponible">Élargissement automatique de zone quand aucun contenu n'est disponible</a></li>
|
||
<li><a href="#gestion-dun-contenu-supprimé-pendant-lécoute">Gestion d'un contenu supprimé pendant l'écoute</a></li>
|
||
<li><a href="#mode-dégradé-sans-géolocalisation">Mode dégradé sans géolocalisation</a></li>
|
||
<li><a href="#gestion-de-la-perte-de-réseau-et-buffering-adaptatif">Gestion de la perte de réseau et buffering adaptatif</a></li>
|
||
<li><a href="#tests-bdd---documentation-des-fonctionnalités">Tests BDD - Documentation des fonctionnalités</a></li>
|
||
<li><a href="#pas-de-dégradation-temporelle-des-jauges">Pas de dégradation temporelle des jauges</a></li>
|
||
<li><a href="#évolution-des-jauges-dintérêt">Évolution des jauges d'intérêt</a></li>
|
||
<li><a href="#jauge-initiale-et-cold-start">Jauge initiale et cold start</a></li>
|
||
<li><a href="#synchronisation-actions-offline">Synchronisation actions offline</a></li>
|
||
<li><a href="#téléchargement-de-contenus-offline">Téléchargement de contenus offline</a></li>
|
||
<li><a href="#validité-et-renouvellement-contenus-offline">Validité et renouvellement contenus offline</a></li>
|
||
<li><a href="#modération-préventive">Modération préventive</a></li>
|
||
<li><a href="#sanctions-et-notifications-de-modération">Sanctions et notifications de modération</a></li>
|
||
<li><a href="#signalement-de-contenu-inapproprié">Signalement de contenu inapproprié</a></li>
|
||
<li><a href="#traitement-des-signalements-par-lia-et-les-modérateurs">Traitement des signalements par l'IA et les modérateurs</a></li>
|
||
<li><a href="#conditions-dactivation-de-la-monétisation">Conditions d'activation de la monétisation</a></li>
|
||
<li><a href="#contenus-premium-exclusifs">Contenus Premium exclusifs</a></li>
|
||
<li><a href="#désactivation-et-suspension-monétisation">Désactivation et suspension monétisation</a></li>
|
||
<li><a href="#kyc-et-inscription-à-la-monétisation">KYC et inscription à la monétisation</a></li>
|
||
<li><a href="#obligations-fiscales">Obligations fiscales</a></li>
|
||
<li><a href="#paiement-des-créateurs">Paiement des créateurs</a></li>
|
||
<li><a href="#sources-de-revenus-créateurs">Sources de revenus créateurs</a></li>
|
||
<li><a href="#actions-complémentaires-à-larrêt">Actions complémentaires à l'arrêt</a></li>
|
||
<li><a href="#commande-précédent">Commande "Précédent"</a></li>
|
||
<li><a href="#commandes-vocales-carplay-et-android-auto">Commandes vocales CarPlay et Android Auto</a></li>
|
||
<li><a href="#commandes-au-volant-et-interactions-simplifiées">Commandes au volant et interactions simplifiées</a></li>
|
||
<li><a href="#file-dattente-et-commande-suivant">File d'attente et commande "Suivant"</a></li>
|
||
<li><a href="#lecture-en-boucle-et-enchaînement-automatique">Lecture en boucle et enchaînement automatique</a></li>
|
||
<li><a href="#partage-de-contenu">Partage de contenu</a></li>
|
||
<li><a href="#avantages-premium">Avantages Premium</a></li>
|
||
<li><a href="#gestion-abonnement-premium">Gestion abonnement Premium</a></li>
|
||
<li><a href="#multi-devices-et-détection-simultanée">Multi-devices et détection simultanée</a></li>
|
||
<li><a href="#offre-et-tarification-premium">Offre et tarification Premium</a></li>
|
||
<li><a href="#profil-créateur">Profil créateur</a></li>
|
||
<li><a href="#création-de-campagnes-publicitaires">Création de campagnes publicitaires</a></li>
|
||
<li><a href="#caractéristiques-et-facturation-des-publicités">Caractéristiques et facturation des publicités</a></li>
|
||
<li><a href="#gestion-du-budget-et-alertes-publicitaires">Gestion du budget et alertes publicitaires</a></li>
|
||
<li><a href="#insertion-et-fréquence-des-publicités">Insertion et fréquence des publicités</a></li>
|
||
<li><a href="#métriques-dengagement-et-dashboard-publicitaire">Métriques d'engagement et dashboard publicitaire</a></li>
|
||
<li><a href="#validation-et-modération-des-publicités">Validation et modération des publicités</a></li>
|
||
<li><a href="#architecture-technique-radio-live">Architecture technique radio live</a></li>
|
||
<li><a href="#arrêt-du-live">Arrêt du live</a></li>
|
||
<li><a href="#comportement-auditeur-pendant-un-live">Comportement auditeur pendant un live</a></li>
|
||
<li><a href="#démarrage-dun-live">Démarrage d'un live</a></li>
|
||
<li><a href="#recherche-de-contenu">Recherche de contenu</a></li>
|
||
<li><a href="#classification-de-géo-pertinence-des-contenus">Classification de géo-pertinence des contenus</a></li>
|
||
<li><a href="#gestion-du-contenu-politique-mvp-simplifié">Gestion du contenu politique (MVP simplifié)</a></li>
|
||
<li><a href="#contenus-géolocalisés-en-mode-voiture">Contenus géolocalisés en mode voiture</a></li>
|
||
<li><a href="#gestion-de-lhistorique-et-reproposition">Gestion de l'historique et reproposition</a></li>
|
||
<li><a href="#médias-traditionnels-sur-roadwave">Médias traditionnels sur RoadWave</a></li>
|
||
<li><a href="#mode-kids-pour-utilisateurs-13-15-ans">Mode Kids pour utilisateurs 13-15 ans</a></li>
|
||
<li><a href="#paramétrabilité-admin-et-ab-testing">Paramétrabilité admin et A/B testing</a></li>
|
||
<li><a href="#paramétrabilité-utilisateur-et-profils">Paramétrabilité utilisateur et profils</a></li>
|
||
<li><a href="#formule-de-scoring-et-recommandation">Formule de scoring et recommandation</a></li>
|
||
<li><a href="#anonymisation-des-données-gps-après-24h">Anonymisation des données GPS après 24h</a></li>
|
||
<li><a href="#conformité-administrative-rgpd-registre-breach-dpo">Conformité administrative RGPD (Registre, Breach, DPO)</a></li>
|
||
<li><a href="#gestion-du-consentement-rgpd">Gestion du consentement RGPD</a></li>
|
||
<li><a href="#durée-de-conservation-des-données-et-purge-automatique">Durée de conservation des données et purge automatique</a></li>
|
||
<li><a href="#cookies-et-analytics-avec-matomo-self-hosted">Cookies et analytics avec Matomo self-hosted</a></li>
|
||
<li><a href="#mode-dégradé-avec-geoip-sans-gps-précis">Mode dégradé avec GeoIP (sans GPS précis)</a></li>
|
||
<li><a href="#portabilité-des-données-article-20-rgpd">Portabilité des données (Article 20 RGPD)</a></li>
|
||
<li><a href="#suppression-du-compte-utilisateur-article-17-rgpd---droit-à-leffacement">Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)</a></li>
|
||
</ul>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="roadwave">RoadWave</h1>
|
||
<p>Réseau social audio géolocalisé pour les usagers de la route.</p>
|
||
<h2 id="concept">Concept</h2>
|
||
<p>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.</p>
|
||
<p>Le contenu est diffusé en fonction de la position géographique de l'utilisateur et de ses centres d'intérêt.</p>
|
||
<hr />
|
||
<h2 id="cas-dusage">Cas d'usage</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Utilisateur</th>
|
||
<th>Scénario</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Conducteur</strong></td>
|
||
<td>É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</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Routier</strong></td>
|
||
<td>Écoute podcasts et radios live pendant ses trajets longue distance</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Touriste à pied</strong></td>
|
||
<td>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é</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Commerçant</strong></td>
|
||
<td>Diffuse une publicité audio ciblée GPS devant son commerce</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Passionné auto</strong></td>
|
||
<td>Découvre du contenu automobile près de circuits ou concessionnaires</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Habitant local</strong></td>
|
||
<td>Partage anecdotes ou bons plans géolocalisés dans son quartier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Média traditionnel</strong></td>
|
||
<td>Le Monde, Le Parisien diffusent actualités géolocalisées ou nationales</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="utilisateurs">Utilisateurs</h2>
|
||
<p>Tout utilisateur peut écouter et créer du contenu (rôle flexible).</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Rôle</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Auditeur</strong></td>
|
||
<td>Écoute, like, s'abonne à des créateurs, signale des contenus</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Créateur</strong></td>
|
||
<td>Publie du contenu audio géolocalisé (individus, médias traditionnels)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Publicitaire</strong></td>
|
||
<td>Diffuse des publicités ciblées géographiquement</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Modérateur</strong></td>
|
||
<td>Valide et modère les contenus signalés</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="types-de-contenu">Types de contenu</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Contenu court</strong></td>
|
||
<td>Audio de quelques secondes à quelques minutes</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Podcast</strong></td>
|
||
<td>Épisodes plus longs, séries thématiques</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Radio live</strong></td>
|
||
<td>Diffusion en direct avec synchronisation approximative entre auditeurs</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Audio-guide</strong></td>
|
||
<td>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</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="geolocalisation">Géolocalisation</h2>
|
||
<p>Le créateur définit la zone de diffusion de son contenu :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Niveau</th>
|
||
<th>Portée</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Point GPS</strong></td>
|
||
<td>Rayon précis autour d'une coordonnée</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Ville</strong></td>
|
||
<td>Diffusion dans une ville</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Département</strong></td>
|
||
<td>Diffusion départementale</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Région</strong></td>
|
||
<td>Diffusion régionale</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Pays</strong></td>
|
||
<td>Diffusion nationale</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Priorité de diffusion</strong> : plus la zone est précise, plus le contenu a de chances d'être diffusé (GPS > ville > département > région > pays).</p>
|
||
<hr />
|
||
<h2 id="algorithme-de-recommandation">Algorithme de recommandation</h2>
|
||
<p>Le contenu proposé est calculé via un <strong>score combiné</strong> :</p>
|
||
<ul>
|
||
<li><strong>Proximité géographique</strong> : distance entre l'utilisateur et la zone du contenu</li>
|
||
<li><strong>Pertinence des intérêts</strong> : correspondance avec les centres d'intérêt de l'utilisateur</li>
|
||
</ul>
|
||
<p>Lorsque plusieurs contenus sont disponibles dans une zone, <strong>seul le plus pertinent est diffusé</strong>.</p>
|
||
<hr />
|
||
<h2 id="centres-dinteret">Centres d'intérêt</h2>
|
||
<p>Chaque utilisateur possède des <strong>jauges d'intérêt</strong> qui évoluent dynamiquement :</p>
|
||
<h3 id="categories">Catégories</h3>
|
||
<ul>
|
||
<li>Automobile</li>
|
||
<li>Voyage</li>
|
||
<li>Famille</li>
|
||
<li>Amour</li>
|
||
<li>Musique</li>
|
||
<li>Économie</li>
|
||
<li>Cryptomonnaie</li>
|
||
<li>Politique</li>
|
||
<li><em>... (extensible)</em></li>
|
||
</ul>
|
||
<h3 id="evolution-des-jauges">Évolution des jauges</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Effet</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Temps d'écoute long</td>
|
||
<td>Augmente la jauge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Like</td>
|
||
<td>Augmente la jauge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Abonnement</td>
|
||
<td>Augmente fortement la jauge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Skip rapide</td>
|
||
<td>Diminue la jauge</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>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.</p>
|
||
<hr />
|
||
<h2 id="interactions">Interactions</h2>
|
||
<h3 id="commandes-au-volant-conduite">Commandes au volant (conduite)</h3>
|
||
<p>Interactions simplifiées pour sécurité routière maximale :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Commande</th>
|
||
<th>Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Suivant</strong></td>
|
||
<td>Passer au contenu suivant</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Précédent</strong></td>
|
||
<td>Revenir au contenu précédent</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Play/Pause</strong></td>
|
||
<td>Mettre en pause / reprendre la lecture</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Like automatique</strong> : 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)</p>
|
||
<blockquote>
|
||
<p>Voir <a href="#docs/adr/010-commandes-volant">ADR-010</a> pour les détails techniques</p>
|
||
</blockquote>
|
||
<h3 id="actions-complementaires-application-a-larret">Actions complémentaires (application à l'arrêt)</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Like explicite</strong></td>
|
||
<td>Bouton cœur pour liker manuellement</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>S'abonner</strong></td>
|
||
<td>Suivre un créateur</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Signaler</strong></td>
|
||
<td>Signaler un contenu inapproprié</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Unlike</strong></td>
|
||
<td>Retirer un like</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="publicites">Publicités</h2>
|
||
<ul>
|
||
<li>Insertion <strong>entre deux contenus</strong> uniquement (jamais d'interruption)</li>
|
||
<li>Ciblage géographique : point GPS, ville, département, région ou national</li>
|
||
<li>Interface dédiée pour les publicitaires</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="radio-live">Radio live</h2>
|
||
<ul>
|
||
<li>Diffusion en direct par des créateurs</li>
|
||
<li><strong>Buffering</strong> pour garantir une écoute fluide</li>
|
||
<li><strong>Synchronisation approximative</strong> entre les auditeurs (quelques secondes de décalage possible)</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="moderation">Modération</h2>
|
||
<p>Approche hybride combinant participation communautaire, IA et modérateurs dédiés.</p>
|
||
<h3 id="contenus-prohibes">Contenus prohibés</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Catégorie</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Haine et violence</strong></td>
|
||
<td>Incitation à la haine, violence, discrimination</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenu sexuel</strong></td>
|
||
<td>Pornographie ou contenu sexuellement explicite</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Illégalité</strong></td>
|
||
<td>Apologie du terrorisme, actes criminels</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Désinformation dangereuse</strong></td>
|
||
<td>Fausses informations sur la santé, sécurité routière</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Harcèlement</strong></td>
|
||
<td>Menaces, intimidation, doxxing</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Droits d'auteur</strong></td>
|
||
<td>Violation de propriété intellectuelle</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Fraude</strong></td>
|
||
<td>Arnaques, escroqueries</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="roles-de-moderation">Rôles de modération</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Rôle</th>
|
||
<th>Capacités</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Auditeur lambda</strong></td>
|
||
<td>Signaler un contenu (1 clic)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Auditeur de confiance</strong></td>
|
||
<td>Signalements priorisés après historique positif</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Modérateur junior</strong></td>
|
||
<td>Traiter signalements simples (spam, contenu évident)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Modérateur senior</strong></td>
|
||
<td>Cas complexes, appels, décisions de ban</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Admin modération</strong></td>
|
||
<td>Définir les règles, superviser l'équipe</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="flux-de-moderation">Flux de modération</h3>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<h3 id="outils-de-moderation-automatique">Outils de modération automatique</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Outil</th>
|
||
<th>Fonction</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Transcription audio</strong></td>
|
||
<td>Conversion automatique en texte pour analyse</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Analyse vocale IA</strong></td>
|
||
<td>Détection de ton agressif, cris, insultes</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Empreinte audio</strong></td>
|
||
<td>Détection de contenus déjà modérés (réupload)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Détection droits d'auteur</strong></td>
|
||
<td>Identification automatique de musique protégée</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Filtrage mots-clés</strong></td>
|
||
<td>Liste noire de termes inappropriés</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="moderation-preventive">Modération préventive</h3>
|
||
<ul>
|
||
<li><strong>Nouveaux créateurs</strong> : validation manuelle des 3 premiers contenus</li>
|
||
<li><strong>Score de confiance</strong> : évolution selon l'historique du créateur</li>
|
||
<li><strong>Publicités</strong> : validation manuelle obligatoire avant diffusion</li>
|
||
</ul>
|
||
<h3 id="systeme-de-strikes">Système de strikes</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Strike</th>
|
||
<th>Sanction</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Strike 1</strong></td>
|
||
<td>Avertissement + formation modération</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Strike 2</strong></td>
|
||
<td>Suspension 7 jours + contenu supprimé</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Strike 3</strong></td>
|
||
<td>Suspension 30 jours</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Strike 4</strong></td>
|
||
<td>Ban définitif</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<ul>
|
||
<li><strong>Réhabilitation</strong> : -1 strike tous les 6 mois sans incident</li>
|
||
</ul>
|
||
<h3 id="priorisation-des-signalements">Priorisation des signalements</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Priorité</th>
|
||
<th>Type de contenu</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>CRITIQUE</strong></td>
|
||
<td>Violence, suicide, mise en danger immédiate</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>HAUTE</strong></td>
|
||
<td>Harcèlement, haine, désinformation</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>MOYENNE</strong></td>
|
||
<td>Spam, contenu inapproprié</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>BASSE</strong></td>
|
||
<td>Qualité audio, tags incorrects</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="transparence-et-recours">Transparence et recours</h3>
|
||
<ul>
|
||
<li><strong>Notification explicite</strong> lors de suppression (raison détaillée)</li>
|
||
<li><strong>Processus d'appel</strong> : le créateur peut contester une décision</li>
|
||
<li><strong>Délai de traitement</strong> : 48-72h pour les recours</li>
|
||
<li><strong>Historique</strong> : tableau de bord des sanctions pour le créateur</li>
|
||
</ul>
|
||
<h3 id="moderation-communautaire">Modération communautaire</h3>
|
||
<ul>
|
||
<li><strong>Utilisateurs de confiance</strong> : signalements priorisés après historique positif</li>
|
||
<li><strong>Récompenses</strong> : badges, réduction premium pour signalements pertinents</li>
|
||
<li>Lutte contre les signalements abusifs (sanctions possibles)</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="modele-economique">Modèle économique</h2>
|
||
<h3 id="offres">Offres</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Formule</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Gratuit</strong></td>
|
||
<td>Accès complet avec publicités entre les contenus</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Premium</strong></td>
|
||
<td>Sans publicité + accès aux contenus exclusifs</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="monetisation-createurs">Monétisation créateurs</h3>
|
||
<ul>
|
||
<li><strong>Partage des revenus pub</strong> : rémunération basée sur le nombre d'écoutes</li>
|
||
<li><strong>Pourboires</strong> : les auditeurs peuvent faire des dons aux créateurs</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="conformite-rgpd">Conformité RGPD</h2>
|
||
<h3 id="donnees-collectees">Données collectées</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Donnée</th>
|
||
<th>Finalité</th>
|
||
<th>Base légale</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Position GPS</strong></td>
|
||
<td>Diffusion de contenu géolocalisé</td>
|
||
<td>Consentement</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Historique d'écoute</strong></td>
|
||
<td>Personnalisation des recommandations</td>
|
||
<td>Intérêt légitime</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Centres d'intérêt</strong></td>
|
||
<td>Algorithme de recommandation</td>
|
||
<td>Consentement</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Identité créateur</strong></td>
|
||
<td>Publication de contenu</td>
|
||
<td>Exécution du contrat</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="droits-des-utilisateurs">Droits des utilisateurs</h3>
|
||
<ul>
|
||
<li><strong>Accès</strong> : consulter toutes ses données personnelles</li>
|
||
<li><strong>Rectification</strong> : modifier ses informations</li>
|
||
<li><strong>Suppression</strong> : supprimer son compte et toutes ses données</li>
|
||
<li><strong>Portabilité</strong> : exporter ses données dans un format standard</li>
|
||
<li><strong>Opposition</strong> : désactiver le profilage publicitaire</li>
|
||
</ul>
|
||
<h3 id="mesures-techniques">Mesures techniques</h3>
|
||
<ul>
|
||
<li>Consentement explicite requis pour la géolocalisation</li>
|
||
<li>Anonymisation des données de localisation après 24h (sauf historique personnel)</li>
|
||
<li>Possibilité d'utiliser l'app en mode dégradé (sans géolocalisation précise)</li>
|
||
<li>Données hébergées dans l'UE</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="roadwave-architecture-technique">RoadWave - Architecture Technique</h1>
|
||
<blockquote>
|
||
<p>Les décisions techniques sont documentées dans <a href="#docs/adr/">docs/adr/</a></p>
|
||
</blockquote>
|
||
<h2 id="stack-technologique">Stack Technologique</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Composant</th>
|
||
<th>Technologie</th>
|
||
<th>ADR</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Backend</strong></td>
|
||
<td>Go + Fiber</td>
|
||
<td><a href="#docs/adr/001-langage-backend">ADR-001</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Architecture Backend</strong></td>
|
||
<td>Monolithe Modulaire</td>
|
||
<td><a href="#docs/adr/012-architecture-backend">ADR-012</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Authentification</strong></td>
|
||
<td>Zitadel</td>
|
||
<td><a href="#docs/adr/008-authentification">ADR-008</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Streaming</strong></td>
|
||
<td>HLS</td>
|
||
<td><a href="#docs/adr/002-protocole-streaming">ADR-002</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Codec</strong></td>
|
||
<td>Opus</td>
|
||
<td><a href="#docs/adr/003-codec-audio">ADR-003</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>CDN</strong></td>
|
||
<td>Bunny CDN</td>
|
||
<td><a href="#docs/adr/004-cdn">ADR-004</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Base de données</strong></td>
|
||
<td>PostgreSQL + PostGIS</td>
|
||
<td><a href="#docs/adr/005-base-de-donnees">ADR-005</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>ORM/Accès données</strong></td>
|
||
<td>sqlc</td>
|
||
<td><a href="#docs/adr/013-orm-acces-donnees">ADR-013</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Cache</strong></td>
|
||
<td>Redis Cluster</td>
|
||
<td><a href="#docs/adr/005-base-de-donnees">ADR-005</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Chiffrement</strong></td>
|
||
<td>TLS 1.3</td>
|
||
<td><a href="#docs/adr/006-chiffrement">ADR-006</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Live</strong></td>
|
||
<td>WebRTC</td>
|
||
<td><a href="#docs/adr/002-protocole-streaming">ADR-002</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Frontend Mobile</strong></td>
|
||
<td>Flutter</td>
|
||
<td><a href="#docs/adr/014-frontend-mobile">ADR-014</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tests</strong></td>
|
||
<td>Testify + Godog (Gherkin)</td>
|
||
<td><a href="#docs/adr/015-strategie-tests">ADR-015</a>, <a href="#docs/adr/007-tests-bdd">ADR-007</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Paiements</strong></td>
|
||
<td>Mangopay</td>
|
||
<td><a href="#docs/adr/009-solution-paiement">ADR-009</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Commandes volant</strong></td>
|
||
<td>Like automatique</td>
|
||
<td><a href="#docs/adr/010-commandes-volant">ADR-010</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Conformité stores</strong></td>
|
||
<td>CarPlay, Android Auto, App/Play Store</td>
|
||
<td><a href="#docs/adr/011-conformite-stores-carplay-android-auto">ADR-011</a></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="streaming-audio">Streaming Audio</h2>
|
||
<h3 id="protocole-hls-http-live-streaming">Protocole : HLS (HTTP Live Streaming)</h3>
|
||
<ul>
|
||
<li>Fonctionne à travers firewalls et réseaux mobiles instables</li>
|
||
<li>Cache CDN natif (réduction des coûts)</li>
|
||
<li>Bitrate adaptatif automatique (tunnels, zones rurales)</li>
|
||
<li>Support natif iOS/Android</li>
|
||
</ul>
|
||
<h3 id="codec-opus">Codec : Opus</h3>
|
||
<p>Optimisé pour la voix en environnement bruyant (voiture).</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Qualité</th>
|
||
<th>Bitrate</th>
|
||
<th>Usage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Basse</td>
|
||
<td>24 kbps</td>
|
||
<td>2G/Edge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Standard</td>
|
||
<td>48 kbps</td>
|
||
<td>3G</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Haute</td>
|
||
<td>64 kbps</td>
|
||
<td>4G/5G</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Fallback AAC-LC pour appareils legacy.</p>
|
||
<h3 id="buffering-adaptatif">Buffering Adaptatif</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Réseau</th>
|
||
<th>Buffer min</th>
|
||
<th>Buffer cible</th>
|
||
<th>Buffer max</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>WiFi</td>
|
||
<td>5s</td>
|
||
<td>30s</td>
|
||
<td>120s</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4G/5G</td>
|
||
<td>10s</td>
|
||
<td>45s</td>
|
||
<td>120s</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3G</td>
|
||
<td>30s</td>
|
||
<td>90s</td>
|
||
<td>300s</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="securite">Sécurité</h2>
|
||
<h3 id="chiffrement">Chiffrement</h3>
|
||
<ul>
|
||
<li><strong>TLS 1.3</strong> sur tous les endpoints (overhead ~1-2%)</li>
|
||
<li><strong>DTLS-SRTP</strong> pour WebRTC (radio live)</li>
|
||
<li>Pas de DRM initialement (ajout si licences l'exigent)</li>
|
||
</ul>
|
||
<h3 id="authentification">Authentification</h3>
|
||
<ul>
|
||
<li><strong>Zitadel</strong> (self-hosted) pour IAM</li>
|
||
<li>JWT validation locale (zitadel-go SDK)</li>
|
||
<li>OAuth2 PKCE pour mobile (iOS/Android)</li>
|
||
<li>MFA et passkeys disponibles</li>
|
||
<li>Rate limiting par IP et par utilisateur (Nginx + Zitadel)</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="base-de-donnees">Base de Données</h2>
|
||
<h3 id="postgresql-postgis">PostgreSQL + PostGIS</h3>
|
||
<pre><code class="language-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;
|
||
</code></pre>
|
||
<h3 id="redis-geospatial-cache">Redis Geospatial (Cache)</h3>
|
||
<pre><code>GEOADD contents:geo longitude latitude content_id
|
||
GEORADIUS contents:geo user_lon user_lat 50 km WITHDIST COUNT 20 ASC
|
||
</code></pre>
|
||
<p>TTL cache : 5 minutes (le contenu ne bouge pas).</p>
|
||
<hr />
|
||
<h2 id="architecture-services">Architecture Services</h2>
|
||
<pre><code>┌─────────────────┐
|
||
│ 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 │
|
||
└───────┘ └───────────┘
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="scaling-10m-utilisateurs">Scaling 10M Utilisateurs</h2>
|
||
<h3 id="strategie-par-phase">Stratégie par phase</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Phase</th>
|
||
<th>Utilisateurs</th>
|
||
<th>Infra</th>
|
||
<th>Coût estimé</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>MVP</td>
|
||
<td>0-100K</td>
|
||
<td>Monolithe Go, PostgreSQL managé + Zitadel, Bunny CDN/Storage</td>
|
||
<td>50-150€/mois</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Growth</td>
|
||
<td>100K-1M</td>
|
||
<td>Kubernetes managé, replicas multi-région</td>
|
||
<td>2-5K€/mois</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Scale</td>
|
||
<td>1M-10M</td>
|
||
<td>Multi-région, Nginx origin shield, Bunny CDN</td>
|
||
<td>20-50K€/mois</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="metriques-cibles">Métriques cibles</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Métrique</th>
|
||
<th>Objectif</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Latence API p99</td>
|
||
<td>< 100ms</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Temps de démarrage audio</td>
|
||
<td>< 3s</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Disponibilité</td>
|
||
<td>99.9%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Connexions/serveur</td>
|
||
<td>100K+</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="points-de-vigilance">Points de vigilance</h2>
|
||
<ol>
|
||
<li><strong>Buffering mobile</strong> : Pré-chargement agressif avant tunnels (détection GPS)</li>
|
||
<li><strong>Handoff réseau</strong> : Buffer suffisant pour survivre aux changements de cellule</li>
|
||
<li><strong>Mode offline</strong> : Téléchargement complet sur WiFi</li>
|
||
<li><strong>Bande passante</strong> : 48 kbps Opus = ~20 MB/heure (faible consommation data)</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="pourquoi-pas-udp-brut">Pourquoi pas UDP brut ?</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>UDP</th>
|
||
<th>HLS/TCP</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Latence minimale</td>
|
||
<td>Latence acceptable (5-30s)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Problèmes NAT/firewall</td>
|
||
<td>Passe partout</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Perte de paquets = artefacts</td>
|
||
<td>Retransmission automatique</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Pas de cache CDN</td>
|
||
<td>Cache CDN = économies</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Complexité++</td>
|
||
<td>Standard de l'industrie</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Pour du contenu non-interactif (podcasts, audio-guides), la latence HLS est acceptable. WebRTC réservé à la radio live uniquement.</p>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-001-langage-backend">ADR-001 : Langage Backend</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-17</p>
|
||
<h2 id="contexte">Contexte</h2>
|
||
<p>RoadWave doit gérer 10M d'utilisateurs avec des connexions concurrentes massives pour le streaming audio géolocalisé.</p>
|
||
<h2 id="decision">Décision</h2>
|
||
<p><strong>Go</strong> avec le framework <strong>Fiber</strong>.</p>
|
||
<h2 id="alternatives-considerees">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Option</th>
|
||
<th>Performance</th>
|
||
<th>Simplicité</th>
|
||
<th>Écosystème</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Go + Fiber</strong></td>
|
||
<td>1M+ conn/serveur</td>
|
||
<td>Élevée</td>
|
||
<td>Excellent cloud-native</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Rust + Tokio</td>
|
||
<td>2M+ conn/serveur</td>
|
||
<td>Faible</td>
|
||
<td>Bon</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Node.js</td>
|
||
<td>100-500K conn</td>
|
||
<td>Élevée</td>
|
||
<td>Excellent</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Elixir/Phoenix</td>
|
||
<td>2M+ conn</td>
|
||
<td>Moyenne</td>
|
||
<td>Bon temps réel</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification">Justification</h2>
|
||
<ul>
|
||
<li><strong>Performance</strong> : Go gère 1M+ connexions par serveur avec ~10KB/connexion</li>
|
||
<li><strong>Simplicité</strong> : Syntaxe claire, compilation rapide, facile à recruter</li>
|
||
<li><strong>Écosystème</strong> : First-class Kubernetes, tooling natif (profiling, race detection)</li>
|
||
<li><strong>Équilibre</strong> : Meilleur compromis performance/simplicité pour une startup</li>
|
||
</ul>
|
||
<h2 id="consequences">Conséquences</h2>
|
||
<ul>
|
||
<li>Formation équipe sur Go si nécessaire</li>
|
||
<li>Utilisation des bibliothèques : Fiber (HTTP), pgx (PostgreSQL), go-redis</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-002-protocole-de-streaming">ADR-002 : Protocole de Streaming</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-17</p>
|
||
<h2 id="contexte_1">Contexte</h2>
|
||
<p>Streaming audio vers des utilisateurs mobiles en voiture, avec réseaux instables (tunnels, zones rurales, handoff cellulaire).</p>
|
||
<h2 id="decision_1">Décision</h2>
|
||
<p><strong>HLS</strong> (HTTP Live Streaming) pour le contenu à la demande.
|
||
<strong>WebRTC</strong> réservé à la radio live.</p>
|
||
<h2 id="alternatives-considerees_1">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Option</th>
|
||
<th>Latence</th>
|
||
<th>Fiabilité mobile</th>
|
||
<th>Cache CDN</th>
|
||
<th>Complexité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>HLS</strong></td>
|
||
<td>5-30s</td>
|
||
<td>Excellente</td>
|
||
<td>Oui</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>DASH</td>
|
||
<td>5-30s</td>
|
||
<td>Bonne</td>
|
||
<td>Oui</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>WebRTC</td>
|
||
<td><500ms</td>
|
||
<td>Moyenne</td>
|
||
<td>Non</td>
|
||
<td>Élevée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>UDP brut</td>
|
||
<td>Minimale</td>
|
||
<td>Faible</td>
|
||
<td>Non</td>
|
||
<td>Très élevée</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_1">Justification</h2>
|
||
<ul>
|
||
<li><strong>Réseaux mobiles</strong> : HLS gère les coupures et changements de cellule nativement</li>
|
||
<li><strong>Cache CDN</strong> : Segments .ts cachables = réduction des coûts</li>
|
||
<li><strong>Compatibilité</strong> : Support natif iOS/Android</li>
|
||
<li><strong>Bitrate adaptatif</strong> : Ajustement automatique selon la qualité réseau</li>
|
||
</ul>
|
||
<h2 id="pourquoi-pas-udp">Pourquoi pas UDP ?</h2>
|
||
<ul>
|
||
<li>Problèmes NAT/firewall sur réseaux mobiles</li>
|
||
<li>Perte de paquets = artefacts audio</li>
|
||
<li>Impossible à cacher sur CDN</li>
|
||
<li>Complexité sans bénéfice pour du contenu non-interactif</li>
|
||
</ul>
|
||
<h2 id="consequences_1">Conséquences</h2>
|
||
<ul>
|
||
<li>Latence de 5-30s acceptable pour podcasts/audio-guides</li>
|
||
<li>WebRTC à implémenter séparément pour la radio live</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-003-codec-audio">ADR-003 : Codec Audio</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-17</p>
|
||
<h2 id="contexte_2">Contexte</h2>
|
||
<p>Audio diffusé en voiture : environnement bruyant, réseau mobile variable, qualité studio non nécessaire.</p>
|
||
<h2 id="decision_2">Décision</h2>
|
||
<p><strong>Opus</strong> comme codec principal, <strong>AAC-LC</strong> en fallback.</p>
|
||
<h2 id="profils-dencodage">Profils d'encodage</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Qualité</th>
|
||
<th>Bitrate</th>
|
||
<th>Usage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Basse</td>
|
||
<td>24 kbps</td>
|
||
<td>2G/Edge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Standard</td>
|
||
<td>48 kbps</td>
|
||
<td>3G</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Haute</td>
|
||
<td>64 kbps</td>
|
||
<td>4G/5G</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="alternatives-considerees_2">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Codec</th>
|
||
<th>Bitrate</th>
|
||
<th>Qualité voix</th>
|
||
<th>Support mobile</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Opus</strong></td>
|
||
<td>24-64 kbps</td>
|
||
<td>Excellente</td>
|
||
<td>Android natif, iOS via libs</td>
|
||
</tr>
|
||
<tr>
|
||
<td>AAC-LC</td>
|
||
<td>64-128 kbps</td>
|
||
<td>Bonne</td>
|
||
<td>Universel</td>
|
||
</tr>
|
||
<tr>
|
||
<td>AAC-HE v2</td>
|
||
<td>32-64 kbps</td>
|
||
<td>Très bonne</td>
|
||
<td>Bon</td>
|
||
</tr>
|
||
<tr>
|
||
<td>MP3</td>
|
||
<td>128-320 kbps</td>
|
||
<td>Correcte</td>
|
||
<td>Universel (legacy)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_2">Justification</h2>
|
||
<ul>
|
||
<li><strong>Environnement bruyant</strong> : Opus intègre des algorithmes de résilience au bruit</li>
|
||
<li><strong>Bande passante</strong> : 48 kbps Opus ≈ qualité 96 kbps AAC pour la voix</li>
|
||
<li><strong>Consommation data</strong> : ~20 MB/heure à 48 kbps</li>
|
||
<li><strong>Latence</strong> : 2.5-60ms, idéal pour streaming adaptatif</li>
|
||
</ul>
|
||
<h2 id="consequences_2">Conséquences</h2>
|
||
<ul>
|
||
<li>Fallback AAC-LC pour appareils legacy</li>
|
||
<li>Pipeline d'encodage à prévoir côté ingestion</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-004-cdn">ADR-004 : CDN</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-17</p>
|
||
<h2 id="contexte_3">Contexte</h2>
|
||
<p>Distribution audio HLS à 10M d'utilisateurs, besoin de performance, coût maîtrisé, et indépendance vis-à-vis des géants du cloud.</p>
|
||
<h2 id="decision_3">Décision</h2>
|
||
<p><strong>Bunny CDN</strong> comme CDN principal.</p>
|
||
<h2 id="alternatives-considerees_3">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Solution</th>
|
||
<th>Coût/mois (100TB)</th>
|
||
<th>Setup</th>
|
||
<th>Performance</th>
|
||
<th>Dépendance</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Bunny CDN</strong></td>
|
||
<td>~1 000€</td>
|
||
<td>15 min</td>
|
||
<td>Très bon</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Cloudflare</td>
|
||
<td>0-5 000€</td>
|
||
<td>5 min</td>
|
||
<td>Excellent</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>CloudFront</td>
|
||
<td>~9 750€</td>
|
||
<td>1h</td>
|
||
<td>Excellent</td>
|
||
<td>Forte (AWS)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Fastly</td>
|
||
<td>~12-20 000€</td>
|
||
<td>2h</td>
|
||
<td>Exceptionnel</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Nginx self-hosted</td>
|
||
<td>~2-5 000€</td>
|
||
<td>1 jour</td>
|
||
<td>Excellent</td>
|
||
<td>Aucune</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_3">Justification</h2>
|
||
<ul>
|
||
<li><strong>Coût</strong> : 10x moins cher que CloudFront</li>
|
||
<li><strong>HLS natif</strong> : Support optimisé pour le streaming</li>
|
||
<li><strong>Simplicité</strong> : Setup en 15 minutes, zéro maintenance</li>
|
||
<li><strong>Européen</strong> : Conforme RGPD, 114 PoPs</li>
|
||
<li><strong>Pas de lock-in</strong> : Migration facile si besoin</li>
|
||
</ul>
|
||
<h2 id="evolution-prevue">Évolution prévue</h2>
|
||
<ol>
|
||
<li><strong>Phase 1</strong> (0-1M users) : Bunny CDN seul</li>
|
||
<li><strong>Phase 2</strong> (1-5M users) : Ajout Nginx origin shield si nécessaire</li>
|
||
<li><strong>Phase 3</strong> (5M+) : Évaluation multi-CDN</li>
|
||
</ol>
|
||
<h2 id="consequences_3">Conséquences</h2>
|
||
<ul>
|
||
<li>Configuration des règles de cache pour <code>.m3u8</code> (TTL court) et <code>.ts</code> (TTL long)</li>
|
||
<li>Token authentication pour protéger les segments</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-005-base-de-donnees">ADR-005 : Base de Données</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-17</p>
|
||
<h2 id="contexte_4">Contexte</h2>
|
||
<p>Requêtes géolocalisées intensives (contenus à proximité), données utilisateurs, historiques d'écoute.</p>
|
||
<h2 id="decision_4">Décision</h2>
|
||
<ul>
|
||
<li><strong>PostgreSQL + PostGIS</strong> : Données persistantes et requêtes géospatiales</li>
|
||
<li><strong>Redis Cluster</strong> : Cache géolocalisation et sessions</li>
|
||
</ul>
|
||
<h2 id="architecture">Architecture</h2>
|
||
<pre><code>Requête → Redis Cache → [HIT] → Réponse
|
||
↓
|
||
[MISS]
|
||
↓
|
||
PostGIS → Cache → Réponse
|
||
</code></pre>
|
||
<h2 id="alternatives-considerees_4">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Usage</th>
|
||
<th>Option choisie</th>
|
||
<th>Alternatives</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Données utilisateurs</td>
|
||
<td>PostgreSQL</td>
|
||
<td>MySQL, MongoDB</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Géolocalisation</td>
|
||
<td>PostGIS</td>
|
||
<td>MongoDB Geo, Elasticsearch</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Cache</td>
|
||
<td>Redis</td>
|
||
<td>Memcached, KeyDB</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Analytics (futur)</td>
|
||
<td>ClickHouse</td>
|
||
<td>TimescaleDB</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_4">Justification</h2>
|
||
<h3 id="postgresql-postgis_1">PostgreSQL + PostGIS</h3>
|
||
<ul>
|
||
<li>Requêtes géospatiales complexes et précises</li>
|
||
<li>Index GIST pour performance</li>
|
||
<li>ACID, fiabilité éprouvée</li>
|
||
<li>Écosystème mature</li>
|
||
</ul>
|
||
<h3 id="redis">Redis</h3>
|
||
<ul>
|
||
<li>Cache géo natif (<code>GEORADIUS</code>) : 100K+ requêtes/sec</li>
|
||
<li>Sessions utilisateurs</li>
|
||
<li>Pub/sub pour temps réel</li>
|
||
</ul>
|
||
<h2 id="exemple-de-requete">Exemple de requête</h2>
|
||
<pre><code class="language-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;
|
||
</code></pre>
|
||
<h2 id="consequences_4">Conséquences</h2>
|
||
<ul>
|
||
<li>TTL cache Redis : 5 minutes (le contenu géolocalisé ne bouge pas)</li>
|
||
<li>Index GIST sur colonnes géométriques</li>
|
||
<li>Réplication read replicas pour scaling lecture</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-006-chiffrement">ADR-006 : Chiffrement</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-17</p>
|
||
<h2 id="contexte_5">Contexte</h2>
|
||
<p>Streaming audio sur réseaux mobiles, conformité RGPD, protection du contenu.</p>
|
||
<h2 id="decision_5">Décision</h2>
|
||
<ul>
|
||
<li><strong>TLS 1.3</strong> sur tous les endpoints</li>
|
||
<li><strong>DTLS-SRTP</strong> pour WebRTC (radio live)</li>
|
||
<li>Pas de DRM au lancement</li>
|
||
</ul>
|
||
<h2 id="alternatives-considerees_5">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Méthode</th>
|
||
<th>Overhead</th>
|
||
<th>Usage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>TLS 1.3</strong></td>
|
||
<td>~1-2% CPU</td>
|
||
<td>HTTPS streaming</td>
|
||
</tr>
|
||
<tr>
|
||
<td>DTLS-SRTP</td>
|
||
<td>~3-5% CPU</td>
|
||
<td>WebRTC temps réel</td>
|
||
</tr>
|
||
<tr>
|
||
<td>AES-128-CBC</td>
|
||
<td>Minimal</td>
|
||
<td>Chiffrement segments HLS</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Widevine/FairPlay</td>
|
||
<td>Modéré</td>
|
||
<td>DRM (si licences l'exigent)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_5">Justification</h2>
|
||
<h3 id="pourquoi-chiffrer">Pourquoi chiffrer ?</h3>
|
||
<ul>
|
||
<li><strong>RGPD</strong> : Protection des données utilisateurs obligatoire</li>
|
||
<li><strong>Confiance</strong> : Standard attendu en 2025</li>
|
||
<li><strong>Intégrité</strong> : Empêche injection de contenu par opérateurs</li>
|
||
<li><strong>Overhead minimal</strong> : TLS 1.3 optimisé, impact négligeable</li>
|
||
</ul>
|
||
<h3 id="pourquoi-pas-de-drm">Pourquoi pas de DRM ?</h3>
|
||
<ul>
|
||
<li>Contenu généré par utilisateurs (pas de licences)</li>
|
||
<li>Complexité et coût d'intégration Widevine/FairPlay</li>
|
||
<li>À reconsidérer si partenariats avec labels/éditeurs</li>
|
||
</ul>
|
||
<h2 id="consequences_5">Conséquences</h2>
|
||
<ul>
|
||
<li>Certificats SSL gérés par Bunny CDN ou Let's Encrypt</li>
|
||
<li>Configuration TLS 1.3 sur Nginx/API</li>
|
||
<li>DTLS-SRTP à implémenter pour le module radio live</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-007-tests-et-specifications-executables">ADR-007 : Tests et Spécifications Exécutables</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-17</p>
|
||
<h2 id="contexte_6">Contexte</h2>
|
||
<p>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.</p>
|
||
<h2 id="decision_6">Décision</h2>
|
||
<p><strong>Gherkin</strong> pour les spécifications avec <strong>Godog</strong> comme runner de tests.</p>
|
||
<h2 id="alternatives-considerees_6">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Option</th>
|
||
<th>Lisibilité</th>
|
||
<th>Intégration Go</th>
|
||
<th>Maintenance</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Gherkin + Godog</strong></td>
|
||
<td>Excellente</td>
|
||
<td>Native</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Gauge (Markdown)</td>
|
||
<td>Bonne</td>
|
||
<td>Plugin</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Tests Go natifs</td>
|
||
<td>Faible (devs only)</td>
|
||
<td>Native</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Concordion</td>
|
||
<td>Bonne</td>
|
||
<td>Java-centric</td>
|
||
<td>Élevée</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_6">Justification</h2>
|
||
<ul>
|
||
<li><strong>Living Documentation</strong> : Les fichiers <code>.feature</code> servent de documentation ET de tests</li>
|
||
<li><strong>Accessibilité</strong> : Syntaxe Given/When/Then lisible par PO, devs, testeurs</li>
|
||
<li><strong>Cohérence stack</strong> : Godog est le standard BDD pour Go</li>
|
||
<li><strong>CI/CD</strong> : Intégration simple dans les pipelines</li>
|
||
</ul>
|
||
<h2 id="structure">Structure</h2>
|
||
<pre><code>features/
|
||
├── recommendation/
|
||
│ ├── geolocalisation.feature
|
||
│ └── interets.feature
|
||
├── streaming/
|
||
│ ├── lecture.feature
|
||
│ └── buffering.feature
|
||
├── moderation/
|
||
│ └── signalement.feature
|
||
└── steps/
|
||
└── steps.go
|
||
</code></pre>
|
||
<h2 id="exemple">Exemple</h2>
|
||
<pre><code class="language-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
|
||
</code></pre>
|
||
<h2 id="consequences_6">Conséquences</h2>
|
||
<ul>
|
||
<li>Dépendance : <code>github.com/cucumber/godog</code></li>
|
||
<li>Les use cases du README doivent être traduits en <code>.feature</code></li>
|
||
<li>CI exécute <code>godog run</code> avant chaque merge</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-008-authentification-et-gestion-didentite">ADR-008 : Authentification et Gestion d'Identité</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-18</p>
|
||
<h2 id="contexte_7">Contexte</h2>
|
||
<p>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.</p>
|
||
<h2 id="decision_7">Décision</h2>
|
||
<p><strong>Zitadel</strong> (self-hosted) pour l'IAM avec validation JWT locale côté API Go.</p>
|
||
<h2 id="alternatives-considerees_7">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Solution</th>
|
||
<th>Coût (10M users)</th>
|
||
<th>Performance</th>
|
||
<th>Simplicité</th>
|
||
<th>Intégration Go</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Zitadel</strong></td>
|
||
<td>200-500€/mois</td>
|
||
<td>Excellente</td>
|
||
<td>Élevée</td>
|
||
<td>SDK natif</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Supabase Auth</td>
|
||
<td>32K€/mois</td>
|
||
<td>Excellente</td>
|
||
<td>Élevée</td>
|
||
<td>REST API</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Keycloak</td>
|
||
<td>200-800€/mois</td>
|
||
<td>Bonne</td>
|
||
<td>Faible</td>
|
||
<td>Lib tierce</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Auth0</td>
|
||
<td>50K€+/mois</td>
|
||
<td>Excellente</td>
|
||
<td>Élevée</td>
|
||
<td>SDK natif</td>
|
||
</tr>
|
||
<tr>
|
||
<td>JWT Custom</td>
|
||
<td>0€ (dev)</td>
|
||
<td>Excellente</td>
|
||
<td>Moyenne</td>
|
||
<td>Natif</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_7">Justification</h2>
|
||
<ul>
|
||
<li><strong>Coût maîtrisé</strong> : 100x moins cher que Supabase/Auth0 à 10M users</li>
|
||
<li><strong>Performance</strong> : JWT validation locale = 0 latence auth sur chaque requête API</li>
|
||
<li><strong>Stack alignée</strong> : Go + PostgreSQL + Redis (déjà dans RoadWave)</li>
|
||
<li><strong>Scalabilité prouvée</strong> : Clients avec 2.3M tenants, architecture event-sourced</li>
|
||
<li><strong>RGPD natif</strong> : Entreprise suisse, data residency EU, DPA fourni</li>
|
||
<li><strong>Standards ouverts</strong> : OpenID Connect certifié (pas de vendor lock-in)</li>
|
||
</ul>
|
||
<h2 id="architecture_1">Architecture</h2>
|
||
<pre><code>┌─────────────────┐
|
||
│ 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
|
||
└─────────────────┘
|
||
</code></pre>
|
||
<h2 id="exemple-dintegration">Exemple d'intégration</h2>
|
||
<pre><code class="language-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)
|
||
</code></pre>
|
||
<h2 id="consequences_7">Conséquences</h2>
|
||
<ul>
|
||
<li>Déploiement Docker Compose pour MVP</li>
|
||
<li>Migration vers Kubernetes HA en production</li>
|
||
<li>Gestion refresh tokens (rotation automatique)</li>
|
||
<li>MFA et passkeys disponibles out-of-the-box</li>
|
||
<li>Rate limiting intégré à Zitadel</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-009-solution-de-paiement-et-gestion-des-abonnements">ADR-009 : Solution de Paiement et Gestion des Abonnements</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-19</p>
|
||
<h2 id="contexte_8">Contexte</h2>
|
||
<p>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.</p>
|
||
<h2 id="decision_8">Décision</h2>
|
||
<p><strong>Mangopay</strong> (France/Luxembourg) comme solution unique pour paiements, marketplace et abonnements.</p>
|
||
<h2 id="alternatives-considerees_8">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Solution</th>
|
||
<th>Coût transaction</th>
|
||
<th>Marketplace</th>
|
||
<th>KYC</th>
|
||
<th>Souveraineté</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Mangopay</strong></td>
|
||
<td>1.8% + 0.18€</td>
|
||
<td>✅ Natif</td>
|
||
<td>✅ Gratuit</td>
|
||
<td>🇪🇺 France/LU</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Stripe Connect</td>
|
||
<td>2.9% + 0.30€</td>
|
||
<td>✅ Natif</td>
|
||
<td>❌ 1.20€</td>
|
||
<td>🇺🇸 USA</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Mollie</td>
|
||
<td>2.9% + 0.29€</td>
|
||
<td>❌ Non</td>
|
||
<td>❌ Non</td>
|
||
<td>🇪🇺 Pays-Bas</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Paddle</td>
|
||
<td>5% + 0.50€</td>
|
||
<td>✅ Natif</td>
|
||
<td>✅ Inclus</td>
|
||
<td>🇬🇧 UK</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_8">Justification</h2>
|
||
<ul>
|
||
<li><strong>38% moins cher</strong> que Stripe (1.8% vs 2.9%)</li>
|
||
<li><strong>Marketplace natif</strong> : E-wallets automatiques, split payments 70/30, payouts SEPA gratuits</li>
|
||
<li><strong>KYC gratuit</strong> : vérification d'identité incluse (vs 1.20€/créateur chez Stripe)</li>
|
||
<li><strong>Souveraineté EU</strong> : France/Luxembourg, régulé ACPR, RGPD natif</li>
|
||
<li><strong>Conformité DAC7</strong> : reporting fiscal automatique</li>
|
||
<li><strong>Spécialisé marketplace</strong> : utilisé par Vinted, Ulule, ManoMano</li>
|
||
</ul>
|
||
<h2 id="architecture_2">Architecture</h2>
|
||
<pre><code>┌────────────────────────┐
|
||
│ 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%)│ │ │
|
||
└─────┘ └─────┘ └──────┘
|
||
</code></pre>
|
||
<h2 id="exemple-integration">Exemple intégration</h2>
|
||
<pre><code class="language-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
|
||
</code></pre>
|
||
<h2 id="consequences_8">Conséquences</h2>
|
||
<ul>
|
||
<li>Solution tout-en-un : 1 seul prestataire vs 2-3</li>
|
||
<li>Économie de 2160€/an sur 1000 abonnés (vs Stripe)</li>
|
||
<li>Délai activation compte : 2-5 jours</li>
|
||
<li>Intégration Go via REST API (pas de SDK Go officiel)</li>
|
||
<li>Apple/Google IAP gérés séparément (comme toute solution de paiement)</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-010-commandes-au-volant-et-likes">ADR-010 : Commandes au volant et likes</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2026-01-20</p>
|
||
<h2 id="contexte_9">Contexte</h2>
|
||
<p>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</p>
|
||
<h2 id="decision_9">Décision</h2>
|
||
<p><strong>Like automatique basé sur le temps d'écoute</strong>.</p>
|
||
<p>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)</p>
|
||
<h2 id="alternatives-considerees_9">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Option</th>
|
||
<th>Compatibilité</th>
|
||
<th>Sécurité</th>
|
||
<th>Complexité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Like automatique</strong></td>
|
||
<td>100%</td>
|
||
<td>Maximale</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Double-tap Pause</td>
|
||
<td>~80%</td>
|
||
<td>Moyenne</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Appui long Suivant</td>
|
||
<td>~95%</td>
|
||
<td>Faible</td>
|
||
<td>Élevée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Configuration paramétrable</td>
|
||
<td>100%</td>
|
||
<td>Variable</td>
|
||
<td>Très élevée</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_9">Justification</h2>
|
||
<ul>
|
||
<li><strong>Sécurité maximale</strong> : Aucune action complexe en conduite</li>
|
||
<li><strong>Compatibilité universelle</strong> : Fonctionne sur 100% des véhicules</li>
|
||
<li><strong>UX intuitive</strong> : Comportement standard (Spotify, YouTube Music)</li>
|
||
<li><strong>Engagement</strong> : Tous les contenus génèrent des signaux</li>
|
||
<li><strong>Simplicité</strong> : Une seule logique à implémenter et maintenir</li>
|
||
</ul>
|
||
<h2 id="consequences_9">Conséquences</h2>
|
||
<ul>
|
||
<li>Tracking du temps d'écoute via le player audio</li>
|
||
<li>Calcul du score côté backend basé sur <code>completion_rate</code></li>
|
||
<li>Communication onboarding : "Vos likes sont automatiques selon votre temps d'écoute"</li>
|
||
<li>Possibilité de like manuel depuis l'app (à l'arrêt)</li>
|
||
<li>Métriques à suivre : taux de complétion, distribution des scores, feedbacks utilisateurs</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-011-conformite-app-stores-et-plateformes-auto">ADR-011 : Conformité App Stores et Plateformes Auto</h1>
|
||
<p><strong>Statut</strong> : Accepté avec actions requises
|
||
<strong>Date</strong> : 2026-01-20</p>
|
||
<h2 id="contexte_10">Contexte</h2>
|
||
<p>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)</p>
|
||
<h2 id="decision_10">Décision</h2>
|
||
<p><strong>Stratégie de conformité multi-plateforme</strong> 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)</p>
|
||
<h2 id="plateformes-analysees">Plateformes analysées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Plateforme</th>
|
||
<th>Conformité</th>
|
||
<th>Points critiques</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Android Auto</strong></td>
|
||
<td>✅ Conforme</td>
|
||
<td>API Level 35+ (Android 15+)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>CarPlay</strong></td>
|
||
<td>✅ Conforme</td>
|
||
<td>Entitlement audio à demander</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Google Play</strong></td>
|
||
<td>⚠️ Actions requises</td>
|
||
<td>Déclaration GPS + UGC modération</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>App Store</strong></td>
|
||
<td>⚠️ Actions requises</td>
|
||
<td>Prix différenciés US/EU</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="conformite-detaillee">Conformité détaillée</h2>
|
||
<h3 id="android-auto-carplay">Android Auto / CarPlay ✅</h3>
|
||
<ul>
|
||
<li>100% audio (pas de vidéo)</li>
|
||
<li>Commandes standard au volant</li>
|
||
<li>Aucun achat in-car</li>
|
||
<li>Like automatique = sécurité maximale</li>
|
||
<li><strong>Notifications géolocalisées</strong> : sonore uniquement en mode CarPlay/Android Auto (pas d'overlay visuel)</li>
|
||
<li><strong>Action</strong> : Demander CarPlay Audio Entitlement (Apple)</li>
|
||
</ul>
|
||
<h3 id="google-play">Google Play ⚠️</h3>
|
||
<p><strong>UGC (critique)</strong> :
|
||
- Modération hybride IA + humain ✅
|
||
- 3 premiers contenus validés manuellement ✅
|
||
- Système de strikes (4 = ban) ✅
|
||
- Signalement + blocage utilisateurs ✅</p>
|
||
<p><strong>GPS Background (critique)</strong> :
|
||
- Permission "Always Location" = <strong>OPTIONNELLE</strong>
|
||
- Demandée uniquement pour mode piéton (notifications arrière-plan audio-guides)
|
||
- Justification Play Console :</p>
|
||
<blockquote>
|
||
<p>"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
|
||
- <strong>Action</strong> : Remplir formulaire background location Play Console avec justification</p>
|
||
</blockquote>
|
||
<p><strong>Réponses formulaire Play Console</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Question</th>
|
||
<th>Réponse</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Why does your app need background location?</td>
|
||
<td>"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."</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Is this feature core to your app?</td>
|
||
<td>"No. This is an optional feature. Users can use RoadWave without background location permission (in-car mode works with foreground location only)."</td>
|
||
</tr>
|
||
<tr>
|
||
<td>What user value does this provide?</td>
|
||
<td>"Pedestrian users (tourists, museum visitors) can keep phone in pocket and receive audio-guide alerts automatically without opening the app."</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Does a less invasive alternative exist?</td>
|
||
<td>"Yes. Users can use manual navigation (open app, select audio-guide). Background location is a convenience feature for hands-free experience."</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="app-store">App Store ⚠️</h3>
|
||
<p><strong>Prix différenciés (légaux depuis 2025-2026)</strong> :
|
||
- 🇺🇸 US : Lien externe autorisé (0% commission)
|
||
- 🇪🇺 EU : Paiement externe DMA (7-20% commission réduite)
|
||
- 🌍 Monde : IAP obligatoire (30% commission)</p>
|
||
<p><strong>UGC</strong> :
|
||
- Mode Kids obligatoire (filtrage selon âge) ✅
|
||
- Système de modération + signalement ✅</p>
|
||
<p><strong>GPS Background (critique)</strong> :
|
||
- Permission "Always Location" = <strong>OPTIONNELLE</strong>
|
||
- Deux strings Info.plist requises :
|
||
- <code>NSLocationWhenInUseUsageDescription</code> : explication mode voiture
|
||
- <code>NSLocationAlwaysAndWhenInUseUsageDescription</code> : 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
|
||
- <strong>Action</strong> : Voir strings détaillés dans <a href="#../regles-metier/05-interactions-navigation.md#512-mode-piéton-audio-guides">05-interactions-navigation.md</a></p>
|
||
<h3 id="revenus-createurs">Revenus créateurs</h3>
|
||
<p><strong>Position</strong> : 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</p>
|
||
<h2 id="actions-bloquantes-avant-soumission">Actions bloquantes avant soumission</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Plateforme</th>
|
||
<th>Deadline</th>
|
||
<th>Complexité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Demander CarPlay Audio Entitlement</td>
|
||
<td>Apple</td>
|
||
<td>Avant soumission iOS</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Remplir formulaire background location avec justification</td>
|
||
<td>Google Play</td>
|
||
<td>Avant soumission Android</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Implémenter disclosure GPS (écran dédié mode piéton)</td>
|
||
<td>iOS + Android</td>
|
||
<td>MVP</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Rendre permission "Always Location" optionnelle</td>
|
||
<td>iOS + Android</td>
|
||
<td>MVP</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Désactiver overlay visuel notification en CarPlay/Android Auto</td>
|
||
<td>iOS + Android</td>
|
||
<td>MVP</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Mettre à jour strings Info.plist avec justifications détaillées</td>
|
||
<td>iOS</td>
|
||
<td>MVP</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Finaliser système modération UGC</td>
|
||
<td>Google + Apple</td>
|
||
<td>MVP</td>
|
||
<td>Élevée</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Estimation totale</strong> : +5 jours développement avant soumission stores</p>
|
||
<h2 id="strategie-de-lancement">Stratégie de lancement</h2>
|
||
<p><strong>Phase 1 - MVP</strong> :
|
||
- IAP uniquement (5.99€/mois mondial)
|
||
- Modération UGC active
|
||
- GPS avec disclosure
|
||
- CarPlay/Android Auto basique</p>
|
||
<p><strong>Phase 2 - Post-validation</strong> :
|
||
- Prix différenciés US (lien externe 4.99€)
|
||
- Paiement externe EU (DMA)
|
||
- Monétisation créateurs (Mangopay)</p>
|
||
<h2 id="consequences_10">Conséquences</h2>
|
||
<ul>
|
||
<li>Formation équipe sur politiques stores</li>
|
||
<li>Suivi des métriques modération (% rejet, SLA)</li>
|
||
<li>Migration iOS 26 SDK (Avril 2026)</li>
|
||
<li>API Level 35 Android (2026)</li>
|
||
<li>Communication transparente GPS/publicités</li>
|
||
</ul>
|
||
<h2 id="sources">Sources</h2>
|
||
<ul>
|
||
<li><a href="https://developer.android.com/training/cars/media">Android Auto Media Apps</a></li>
|
||
<li><a href="https://developer.apple.com/carplay">CarPlay Developer Guide</a></li>
|
||
<li><a href="https://support.google.com/googleplay/android-developer/answer/9876937">Google Play UGC Policy</a></li>
|
||
<li><a href="https://developer.apple.com/app-store/review/guidelines/">App Store Guidelines</a></li>
|
||
<li><a href="https://www.revenuecat.com/blog/growth/apple-eu-dma-update-june-2025/">Apple DMA Update EU</a></li>
|
||
<li><a href="https://support.google.com/googleplay/android-developer/answer/9799150">Google Background Location 2026</a></li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-012-architecture-backend">ADR-012 : Architecture Backend</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-20</p>
|
||
<h2 id="contexte_11">Contexte</h2>
|
||
<p>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.</p>
|
||
<h2 id="decision_11">Décision</h2>
|
||
<p><strong>Monolithe modulaire</strong> avec séparation claire en modules internes.</p>
|
||
<h2 id="alternatives-considerees_10">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Architecture</th>
|
||
<th>Complexité</th>
|
||
<th>Coûts infra</th>
|
||
<th>Time to market</th>
|
||
<th>Évolutivité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Monolithe modulaire</strong></td>
|
||
<td>Faible</td>
|
||
<td>Faible</td>
|
||
<td>Rapide</td>
|
||
<td>0-1M users</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Microservices</td>
|
||
<td>Élevée</td>
|
||
<td>Élevée</td>
|
||
<td>Lent</td>
|
||
<td>1M+ users</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Hybrid (Mono + Workers)</td>
|
||
<td>Moyenne</td>
|
||
<td>Moyenne</td>
|
||
<td>Moyen</td>
|
||
<td>100K-5M users</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_10">Justification</h2>
|
||
<ul>
|
||
<li><strong>Simplicité</strong> : 1 seul binaire Go, déploiement trivial</li>
|
||
<li><strong>Transactions</strong> : Communications inter-modules en mémoire (pas de latence réseau)</li>
|
||
<li><strong>Debugging</strong> : Stack traces complètes, profiling unifié</li>
|
||
<li><strong>Coûts</strong> : 1 serveur suffit pour 100K users (vs N services)</li>
|
||
<li><strong>Refactoring</strong> : Modules internes bien séparés facilitent migration vers microservices si nécessaire</li>
|
||
</ul>
|
||
<h2 id="structure-modulaire">Structure modulaire</h2>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p>Chaque module suit : <code>handler.go</code> → <code>service.go</code> → <code>repository.go</code>.</p>
|
||
<h2 id="consequences_11">Conséquences</h2>
|
||
<ul>
|
||
<li>Scaling horizontal : réplication complète du binaire (acceptable jusqu'à 1M users)</li>
|
||
<li>Transition vers microservices possible en phase 2 (extraction progressive des modules)</li>
|
||
<li>Importance de maintenir découplage fort entre modules (interfaces claires)</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-013-orm-et-acces-donnees">ADR-013 : ORM et Accès Données</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-20</p>
|
||
<h2 id="contexte_12">Contexte</h2>
|
||
<p>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.</p>
|
||
<h2 id="decision_12">Décision</h2>
|
||
<p><strong>sqlc</strong> pour génération de code Go type-safe depuis SQL.</p>
|
||
<h2 id="alternatives-considerees_11">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Solution</th>
|
||
<th>Performance</th>
|
||
<th>Type Safety</th>
|
||
<th>Contrôle SQL</th>
|
||
<th>Courbe apprentissage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>sqlc</strong></td>
|
||
<td>Excellente</td>
|
||
<td>Très haute</td>
|
||
<td>Total</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>GORM</td>
|
||
<td>Moyenne</td>
|
||
<td>Moyenne</td>
|
||
<td>Limité</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>pgx + SQL brut</td>
|
||
<td>Excellente</td>
|
||
<td>Faible</td>
|
||
<td>Total</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>sqlx</td>
|
||
<td>Bonne</td>
|
||
<td>Faible</td>
|
||
<td>Total</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_11">Justification</h2>
|
||
<ul>
|
||
<li><strong>Performance</strong> : Génération compile-time, zero overhead runtime</li>
|
||
<li><strong>Type safety</strong> : Structs Go générées automatiquement, erreurs détectées à la compilation</li>
|
||
<li><strong>Contrôle SQL</strong> : Requêtes PostGIS complexes écrites en pur SQL (pas de limitations ORM)</li>
|
||
<li><strong>Maintenabilité</strong> : Modifications SQL → <code>sqlc generate</code> → code mis à jour</li>
|
||
<li><strong>Simplicité</strong> : Pas de magic, code généré lisible et debuggable</li>
|
||
</ul>
|
||
<h2 id="workflow">Workflow</h2>
|
||
<pre><code class="language-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;
|
||
</code></pre>
|
||
<pre><code class="language-bash">sqlc generate
|
||
</code></pre>
|
||
<pre><code class="language-go">// Code Go type-safe généré automatiquement
|
||
contents, err := q.GetContentNearby(ctx, location, radius, limit)
|
||
</code></pre>
|
||
<h2 id="consequences_12">Conséquences</h2>
|
||
<ul>
|
||
<li>Dépendance : <code>github.com/sqlc-dev/sqlc</code></li>
|
||
<li>Fichier <code>sqlc.yaml</code> à la racine pour configuration</li>
|
||
<li>Migrations gérées séparément avec <code>golang-migrate</code></li>
|
||
<li>CI doit exécuter <code>sqlc generate</code> pour valider cohérence SQL/Go</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-014-frontend-mobile">ADR-014 : Frontend Mobile</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-20</p>
|
||
<h2 id="contexte_13">Contexte</h2>
|
||
<p>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.</p>
|
||
<h2 id="decision_13">Décision</h2>
|
||
<p><strong>Flutter</strong> pour iOS et Android avec codebase unique.</p>
|
||
<h2 id="alternatives-considerees_12">Alternatives considérées</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Framework</th>
|
||
<th>Codebase</th>
|
||
<th>Performance</th>
|
||
<th>Audio/CarPlay</th>
|
||
<th>Communauté</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Flutter</strong></td>
|
||
<td>Unique</td>
|
||
<td>Native</td>
|
||
<td>Excellente</td>
|
||
<td>Large</td>
|
||
</tr>
|
||
<tr>
|
||
<td>React Native</td>
|
||
<td>Unique</td>
|
||
<td>Bonne</td>
|
||
<td>Modules natifs requis</td>
|
||
<td>Très large</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Native (Swift+Kotlin)</td>
|
||
<td>Double</td>
|
||
<td>Excellente</td>
|
||
<td>Native</td>
|
||
<td>Large</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Ionic/Capacitor</td>
|
||
<td>Unique</td>
|
||
<td>Moyenne</td>
|
||
<td>Limitée</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="justification_12">Justification</h2>
|
||
<ul>
|
||
<li><strong>Codebase unique</strong> : iOS + Android maintenus ensemble, vélocité développement x2</li>
|
||
<li><strong>Performance</strong> : Dart compilé en code natif (pas de bridge JS)</li>
|
||
<li><strong>Audio HLS</strong> : Package <code>just_audio</code> mature avec support HLS, buffering adaptatif</li>
|
||
<li><strong>CarPlay/Android Auto</strong> : Support via packages communautaires (<code>flutter_carplay</code>, <code>android_auto_flutter</code>)</li>
|
||
<li><strong>Géolocalisation</strong> : <code>geolocator</code> robuste avec gestion permissions</li>
|
||
<li><strong>Écosystème</strong> : Widgets riches (Material/Cupertino), state management mature (Bloc, Riverpod)</li>
|
||
</ul>
|
||
<h2 id="packages-cles">Packages clés</h2>
|
||
<pre><code class="language-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
|
||
</code></pre>
|
||
<p><strong>Nouveaux packages (contenus géolocalisés)</strong> :</p>
|
||
<ul>
|
||
<li><strong><code>geofence_service</code></strong> : Détection entrée/sortie rayon 200m en arrière-plan (mode piéton)</li>
|
||
<li>Geofencing natif iOS/Android</li>
|
||
<li>Minimise consommation batterie</li>
|
||
<li>
|
||
<p>Supporte notifications push même app fermée</p>
|
||
</li>
|
||
<li>
|
||
<p><strong><code>flutter_local_notifications</code></strong> : Notifications locales avec compteur dynamique</p>
|
||
</li>
|
||
<li>Notification avec compteur décroissant (7→1) en mode voiture</li>
|
||
<li>Icônes personnalisées selon type contenu</li>
|
||
<li>Désactivation overlay en mode CarPlay/Android Auto (conformité)</li>
|
||
</ul>
|
||
<h2 id="structure-application">Structure application</h2>
|
||
<pre><code>lib/
|
||
├── core/ # Config, DI, routes
|
||
├── data/ # Repositories, API clients
|
||
├── domain/ # Models, business logic
|
||
├── presentation/ # UI (screens, widgets, blocs)
|
||
└── main.dart
|
||
</code></pre>
|
||
<h2 id="consequences_13">Conséquences</h2>
|
||
<ul>
|
||
<li>Équipe doit apprendre Dart (syntaxe proche Java/TypeScript)</li>
|
||
<li>Taille binaire : 8-15 MB (acceptable)</li>
|
||
<li>Tests : <code>flutter_test</code> pour widgets, <code>integration_test</code> pour E2E</li>
|
||
<li>CI/CD : Fastlane pour déploiement stores</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="adr-015-strategie-tests">ADR-015 : Stratégie Tests</h1>
|
||
<p><strong>Statut</strong> : Accepté
|
||
<strong>Date</strong> : 2025-01-20</p>
|
||
<h2 id="contexte_14">Contexte</h2>
|
||
<p>RoadWave nécessite une couverture tests robuste avec documentation vivante des use cases. La stratégie doit équilibrer vélocité développement et qualité.</p>
|
||
<h2 id="decision_14">Décision</h2>
|
||
<p>Approche <strong>multi-niveaux</strong> : unitaires, intégration, BDD (Gherkin), E2E, load testing.</p>
|
||
<h2 id="strategie-par-type">Stratégie par type</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Framework</th>
|
||
<th>Cible</th>
|
||
<th>Fréquence</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Unitaires</strong></td>
|
||
<td>Testify</td>
|
||
<td>80%+ couverture</td>
|
||
<td>Chaque commit</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Intégration DB</strong></td>
|
||
<td>Testify + Testcontainers</td>
|
||
<td>Repositories critiques</td>
|
||
<td>Avant merge PR</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>BDD (Gherkin)</strong></td>
|
||
<td>Godog</td>
|
||
<td>User stories</td>
|
||
<td>Avant release</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>E2E Mobile</strong></td>
|
||
<td>Flutter integration_test</td>
|
||
<td>Parcours critiques</td>
|
||
<td>Nightly</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Load</strong></td>
|
||
<td>k6</td>
|
||
<td>N/A</td>
|
||
<td>Avant mise en prod</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="tests-unitaires-testify">Tests unitaires (Testify)</h2>
|
||
<pre><code class="language-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)
|
||
}
|
||
</code></pre>
|
||
<p><strong>Couverture minimale</strong> : 80% sur packages <code>internal/*/service.go</code></p>
|
||
<h2 id="tests-bdd-gherkin-godog">Tests BDD (Gherkin + Godog)</h2>
|
||
<p>Voir <a href="#007-tests-bdd">ADR-007</a> pour contexte complet.</p>
|
||
<pre><code class="language-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
|
||
</code></pre>
|
||
<p><strong>Couverture</strong> : Tous les cas d'usage du <a href="#../../README">README.md</a> traduits en <code>.feature</code>.</p>
|
||
<h2 id="tests-integration-testcontainers">Tests intégration (Testcontainers)</h2>
|
||
<pre><code class="language-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)
|
||
}
|
||
</code></pre>
|
||
<h2 id="tests-e2e-mobile-flutter">Tests E2E Mobile (Flutter)</h2>
|
||
<pre><code class="language-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);
|
||
});
|
||
</code></pre>
|
||
<h2 id="load-testing-k6">Load testing (k6)</h2>
|
||
<pre><code class="language-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 });
|
||
}
|
||
</code></pre>
|
||
<p><strong>Objectif</strong> : API p99 < 100ms à 10K RPS.</p>
|
||
<h2 id="cicd-pipeline">CI/CD Pipeline</h2>
|
||
<pre><code class="language-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
|
||
</code></pre>
|
||
<h2 id="consequences_14">Conséquences</h2>
|
||
<ul>
|
||
<li>Dépendances :</li>
|
||
<li><code>github.com/stretchr/testify</code></li>
|
||
<li><code>github.com/cucumber/godog</code></li>
|
||
<li><code>github.com/testcontainers/testcontainers-go</code></li>
|
||
<li><code>grafana/k6</code></li>
|
||
<li>Temps CI : ~3-5 min (tests unitaires + BDD)</li>
|
||
<li>Tests intégration/E2E : nightly builds (15-30 min)</li>
|
||
<li>Load tests : avant chaque release majeure</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="regles-metier-roadwave">Règles métier RoadWave</h1>
|
||
<blockquote>
|
||
<p>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.</p>
|
||
</blockquote>
|
||
<hr />
|
||
<h2 id="table-des-matieres_1">📋 Table des matières</h2>
|
||
<h3 id="01-authentification-inscription"><a href="#01-authentification-inscription">01. Authentification & Inscription</a></h3>
|
||
<p><strong>Contenu</strong> : Inscription, connexion, récupération de compte</p>
|
||
<ul>
|
||
<li>Inscription : email/password uniquement (pas d'OAuth tiers)</li>
|
||
<li>Vérification email : optionnelle auditeurs (limite 5 contenus), obligatoire créateurs (lien expire 7j)</li>
|
||
<li>Connexion : 5 tentatives max, blocage 15 min, refresh token 30j</li>
|
||
<li>Récupération mot de passe : email, lien expire 1h</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="02-algorithme-de-recommandation"><a href="#02-algorithme-recommandation">02. Algorithme de recommandation</a></h3>
|
||
<p><strong>Contenu</strong> : Scoring, géolocalisation, orientation politique, mode Kids</p>
|
||
<ul>
|
||
<li>Classification géo : Ancré (70%) / Contextuel (50%) / Neutre (20%)</li>
|
||
<li>Engagement : 20%, Aléatoire : 10%</li>
|
||
<li>Orientation politique : 5 niveaux, équilibre imposé (40/40/20)</li>
|
||
<li>Mode Kids : 4 tranches (3-6 / 6-9 / 9-12 / 13-15 ans), activation auto <13 ans</li>
|
||
<li>Historique : >80% jamais reproposer, <10s ne pas reproposer</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="03-centres-dinteret-et-jauges"><a href="#03-centres-interet-jauges">03. Centres d'intérêt et jauges</a></h3>
|
||
<p><strong>Contenu</strong> : Évolution jauges, valeurs initiales</p>
|
||
<ul>
|
||
<li>Like automatique : écoute ≥80% → +2%, écoute 30-79% → +1%</li>
|
||
<li>Like explicite (manuel) : +2% (cumulable avec auto)</li>
|
||
<li>Abonnement : +5%</li>
|
||
<li>Skip rapide (<10s) : -0.5%</li>
|
||
<li>Valeur initiale : 50% (neutre)</li>
|
||
<li>Limites : 0-100% stricte, pas de dégradation temporelle</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="04-creation-et-publication-de-contenu"><a href="#04-creation-publication-contenu">04. Création et publication de contenu</a></h3>
|
||
<p><strong>Contenu</strong> : Upload, métadonnées, validation, modification</p>
|
||
<ul>
|
||
<li>Formats : MP3, AAC (.mp3, .aac, .m4a), max 200 MB, 4h</li>
|
||
<li>Métadonnées obligatoires : titre, type géo, zone, tags (1-3), classification âge</li>
|
||
<li>Validation 3 premiers contenus : 24-48h (modération RoadWave)</li>
|
||
<li>Modification : métadonnées uniquement, pas audio/zone/classification</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="05-interactions-et-navigation"><a href="#05-interactions-navigation">05. Interactions et navigation</a></h3>
|
||
<p><strong>Contenu</strong> : Commandes Suivant/Précédent, interactions volant, lecture en boucle</p>
|
||
<ul>
|
||
<li>Suivant : pré-calcul 5 contenus, recalcul >10km ou 10 min</li>
|
||
<li>Précédent : <10s → contenu avant, ≥10s → replay début</li>
|
||
<li>Commandes volant : Suivant, Précédent, Play/Pause uniquement</li>
|
||
<li>Like automatique : ≥80% écoute → +2 points, 30-79% → +1 point</li>
|
||
<li>Actions manuelles : bouton cœur (arrêt véhicule) ou vocal (CarPlay/Android Auto)</li>
|
||
<li>Passage auto après 2s (1s mode Kids)</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="06-publicites"><a href="#06-publicites">06. Publicités</a></h3>
|
||
<p><strong>Contenu</strong> : Campagnes, fréquence, insertion, facturation</p>
|
||
<ul>
|
||
<li>Interface self-service, budget min 50€, étalement paramétrable</li>
|
||
<li>Fréquence : 1/5 contenus (gratuits uniquement)</li>
|
||
<li>Durée : 10-60s (recommandé 15-30s), skippable après 5s</li>
|
||
<li>Validation manuelle 24-48h, prépaiement Mangopay</li>
|
||
<li>Facturation : écoute complète 0.05€, skip après 5s : 0.02€, skip immédiat : 0€</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="07-radio-live"><a href="#07-radio-live">07. Radio live</a></h3>
|
||
<p><strong>Contenu</strong> : Démarrage, arrêt, comportement auditeur</p>
|
||
<ul>
|
||
<li>Buffer 15s avant diffusion publique, durée max 8h</li>
|
||
<li>Notification push abonnés dans zone géo uniquement</li>
|
||
<li>Arrêt : compte à rebours 5s (manuel) ou auto si déco ≥60s</li>
|
||
<li>Enregistrement auto MP3 256 kbps → replay sous 5-10 min</li>
|
||
<li>Auditeur : buffer 15s, continuation si sortie zone, AUCUN chat</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="08-abonnements-et-notifications"><a href="#08-abonnements-notifications">08. Abonnements et notifications</a></h3>
|
||
<p><strong>Contenu</strong> : Impact algorithme, notifications, audio-guides, limites</p>
|
||
<ul>
|
||
<li>Boost +30% au score final (pas priorité absolue)</li>
|
||
<li>Détection contexte : <5 km/h piéton, >10 km/h voiture</li>
|
||
<li>Voiture : in-app uniquement, Piéton : push actives</li>
|
||
<li>Limite 10 notifications push/jour (5-20), mode silencieux 22h-8h</li>
|
||
<li>Audio-guide piéton : détection <100m lieu, page sélection, navigation manuelle</li>
|
||
<li>Max 200 abonnements, +5% jauges tous tags créateur</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="09-monetisation-createurs"><a href="#09-monetisation-createurs">09. Monétisation créateurs</a></h3>
|
||
<p><strong>Contenu</strong> : Activation, KYC, sources revenus, paiement</p>
|
||
<ul>
|
||
<li>Conditions : compte ≥3 mois, ≥500 abonnés, ≥10K écoutes, 0 strike, ≥5 contenus/90j</li>
|
||
<li>KYC via Mangopay Connect : SIRET, TVA, RIB pro, pièce ID, Kbis <3 mois</li>
|
||
<li>Revenus pub : 3€ / 1000 écoutes complètes (6% CA pub)</li>
|
||
<li>Revenus Premium : 70% créateur, 30% plateforme (proportionnel temps écoute)</li>
|
||
<li>Paiement : seuil 50€, mensuel (15 du mois suivant), SEPA Mangopay</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="10-premium"><a href="#10-premium">10. Premium</a></h3>
|
||
<p><strong>Contenu</strong> : Offre, multi-devices, avantages, gestion abonnement</p>
|
||
<ul>
|
||
<li>Prix : 4.99€/mois OU 49.99€/an (4.16€/mois effectif)</li>
|
||
<li>Pas d'essai gratuit, pas de partage familial (MVP)</li>
|
||
<li>Multi-devices : 1 seul stream actif, détection connexion simultanée</li>
|
||
<li>Avantages : 0 pub, contenus exclusifs 👑, qualité 64 kbps Opus, offline illimité</li>
|
||
<li>Paiement : Mangopay (web) ou IAP iOS/Android 5.99€/mois (+30% commission)</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="11-mode-offline"><a href="#11-mode-offline">11. Mode offline</a></h3>
|
||
<p><strong>Contenu</strong> : Téléchargement, validité, synchronisation</p>
|
||
<ul>
|
||
<li>Zone géographique : choix manuel (autour de moi / ville / département / région)</li>
|
||
<li>Nombre contenus : gratuit 50 max, Premium illimité</li>
|
||
<li>WiFi par défaut, mobile avec confirmation + estimation volume</li>
|
||
<li>Validité : 30 jours, renouvellement auto si WiFi (contenus >25 jours)</li>
|
||
<li>Sync : likes/abonnements batch auto à reconnexion, queue actions 7j max</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="12-gestion-des-erreurs"><a href="#12-gestion-erreurs">12. Gestion des erreurs</a></h3>
|
||
<p><strong>Contenu</strong> : Aucun contenu, contenu supprimé, perte réseau, GPS désactivé</p>
|
||
<ul>
|
||
<li>Aucun contenu : élargissement auto 50km → 100km → département → région → national</li>
|
||
<li>Contenu supprimé : laisser terminer, passage auto suivant après 2s</li>
|
||
<li>Perte réseau : buffer adaptatif (WiFi 5-120s, 4G 10-120s, 3G 30-300s), retry 5s max 6×</li>
|
||
<li>GPS désactivé : mode dégradé (contenu national + neutre + téléchargé)</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="13-conformite-rgpd"><a href="#13-conformite-rgpd">13. Conformité RGPD</a></h3>
|
||
<p><strong>Contenu</strong> : Consentements, anonymisation, export, suppression</p>
|
||
<ul>
|
||
<li>Consentement : Tarteaucitron.js + PostgreSQL versioning</li>
|
||
<li>GPS précis : 24h puis geohash 5 (~5km²)</li>
|
||
<li>Export : JSON + HTML + audio → ZIP, génération asynchrone sous 48h, expire 7j</li>
|
||
<li>Suppression : grace period 30j, contenus créés anonymisés (créateur = "Utilisateur supprimé")</li>
|
||
<li>Analytics : Matomo self-hosted, IP anonymisées, 0 cookie tiers</li>
|
||
<li>DPO : fondateur formé CNIL (non obligatoire <250 employés)</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="14-moderation-flows-operationnels"><a href="#14-moderation-flows">14. Modération - Flows opérationnels</a></h3>
|
||
<p><strong>Contenu</strong> : Signalement, traitement, sanctions</p>
|
||
<ul>
|
||
<li>Signalement : 7 catégories (haine, sexuel, illégalité, droits auteur, spam, fake news, autre)</li>
|
||
<li>IA pré-filtre : Whisper large-v3 (transcription) + NLP open source (1-10 min)</li>
|
||
<li>SLA : Critique <2h (24/7), Haute/Moyenne <24h, Basse <72h</li>
|
||
<li>Notification sanction : email + push + in-app (détail complet : catégorie, timestamp, transcription)</li>
|
||
<li>Appel : formulaire in-app, délai 7j max, réponse 72h garanti (standard)</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="15-autres-comportements"><a href="#15-autres-comportements">15. Autres comportements</a></h3>
|
||
<p><strong>Contenu</strong> : Partage, profil créateur, recherche</p>
|
||
<ul>
|
||
<li>Partage : bouton partout, lien <code>roadwave.fr/share/c/[id]</code>, web player + deep link</li>
|
||
<li>Profil créateur : @pseudo, bio (300 car), stats publiques arrondies, badge vérifié ✓</li>
|
||
<li>Badge vérifié : KYC validé OU célébrité OU >10K abonnés</li>
|
||
<li>Recherche : full-text PostgreSQL (français, stemming), recherche géo (Nominatim OSM)</li>
|
||
<li>Filtres : type, durée, âge, géo, tags, date, premium (combinables)</li>
|
||
<li>Affichage : liste enrichie (20/page, infinite scroll) + vue carte Leaflet</li>
|
||
</ul>
|
||
<hr />
|
||
<h3 id="16-audio-guides-multi-sequences"><a href="#16-audio-guides-multi-sequences">16. Audio-guides multi-séquences</a></h3>
|
||
<p><strong>Contenu</strong> : Modes déplacement, navigation, déclenchement GPS, publicités</p>
|
||
<ul>
|
||
<li><strong>4 modes</strong> : 🚶 Piéton (manuel) / 🚗 Voiture (GPS auto + manuel) / 🚴 Vélo / 🚌 Transport</li>
|
||
<li><strong>Mode Piéton</strong> : pause auto après chaque séquence, user clique Suivant, navigation libre</li>
|
||
<li><strong>Mode Voiture</strong> : déclenchement GPS auto (rayon 30m), boutons manuels actifs, warning sécurité >10 km/h</li>
|
||
<li><strong>Affichage voiture</strong> : distance temps réel + ETA + direction (flèche) + vitesse</li>
|
||
<li><strong>Rayons</strong> : Voiture 30m, Vélo 50m, Transport 100m (configurable créateur 10-200m)</li>
|
||
<li><strong>Publicités</strong> : 1/5 séquences tous modes, auto-play, skippable 5s</li>
|
||
<li><strong>Reprise</strong> : sauvegarde auto (séquence + position exacte), popup si <30j, multi-device (sync cloud)</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="organisation">🗂️ Organisation</h2>
|
||
<p>Chaque fichier de règles métier suit la structure :</p>
|
||
<ol>
|
||
<li><strong>Décisions</strong> : choix validés avec justifications</li>
|
||
<li><strong>Comportements détaillés</strong> : flux utilisateur, cas limites</li>
|
||
<li><strong>Paramètres</strong> : valeurs exactes, seuils, durées</li>
|
||
<li><strong>Points d'attention Gherkin</strong> : éléments à tester</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="utilisation">🚀 Utilisation</h2>
|
||
<p>Ces documents servent de <strong>référence unique</strong> pour :</p>
|
||
<ul>
|
||
<li>✅ Développement backend/frontend</li>
|
||
<li>✅ Écriture des tests Gherkin (BDD)</li>
|
||
<li>✅ Validation QA</li>
|
||
<li>✅ Documentation produit</li>
|
||
</ul>
|
||
<p><strong>Prochaine étape</strong> : Création des fichiers <code>.feature</code> Gherkin dans <code>features/</code> basés sur ces règles.</p>
|
||
<hr />
|
||
<h2 id="statistiques">📊 Statistiques</h2>
|
||
<ul>
|
||
<li><strong>16 sections</strong> validées</li>
|
||
<li><strong>~12 000 lignes</strong> de spécifications détaillées</li>
|
||
<li><strong>Coût infrastructure MVP</strong> : ~50-250€/mois (hors salaires)</li>
|
||
<li><strong>Technologies</strong> : 100% open source (sauf Mangopay paiements)</li>
|
||
</ul>
|
||
<hr />
|
||
<p><strong>Dernière mise à jour</strong> : Janvier 2026
|
||
<strong>Statut</strong> : ✅ Toutes sections validées</p>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="1-authentification-inscription">1. Authentification & Inscription</h2>
|
||
<h3 id="11-methodes-dinscription">1.1 Méthodes d'inscription</h3>
|
||
<p><strong>Décision</strong> : Email/Password uniquement (pas d'OAuth tiers)</p>
|
||
<ul>
|
||
<li>❌ Pas de Google, Apple, Facebook OAuth (dépendance services US/Chine)</li>
|
||
<li>✅ Email + mot de passe</li>
|
||
<li>✅ 2FA (Two-Factor Authentication) disponible</li>
|
||
<li>✅ Option "Appareil de confiance" (skip 2FA pour 30 jours)</li>
|
||
</ul>
|
||
<p><strong>Justification</strong> :
|
||
- Souveraineté : pas de dépendance externe
|
||
- RGPD : données 100% contrôlées
|
||
- Coût : 0€ (Zitadel intégré)</p>
|
||
<hr />
|
||
<h3 id="12-verification-email">1.2 Vérification email</h3>
|
||
<p><strong>Décision</strong> : Différenciée selon le rôle utilisateur</p>
|
||
<h4 id="pour-les-auditeurs-ecoute-uniquement">Pour les auditeurs (écoute uniquement)</h4>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>État</th>
|
||
<th>Capacités</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Email non vérifié</strong></td>
|
||
<td>Lecture illimitée + création max 5 contenus</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Email vérifié</strong></td>
|
||
<td>Toutes fonctionnalités débloquées</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Paramètres</strong> :
|
||
- Lien de vérification expire après <strong>7 jours</strong>
|
||
- Possibilité de renvoyer le lien (max 3 fois/jour)
|
||
- Rappel in-app après création du 3ème contenu</p>
|
||
<p><strong>Justification</strong> :
|
||
- Friction minimale à l'inscription
|
||
- Anti-spam sans bloquer l'essai du produit
|
||
- Incitation naturelle à vérifier (déblocage)</p>
|
||
<h4 id="pour-les-createurs-monetisation">Pour les créateurs (monétisation)</h4>
|
||
<p><strong>Vérification obligatoire sous 7 jours</strong> pour :
|
||
- Accès au programme de monétisation
|
||
- KYC et reversement des revenus (conformité Mangopay)
|
||
- Publication illimitée de contenus</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Conformité légale</strong> : KYC obligatoire pour transferts financiers
|
||
- <strong>Anti-fraude</strong> : Vérification identité réelle pour paiements
|
||
- <strong>Responsabilité</strong> : RoadWave doit pouvoir prouver identité créateurs monétisés</p>
|
||
<hr />
|
||
<h3 id="13-donnees-requises-a-linscription">1.3 Données requises à l'inscription</h3>
|
||
<p><strong>Obligatoires</strong> :
|
||
- ✅ 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)</p>
|
||
<p><strong>Optionnelles</strong> :
|
||
- ❌ Nom complet (privacy by design)
|
||
- ❌ Photo de profil (avatar par défaut généré)
|
||
- ❌ Bio (ajout ultérieur)</p>
|
||
<p><strong>Âge minimum</strong> :
|
||
- <strong>13 ans minimum</strong> (conformité réglementation réseaux sociaux EU)
|
||
- Vérification à l'inscription via date de naissance
|
||
- Blocage inscription si <13 ans avec message explicite</p>
|
||
<p><strong>Justification</strong> :
|
||
- RGPD minimal data
|
||
- Friction réduite (4 champs max)
|
||
- Protection mineurs (obligation légale)</p>
|
||
<hr />
|
||
<h3 id="14-tranches-dage-des-contenus">1.4 Tranches d'âge des contenus</h3>
|
||
<p><strong>Décision</strong> : Classification obligatoire des contenus</p>
|
||
<p><strong>Catégories</strong> :
|
||
- 🟢 <strong>Tout public</strong> (défaut)
|
||
- 🟡 <strong>13+</strong> : contenu mature léger (débats, actualité sensible)
|
||
- 🟠 <strong>16+</strong> : contenu mature (violence verbale, sujets sensibles)
|
||
- 🔴 <strong>18+</strong> : contenu adulte (langage explicite, sujets réservés)</p>
|
||
<p><strong>Règles de diffusion</strong> :
|
||
- Utilisateur 13-15 ans → contenus 🟢 uniquement
|
||
- Utilisateur 16-17 ans → contenus 🟢 🟡
|
||
- Utilisateur 18+ → tous contenus</p>
|
||
<p><strong>Modération</strong> :
|
||
- Vérification obligatoire de la classification lors de la validation
|
||
- Reclassification possible par modérateurs
|
||
- Strike si classification volontairement incorrecte</p>
|
||
<p><strong>Justification</strong> :
|
||
- Protection mineurs (obligation légale)
|
||
- Responsabilité plateforme
|
||
- Coût : champ supplémentaire + règle algo</p>
|
||
<hr />
|
||
<h3 id="15-validation-mot-de-passe">1.5 Validation mot de passe</h3>
|
||
<p><strong>Règles</strong> :
|
||
- ✅ Minimum <strong>8 caractères</strong>
|
||
- ✅ Au moins <strong>1 majuscule</strong>
|
||
- ✅ Au moins <strong>1 chiffre</strong>
|
||
- ❌ Pas de symbole obligatoire (simplicité)</p>
|
||
<p><strong>Validation</strong> :
|
||
- Côté client (feedback temps réel)
|
||
- Côté backend (sécurité)
|
||
- Message d'erreur explicite par règle non respectée</p>
|
||
<p><strong>Justification</strong> :
|
||
- Standard industrie
|
||
- Bloque 95% des mots de passe faibles
|
||
- UX acceptable (pas trop restrictif)</p>
|
||
<hr />
|
||
<h3 id="16-two-factor-authentication-2fa">1.6 Two-Factor Authentication (2FA)</h3>
|
||
<p><strong>Décision</strong> : Optionnel mais recommandé</p>
|
||
<p><strong>Méthodes disponibles</strong> :
|
||
- ✅ 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)</p>
|
||
<p><strong>Appareil de confiance</strong> :
|
||
- Option "Ne plus demander sur cet appareil" → bypass 2FA pendant <strong>30 jours</strong>
|
||
- Révocable depuis paramètres compte
|
||
- Liste des appareils de confiance visible</p>
|
||
<p><strong>Justification</strong> :
|
||
- Sécurité renforcée sans coût SMS
|
||
- UX : appareil de confiance évite friction quotidienne
|
||
- Zitadel natif (0€)</p>
|
||
<hr />
|
||
<h3 id="17-tentatives-de-connexion">1.7 Tentatives de connexion</h3>
|
||
<p><strong>Règles</strong> :
|
||
- Maximum <strong>5 tentatives</strong> par période de <strong>15 minutes</strong>
|
||
- Blocage temporaire après 5 échecs
|
||
- Compteur reset automatique après 15 min
|
||
- Notification email si blocage (tentative suspecte)</p>
|
||
<p><strong>Déblocage</strong> :
|
||
- Automatique après 15 min
|
||
- Ou via lien "Mot de passe oublié"</p>
|
||
<p><strong>Justification</strong> :
|
||
- Anti brute-force
|
||
- Standard industrie (équilibre sécurité/UX)
|
||
- Zitadel natif (0€)</p>
|
||
<hr />
|
||
<h3 id="18-sessions-et-refresh-tokens">1.8 Sessions et refresh tokens</h3>
|
||
<p><strong>Durée de vie</strong> :
|
||
- <strong>Access token</strong> : 15 minutes
|
||
- <strong>Refresh token</strong> : 30 jours</p>
|
||
<p><strong>Rotation</strong> :
|
||
- Refresh token rotatif (nouveau token à chaque refresh)
|
||
- Ancien token invalidé immédiatement
|
||
- Détection token replay attack</p>
|
||
<p><strong>Extension automatique</strong> :
|
||
- Si app utilisée, session prolongée automatiquement
|
||
- Inactivité 30 jours → déconnexion</p>
|
||
<p><strong>Justification</strong> :
|
||
- Sécurité (token court-vie)
|
||
- UX (pas de reconnexion fréquente)
|
||
- Standard OAuth2/OIDC</p>
|
||
<hr />
|
||
<h3 id="19-multi-device">1.9 Multi-device</h3>
|
||
<p><strong>Décision</strong> : Sessions simultanées illimitées</p>
|
||
<p><strong>Gestion</strong> :
|
||
- 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"</p>
|
||
<p><strong>Alertes</strong> :
|
||
- Notification push + email si connexion depuis nouveau device
|
||
- Détection localisation suspecte (IP pays différent)</p>
|
||
<p><strong>Justification</strong> :
|
||
- UX maximale (écoute voiture + tablette maison + web)
|
||
- Sécurité via transparence (utilisateur voit tout)
|
||
- Coût : table sessions PostgreSQL</p>
|
||
<hr />
|
||
<h3 id="110-recuperation-de-compte">1.10 Récupération de compte</h3>
|
||
<p><strong>Méthode</strong> : Email uniquement</p>
|
||
<p><strong>Processus</strong> :
|
||
1. Utilisateur clique "Mot de passe oublié"
|
||
2. Email avec lien de reset envoyé
|
||
3. Lien expire après <strong>1 heure</strong>
|
||
4. Page de reset : nouveau mot de passe (validation règles)
|
||
5. Confirmation + déconnexion tous devices (sauf celui en cours)</p>
|
||
<p><strong>Notifications</strong> :
|
||
- Email immédiat si changement mot de passe
|
||
- Push si changement depuis appareil non reconnu</p>
|
||
<p><strong>Limite</strong> :
|
||
- Maximum <strong>3 demandes/heure</strong> (anti-spam)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Standard sécurité
|
||
- Pas de coût SMS
|
||
- Protection contre attaque sociale</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-1">Récapitulatif Section 1</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="2-algorithme-de-recommandation">2. Algorithme de recommandation</h2>
|
||
<h3 id="21-classification-de-geo-pertinence">2.1 Classification de géo-pertinence</h3>
|
||
<p><strong>Décision</strong> : 3 types de contenus selon leur pertinence géographique</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Exemple</th>
|
||
<th>Pondération géo</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Géo-ancré</strong></td>
|
||
<td>Contenu lié à un lieu précis</td>
|
||
<td>Audio-guide monument, pub restaurant local</td>
|
||
<td>70%</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Géo-contextuel</strong></td>
|
||
<td>Pertinent dans une zone</td>
|
||
<td>Actualité régionale, événement local</td>
|
||
<td>50%</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Géo-neutre</strong></td>
|
||
<td>Universel, pas de lien géo</td>
|
||
<td>Podcast philosophie, musique</td>
|
||
<td>20%</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Qui décide</strong> :
|
||
- ✅ 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)</p>
|
||
<p><strong>Justification</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<h3 id="22-formule-de-scoring">2.2 Formule de scoring</h3>
|
||
<p><strong>Décision</strong> : Score combiné dynamique selon type de contenu</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Pondérations par type</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Poids géo</th>
|
||
<th>Poids intérêts</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Géo-ancré</td>
|
||
<td>0.7</td>
|
||
<td>0.1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Géo-contextuel</td>
|
||
<td>0.5</td>
|
||
<td>0.3</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Géo-neutre</td>
|
||
<td>0.2</td>
|
||
<td>0.6</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Paramètres</strong> :
|
||
- Distance max recommandée : <strong>200 km</strong>
|
||
- Dégradation : <strong>linéaire</strong> (1 - distance/200km)
|
||
- Rayon point GPS : <strong>500m</strong> (adapté au volume de contenu local)</p>
|
||
<p><strong>Tous ces paramètres sont configurables à chaud via interface admin.</strong></p>
|
||
<p><strong>Justification</strong> :
|
||
- Flexibilité totale selon type de contenu
|
||
- Linéaire = rattrapage naturel du contenu viral ancien
|
||
- Auditable via métriques engagement (moyenne/médiane)</p>
|
||
<hr />
|
||
<h3 id="23-score-dengagement-et-popularite">2.3 Score d'engagement et popularité</h3>
|
||
<p><strong>Décision</strong> : Intégration popularité avec poids 0.2</p>
|
||
<p><strong>Métriques</strong> :
|
||
- <strong>Taux de complétion</strong> : écoutes >80% / total écoutes (poids 0.5)
|
||
- <strong>Ratio likes</strong> : likes / écoutes (poids 0.3)
|
||
- <strong>Ratio abonnements</strong> : nouveaux abonnés après écoute / écoutes (poids 0.2)</p>
|
||
<p><strong>Seuil minimum</strong> :
|
||
- Minimum <strong>50 écoutes</strong> avant de considérer l'engagement
|
||
- Contenu <50 écoutes : score engagement = 0.5 (neutre)</p>
|
||
<p><strong>Contenu viral</strong> :
|
||
- Un contenu viral à Paris <strong>peut</strong> être proposé à Marseille
|
||
- Score géo faible compensé par score engagement élevé
|
||
- Paramétrable admin</p>
|
||
<p><strong>Dépréciation temporelle</strong> :
|
||
- Pas de dépréciation automatique
|
||
- Ratio linéaire = contenu ancien mais toujours apprécié reste pertinent</p>
|
||
<p><strong>Justification</strong> :
|
||
- Équilibre découverte / qualité
|
||
- Pas de pénalisation arbitraire des contenus anciens
|
||
- Coût : calculs sur métriques existantes</p>
|
||
<hr />
|
||
<h3 id="24-part-daleatoire-exploration">2.4 Part d'aléatoire (exploration)</h3>
|
||
<p><strong>Décision</strong> : 10% par défaut, paramétrable utilisateur</p>
|
||
<p><strong>Fonctionnement</strong> :
|
||
- 1 contenu sur 10 = tirage aléatoire (hors historique déjà écouté)
|
||
- Utilisateur peut ajuster : curseur 0% (aucun aléatoire) à 50% (exploration max)</p>
|
||
<p><strong>Curseur utilisateur</strong> :
|
||
- 🎯 <strong>0%</strong> : Personnalisé max (recommandations strictes)
|
||
- ⚖️ <strong>10%</strong> : Équilibré (défaut)
|
||
- 🎲 <strong>30%</strong> : Découverte élevée
|
||
- 🌍 <strong>50%</strong> : Découverte max (équivaut à national = découverte)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Évite la bulle de filtre
|
||
- Laisse l'utilisateur maître de son expérience
|
||
- Coût : variable aléatoire en algo</p>
|
||
<hr />
|
||
<h3 id="25-contenu-politique-version-mvp-simplifiee">2.5 Contenu politique (version MVP simplifiée)</h3>
|
||
<blockquote>
|
||
<p>⚠️ <strong>Note</strong> : La classification politique avancée (échelle gauche/droite, équilibrage imposé) a été reportée post-MVP. Voir <a href="#ANNEXE-POST-MVP">ANNEXE-POST-MVP.md</a> pour la version complète.</p>
|
||
</blockquote>
|
||
<p><strong>Décision MVP</strong> : Tag simple "Politique" sans classification idéologique</p>
|
||
<p><strong>Tagging</strong> :
|
||
- Créateur peut taguer son contenu comme "Politique" (optionnel)
|
||
- Tag "Politique" au même niveau que "Économie", "Sport", "Culture", etc.
|
||
- <strong>Pas de classification gauche/droite</strong>
|
||
- <strong>Pas d'équilibrage imposé</strong></p>
|
||
<p><strong>Filtrage utilisateur</strong> :
|
||
- Option paramètres : <strong>"Masquer contenu politique"</strong>
|
||
- Si activé → 0% de contenus tagués "Politique" dans le feed
|
||
- Par défaut : désactivé (tous contenus visibles)</p>
|
||
<p><strong>Justification MVP</strong> :
|
||
- <strong>Simplicité</strong> : Pas de modération politique coûteuse (~2000€/mois économisés)
|
||
- <strong>Neutralité technique</strong> : Aucun jugement éditorial sur orientation
|
||
- <strong>Risque minimal</strong> : Évite controverses et contentieux DSA au lancement
|
||
- <strong>Fonctionnel</strong> : Utilisateurs peuvent filtrer si souhaité</p>
|
||
<p><strong>Post-MVP</strong> :
|
||
- Classification avancée possible si forte demande utilisateurs
|
||
- Nécessite ressources modération dédiées et audit DSA</p>
|
||
<hr />
|
||
<h3 id="26-mode-kids-13-15-ans">2.6 Mode Kids (13-15 ans)</h3>
|
||
<p><strong>Décision</strong> : Mode optionnel pour adolescents 13-15 ans uniquement</p>
|
||
<blockquote>
|
||
<p>⚠️ <strong>Note</strong> : Âge minimum d'inscription = <strong>13 ans</strong> (obligation légale EU). Pas d'utilisateurs <13 ans sur la plateforme.</p>
|
||
</blockquote>
|
||
<p><strong>Tranche concernée</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Tranche</th>
|
||
<th>Description</th>
|
||
<th>Contenus autorisés</th>
|
||
<th>Restrictions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>13-15 ans</strong></td>
|
||
<td>Collège</td>
|
||
<td>Contenus "Tous publics" uniquement</td>
|
||
<td>Filtrage 16+ et 18+</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Activation</strong> :
|
||
- ❌ <strong>Pas d'activation automatique</strong> (tous les utilisateurs ont ≥13 ans)
|
||
- ✅ <strong>Activation manuelle</strong> via toggle paramètres
|
||
- ✅ Parents peuvent activer pour leurs enfants 13-15 ans
|
||
- ✅ Utilisateur peut désactiver à tout moment</p>
|
||
<p><strong>Filtrage quand Mode Kids activé</strong> :
|
||
- ✅ Contenus "Tous publics" uniquement
|
||
- ❌ Exclusion contenus 16+ et 18+
|
||
- ❌ Pas de contenu politique (automatiquement filtré)
|
||
- ❌ Pas de publicité (ou uniquement pub validée manuellement)</p>
|
||
<p><strong>Interface</strong> :
|
||
- Interface standard (pas d'interface dédiée enfants pour MVP)
|
||
- Filtrage algorithmique des contenus inappropriés</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Conformité légale</strong> : Âge minimum 13 ans (RGPD, DSA)
|
||
- <strong>Simplicité MVP</strong> : Un seul mode optionnel vs 4 tranches d'âge
|
||
- <strong>Protection mineurs</strong> : Filtrage contenus adultes pour 13-15 ans
|
||
- <strong>Flexibilité</strong> : Parents décident d'activer ou non</p>
|
||
<hr />
|
||
<h3 id="27-declenchement-geographique">2.7 Déclenchement géographique</h3>
|
||
<p><strong>Décision</strong> : Notification au passage, pas d'anticipation</p>
|
||
<p><strong>Fonctionnement</strong> :
|
||
1. Utilisateur passe à <500m d'un point GPS (contenu géo-ancré)
|
||
2. <strong>Notification sonore</strong> (bip court) + <strong>visuelle</strong> (logo selon type)
|
||
3. Types de logos : 📍 Info, 🏛️ Culturel, 🍴 Commercial, 🎭 Événement
|
||
4. Délai réaction utilisateur : <strong>5 secondes</strong> pour accepter (bouton volant ou commande vocale)
|
||
5. Si accepté → lecture immédiate
|
||
6. Si ignoré → contenu proposé normalement en file d'attente</p>
|
||
<p><strong>Publicités</strong> :
|
||
- ⚠️ <strong>Jamais d'interruption</strong> de contenu en cours
|
||
- Pub s'intercale <strong>entre deux séquences</strong> uniquement
|
||
- Notification pub : son différent (facultatif selon paramètres)</p>
|
||
<p><strong>Gestion demi-tour</strong> :
|
||
- Si utilisateur repart du point après notification → pas de nouvelle notification (déjà proposé)
|
||
- Réinitialisation après 24h</p>
|
||
<p><strong>Justification</strong> :
|
||
- Respect écoute en cours (pas de coupure brutale)
|
||
- UX fluide (utilisateur garde contrôle)
|
||
- Simplicité technique (pas de prédiction trajectoire)</p>
|
||
<hr />
|
||
<h3 id="28-historique-et-repropositon">2.8 Historique et repropositon</h3>
|
||
<p><strong>Décision</strong> : Pas de reproposition sauf contenu partiel</p>
|
||
<p><strong>Règles</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>État écoute</th>
|
||
<th>Completion</th>
|
||
<th>Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Écouté complètement</strong></td>
|
||
<td>>80%</td>
|
||
<td>❌ Ne jamais reproposer (sauf flag <code>replayable = true</code> pour audio-guides)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Skippé rapidement</strong></td>
|
||
<td><10s</td>
|
||
<td>❌ Ne pas reproposer</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Partiellement écouté</strong></td>
|
||
<td>10-80%</td>
|
||
<td>✅ Reproposer avec reprise position (<code>last_position_seconds</code>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Stockage historique</strong> :
|
||
- Table <code>user_content_history</code> (user_id, content_id, completion_rate, last_position, listened_at)
|
||
- Historique <strong>illimité</strong> (PostgreSQL)
|
||
- Algorithme considère les <strong>100 derniers</strong> pour optimisation requêtes
|
||
- Export complet disponible (RGPD)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Découverte maximale (pas de redites)
|
||
- Respect erreurs de clic (contenu partiel = 2nde chance)
|
||
- Coût stockage négligeable (PostgreSQL scalable)</p>
|
||
<hr />
|
||
<h3 id="29-parametrabilite-admin-interface-dashboard">2.9 Paramétrabilité admin (interface dashboard)</h3>
|
||
<p><strong>Décision</strong> : Tous paramètres scoring exposés + A/B testing</p>
|
||
<p><strong>Paramètres configurables à chaud</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Paramètre</th>
|
||
<th>Plage</th>
|
||
<th>Défaut</th>
|
||
<th>Unité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>poids_geo_ancre</code></td>
|
||
<td>0.5 - 1.0</td>
|
||
<td>0.7</td>
|
||
<td>%</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>poids_geo_contextuel</code></td>
|
||
<td>0.3 - 0.7</td>
|
||
<td>0.5</td>
|
||
<td>%</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>poids_geo_neutre</code></td>
|
||
<td>0.0 - 0.4</td>
|
||
<td>0.2</td>
|
||
<td>%</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>poids_engagement</code></td>
|
||
<td>0.0 - 0.5</td>
|
||
<td>0.2</td>
|
||
<td>%</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>part_aleatoire_global</code></td>
|
||
<td>0.0 - 0.3</td>
|
||
<td>0.1</td>
|
||
<td>%</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>distance_max_km</code></td>
|
||
<td>50 - 500</td>
|
||
<td>200</td>
|
||
<td>km</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>rayon_gps_point_m</code></td>
|
||
<td>100 - 2000</td>
|
||
<td>500</td>
|
||
<td>m</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>seuil_min_ecoutes_engagement</code></td>
|
||
<td>10 - 200</td>
|
||
<td>50</td>
|
||
<td>nb</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Application changements</strong> :
|
||
- Immédiat : nouveaux calculs utilisent nouvelle config
|
||
- Aucun recalcul batch (coût CPU)
|
||
- Version config trackée (git-like)
|
||
- Rollback 1 clic</p>
|
||
<p><strong>A/B Testing</strong> :
|
||
- 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</p>
|
||
<p><strong>Audit engagement</strong> :
|
||
- Métriques clés : moyenne/médiane temps d'écoute par session
|
||
- Graphiques : évolution engagement selon config
|
||
- Export CSV pour analyse externe</p>
|
||
<p><strong>Justification</strong> :
|
||
- Optimisation continue sans redéploiement
|
||
- Data-driven decisions (métriques objectives)
|
||
- Coût : dashboard admin à développer (one-time)</p>
|
||
<hr />
|
||
<h3 id="210-parametrabilite-utilisateur">2.10 Paramétrabilité utilisateur</h3>
|
||
<p><strong>Décision</strong> : Curseurs avancés avec profils sauvegardables</p>
|
||
<p><strong>Niveaux de personnalisation</strong> :</p>
|
||
<p><strong>Curseurs disponibles</strong> :
|
||
- 📍 <strong>Géolocalisation</strong> : Local ← slider → National (découverte = national)
|
||
- 🎲 <strong>Découverte</strong> : 0% ← slider → 50% (part aléatoire)
|
||
- ⚖️ <strong>Politique</strong> : Masquer / Équilibré / Mes préférences</p>
|
||
<p><strong>Profils sauvegardables</strong> :
|
||
- 🚗 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é</p>
|
||
<p><strong>Synchronisation</strong> :
|
||
- ✅ Sync profils entre devices (cloud PostgreSQL)
|
||
- ❌ Pas de partage profils entre utilisateurs (famille)
|
||
- Auto-switch selon context (détection trajet récurrent via GPS)</p>
|
||
<p><strong>Sécurité conduite</strong> :
|
||
- ⚠️ <strong>Blocage modification si vitesse GPS >10 km/h</strong>
|
||
- Warning au lancement app : "Configurez avant de prendre la route"
|
||
- Modifications uniquement app arrêtée/passager</p>
|
||
<p><strong>Justification</strong> :
|
||
- Utilisateur maître de son expérience
|
||
- Contextes d'usage différents (quotidien vs voyage)
|
||
- Sécurité routière (pas de distraction)</p>
|
||
<hr />
|
||
<h3 id="211-medias-traditionnels">2.11 Médias traditionnels</h3>
|
||
<p><strong>Décision</strong> : Ouverture aux médias établis</p>
|
||
<p><strong>Médias autorisés</strong> :
|
||
- 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.</p>
|
||
<p><strong>Format contenus</strong> :
|
||
- Flashs info géolocalisés (actualité régionale)
|
||
- Chroniques thématiques (culture, économie, sport)
|
||
- Éditos et débats (classification politique appliquée)</p>
|
||
<p><strong>Validation</strong> :
|
||
- Compte média vérifié (badge ✓)
|
||
- Pas de validation 3 premiers contenus (confiance établie)
|
||
- Modération a posteriori uniquement</p>
|
||
<p><strong>Monétisation</strong> :
|
||
- Partage revenus pub standard (même conditions créateurs)
|
||
- Possibilité sponsoring direct (pas via plateforme)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Crédibilité plateforme (contenus professionnels)
|
||
- Diversité éditoriale
|
||
- Attractivité grand public (noms reconnus)</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-2">Récapitulatif Section 2</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="3-centres-dinteret-et-jauges">3. Centres d'intérêt et jauges</h2>
|
||
<h3 id="31-evolution-des-jauges">3.1 Évolution des jauges</h3>
|
||
<p><strong>Décision</strong> : Système simple avec valeurs fixes</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Impact jauge</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Like automatique renforcé (≥80% écoute)</strong></td>
|
||
<td>+2%</td>
|
||
<td>Signal fort d'intérêt (écoute quasi-complète)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Like automatique standard (30-79% écoute)</strong></td>
|
||
<td>+1%</td>
|
||
<td>Signal modéré d'intérêt</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Like explicite (manuel)</strong></td>
|
||
<td>+2%</td>
|
||
<td>Signal fort, cumulable avec auto</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Abonnement créateur</strong></td>
|
||
<td>+5% sur tous ses tags</td>
|
||
<td>Signal très fort d'affinité</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Skip rapide (<10s)</strong></td>
|
||
<td>-0.5%</td>
|
||
<td>Désintérêt marqué</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Skip tardif (≥30%)</strong></td>
|
||
<td>0%</td>
|
||
<td>Neutre (contenu essayé suffisamment)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Paramètres techniques</strong> :
|
||
- Les jauges sont bornées strictement entre <strong>0% et 100%</strong>
|
||
- 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</p>
|
||
<p><strong>Exemple de calcul</strong> :</p>
|
||
<pre><code>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%
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Like automatique</strong> : Reflète l'engagement réel (voir <a href="#../adr/010-commandes-volant">ADR-010</a>)
|
||
- <strong>Sécurité routière</strong> : Pas d'action complexe en conduite
|
||
- <strong>Prévisibilité</strong> : Règles claires et déterministes
|
||
- <strong>Coût minimal</strong> : Calculs simples en backend
|
||
- <strong>Fiabilité</strong> : Pas d'edge cases complexes
|
||
- <strong>Ajustable</strong> : Valeurs modifiables via dashboard admin si besoin</p>
|
||
<hr />
|
||
<h3 id="32-jauge-initiale">3.2 Jauge initiale</h3>
|
||
<p><strong>Décision</strong> : Démarrage neutre à 50%, pas de questionnaire</p>
|
||
<p><strong>À l'inscription</strong> :
|
||
- Toutes les jauges d'intérêt sont initialisées à <strong>50%</strong>
|
||
- Pas de questionnaire onboarding (friction zéro)
|
||
- L'algorithme apprend naturellement via les premières écoutes</p>
|
||
<p><strong>Catégories disponibles</strong> :
|
||
- Automobile
|
||
- Voyage
|
||
- Famille
|
||
- Amour
|
||
- Musique
|
||
- Économie
|
||
- Cryptomonnaie
|
||
- Politique
|
||
- Culture générale
|
||
- Sport
|
||
- Technologie
|
||
- Santé
|
||
- <em>... (extensible)</em></p>
|
||
<p><strong>Cold start (premiers jours)</strong> :
|
||
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</p>
|
||
<p><strong>Alternative optionnelle (post-MVP)</strong> :
|
||
- Questionnaire <strong>optionnel</strong> 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</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Inscription ultra-rapide</strong> : pas de questionnaire = moins de churn
|
||
- <strong>Découverte naturelle</strong> : l'algorithme apprend en quelques écoutes
|
||
- <strong>Équitable</strong> : pas de biais initial vers certains créateurs
|
||
- <strong>Comportement déterministe</strong> : facile à tester et débugger
|
||
- <strong>Cold start acceptable</strong> : à 50%, tous les contenus ont une chance égale initialement</p>
|
||
<hr />
|
||
<h3 id="33-degradation-temporelle">3.3 Dégradation temporelle</h3>
|
||
<p><strong>Décision</strong> : Pas de dégradation automatique</p>
|
||
<p>Les jauges <strong>ne diminuent jamais</strong> avec le temps de manière automatique.</p>
|
||
<p><strong>Règle</strong> :
|
||
- Une jauge ne change <strong>que par les actions utilisateur</strong> (like, écoute, skip)
|
||
- Pas de cron job de dégradation périodique
|
||
- Pas de "rafraîchissement" artificiel</p>
|
||
<p><strong>Scénario illustratif</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Si utilisateur inactif longtemps</strong> :
|
||
- 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</p>
|
||
<p><strong>Alternative utilisateur (contrôle explicite)</strong> :
|
||
- 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.)</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Principe KISS</strong> (Keep It Simple, Stupid)
|
||
- <strong>Coût 0</strong> : pas de batch nocturne, pas de calculs temporels
|
||
- <strong>Fiabilité maximale</strong> : pas de bugs de fuseaux horaires, dates, etc.
|
||
- <strong>UX prévisible</strong> : jauge = reflet des actions, pas d'automatisme caché
|
||
- <strong>Respect historique</strong> : si utilisateur aimait X depuis 2 ans, pourquoi "oublier" ?
|
||
- <strong>Évolution naturelle</strong> : les actions récentes suffisent à faire évoluer les jauges</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-3">Récapitulatif Section 3</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="4-creation-et-publication-de-contenu">4. Création et publication de contenu</h2>
|
||
<h3 id="41-upload-et-encodage">4.1 Upload et encodage</h3>
|
||
<p><strong>Décision</strong> : Formats universels avec encodage asynchrone</p>
|
||
<p><strong>Formats acceptés</strong> :
|
||
- ✅ MP3 (<code>.mp3</code>)
|
||
- ✅ AAC (<code>.aac</code>, <code>.m4a</code>)
|
||
- ❌ WAV, FLAC (trop lourds, inutiles en voiture)</p>
|
||
<p><strong>Limites</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Paramètre</th>
|
||
<th>Valeur</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Taille maximale</strong></td>
|
||
<td>200 MB</td>
|
||
<td>~4h de podcast à 128 kbps</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Durée maximale</strong></td>
|
||
<td>4 heures</td>
|
||
<td>Suffisant pour podcasts longs</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Validation format</strong></td>
|
||
<td>Client + backend</td>
|
||
<td>Double sécurité</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Pipeline d'encodage</strong> :</p>
|
||
<pre><code>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"
|
||
</code></pre>
|
||
<p><strong>Temps d'encodage estimé</strong> :
|
||
- Contenu 5 min → ~30 secondes
|
||
- Podcast 1h → ~5 minutes
|
||
- Podcast 4h → ~20 minutes</p>
|
||
<p><strong>Profils Opus générés</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Qualité</th>
|
||
<th>Bitrate</th>
|
||
<th>Usage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Basse</td>
|
||
<td>24 kbps</td>
|
||
<td>2G/Edge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Standard</td>
|
||
<td>48 kbps</td>
|
||
<td>3G (défaut)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Haute</td>
|
||
<td>64 kbps</td>
|
||
<td>4G/5G</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Écoute accélérée</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Vitesse</th>
|
||
<th>Usage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0.75x</td>
|
||
<td>Compréhension difficile (accent, technique)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1.0x</td>
|
||
<td>Normal (défaut)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1.25x</td>
|
||
<td>Gain léger</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1.5x</td>
|
||
<td>Podcasts longs</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2.0x</td>
|
||
<td>Survol rapide (modérateurs)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Disponible pour</strong> :
|
||
- ✅ Modérateurs (validation rapide : 30s → 15s à 2x)
|
||
- ✅ Auditeurs (tous les contenus)
|
||
- ✅ Standard industrie (YouTube, Spotify, Apple Podcasts)</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Simplicité</strong> : 2 formats couvrent 95% des cas d'usage
|
||
- <strong>Coût optimisé</strong> : pas de conversion WAV/FLAC lourds
|
||
- <strong>Stockage réduit</strong> : suppression original après encodage
|
||
- <strong>Scalabilité</strong> : workers horizontalement (Kubernetes jobs)
|
||
- <strong>Productivité</strong> : écoute accélérée = double productivité modération</p>
|
||
<hr />
|
||
<h3 id="42-metadonnees-obligatoires">4.2 Métadonnées obligatoires</h3>
|
||
<p><strong>Décision</strong> : Minimaliste pour réduire friction</p>
|
||
<p><strong>Champs obligatoires</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Champ</th>
|
||
<th>Format</th>
|
||
<th>Validation</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Titre</strong></td>
|
||
<td>5-100 caractères</td>
|
||
<td>Alphanumérique + ponctuation basique</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Type géo</strong></td>
|
||
<td>Enum</td>
|
||
<td>Ancré / Contextuel / Neutre</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Zone diffusion</strong></td>
|
||
<td>Composite</td>
|
||
<td>Voir détails ci-dessous</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tags</strong></td>
|
||
<td>Enum</td>
|
||
<td>1 à 3 parmi liste prédéfinie</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Classification âge</strong></td>
|
||
<td>Enum</td>
|
||
<td>Tout public / 13+ / 16+ / 18+</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Zone de diffusion (obligatoire)</strong> :</p>
|
||
<p>Options mutuellement exclusives :
|
||
- <strong>Point GPS</strong> : latitude + longitude + rayon (100m à 10km)
|
||
- <strong>Ville</strong> : sélection dans référentiel INSEE
|
||
- <strong>Département</strong> : sélection liste
|
||
- <strong>Région</strong> : sélection liste
|
||
- <strong>National</strong> : France entière</p>
|
||
<p><strong>Tags disponibles</strong> (1 à 3 obligatoires) :
|
||
- Automobile
|
||
- Voyage
|
||
- Famille
|
||
- Amour
|
||
- Musique
|
||
- Économie
|
||
- Cryptomonnaie
|
||
- Politique
|
||
- Culture générale
|
||
- Sport
|
||
- Technologie
|
||
- Santé</p>
|
||
<p><strong>Champs optionnels</strong> :
|
||
- ❌ Description (ajout ultérieur)
|
||
- ❌ Image couverture (génération auto)</p>
|
||
<p><strong>Image de couverture par défaut</strong> :</p>
|
||
<p>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)</p>
|
||
<p><strong>Exemple de publication</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Friction minimale</strong> : 5 champs max = 2 min de publication
|
||
- <strong>Publication rapide</strong> : pas de blocage sur description/image
|
||
- <strong>Coût 0</strong> : pas de génération IA au MVP
|
||
- <strong>Évolutif</strong> : champs optionnels ajoutables ultérieurement</p>
|
||
<hr />
|
||
<h3 id="43-validation-des-3-premiers-contenus">4.3 Validation des 3 premiers contenus</h3>
|
||
<p><strong>Décision</strong> : Validation manuelle par équipe modération RoadWave</p>
|
||
<p><strong>Processus nouveau créateur</strong> :</p>
|
||
<ol>
|
||
<li>Créateur upload ses 3 premiers contenus</li>
|
||
<li>Contenus passent en <strong>file d'attente modération</strong></li>
|
||
<li>Modérateur junior RoadWave :</li>
|
||
<li>Écoute 30 secondes (ou 15s à 2x)</li>
|
||
<li>Vérifie métadonnées</li>
|
||
<li>Valide ou rejette avec raison</li>
|
||
<li>Si accepté : contenu publié + notification créateur</li>
|
||
<li>Si refusé : notification avec raison détaillée + lien vers règles</li>
|
||
<li>Après 3 contenus validés : créateur passe en <strong>statut vérifié</strong></li>
|
||
</ol>
|
||
<p><strong>Critères de validation</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Critère</th>
|
||
<th>Détails</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Qualité audio</strong></td>
|
||
<td>Compréhensible (pas de grésillement excessif)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Respect règles</strong></td>
|
||
<td>Pas de contenu prohibé évident (haine, spam, illégal)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Classification âge</strong></td>
|
||
<td>Cohérente avec contenu écouté</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tags pertinents</strong></td>
|
||
<td>Correspondance minimale avec contenu</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Zone diffusion</strong></td>
|
||
<td>Cohérente (pas "Tour Eiffel" avec zone "National")</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Délai de validation</strong> :
|
||
- Objectif : <strong>24-48h</strong> (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"</p>
|
||
<p><strong>Notification créateur</strong> :</p>
|
||
<p><strong>Si accepté</strong> :
|
||
- 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é"</p>
|
||
<p><strong>Si refusé</strong> :
|
||
- 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</p>
|
||
<p><strong>Après 3 validations</strong> :</p>
|
||
<p>Créateur obtient <strong>statut "Vérifié"</strong> :
|
||
- Badge ✓ visible sur profil
|
||
- Contenus futurs publiés <strong>immédiatement</strong> (modération a posteriori uniquement)
|
||
- Modération seulement si signalé par utilisateurs</p>
|
||
<p><strong>Outils modérateur</strong> :
|
||
- É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)</p>
|
||
<p><strong>Modération communautaire (post-MVP)</strong> :</p>
|
||
<p>⚠️ <strong>Non implémenté au MVP</strong> (complexité juridique)</p>
|
||
<p>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)</p>
|
||
<p><strong>Justification décision MVP</strong> :
|
||
- <strong>Responsabilité juridique</strong> : plateforme reste responsable (DSA EU)
|
||
- <strong>Qualité garantie</strong> : modérateurs formés et mandatés
|
||
- <strong>Anti-spam efficace</strong> : bloque 95% des abus dès le début
|
||
- <strong>Coût raisonnable</strong> : 30s × 3 contenus = 1.5 min/créateur
|
||
- <strong>UX acceptable</strong> : délai 24-48h expliqué clairement
|
||
- <strong>Pas de validation par pairs</strong> au MVP = évite risques juridiques (collusion, compétence, conflits)</p>
|
||
<hr />
|
||
<h3 id="44-modification-et-suppression">4.4 Modification et suppression</h3>
|
||
<p><strong>Décision</strong> : Modification métadonnées uniquement, suppression immédiate</p>
|
||
<p><strong>Modification autorisée</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Élément</th>
|
||
<th>Modifiable</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Titre</strong></td>
|
||
<td>✅</td>
|
||
<td>Correction coquilles</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Description</strong></td>
|
||
<td>✅</td>
|
||
<td>Si ajoutée ultérieurement</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tags</strong></td>
|
||
<td>✅</td>
|
||
<td>Ajustement pertinence</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Image couverture</strong></td>
|
||
<td>✅</td>
|
||
<td>Personnalisation</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Audio</strong></td>
|
||
<td>❌</td>
|
||
<td>Intégrité contenu</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Zone diffusion</strong></td>
|
||
<td>❌</td>
|
||
<td>Évite manipulation algo</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Type géo</strong></td>
|
||
<td>❌</td>
|
||
<td>Évite manipulation algo</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Classification âge</strong></td>
|
||
<td>❌</td>
|
||
<td>Sécurité mineurs</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Raisons restrictions</strong> :</p>
|
||
<p><strong>Audio non modifiable</strong> :
|
||
- Évite fraude : uploader contenu validé → remplacer par spam
|
||
- Intégrité : auditeurs doivent écouter ce qui a été validé</p>
|
||
<p><strong>Zone/Type non modifiables</strong> :
|
||
- É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)</p>
|
||
<p><strong>Classification non modifiable</strong> :
|
||
- Évite contournement : uploader "Tout public" → passer en "18+" sans revalidation
|
||
- Sécurité : garantit que classification a été vérifiée</p>
|
||
<p><strong>Si besoin de changer audio/zone/classification</strong> :
|
||
- Action : <strong>Supprimer contenu + republier</strong>
|
||
- Si créateur <3 contenus validés : retourne en file validation
|
||
- Si créateur ≥3 contenus validés : publication immédiate</p>
|
||
<p><strong>Suppression de contenu</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Aspect</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Délai</strong></td>
|
||
<td>Immédiat</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Réversibilité</strong></td>
|
||
<td>Non</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Historique auditeurs</strong></td>
|
||
<td>Marqué "Contenu supprimé par créateur"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Analytics plateforme</strong></td>
|
||
<td>Anonymisé et conservé</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Fichiers CDN</strong></td>
|
||
<td>Supprimés sous 24h</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Exemple scénario suppression</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Notifications suppression</strong> :
|
||
- 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"</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Simplicité</strong> : règles claires et non-ambiguës
|
||
- <strong>Sécurité</strong> : évite manipulations algorithme et contournements modération
|
||
- <strong>Contrôle créateur</strong> : liberté totale de supprimer (RGPD)
|
||
- <strong>Traçabilité</strong> : historique conservé pour analytics (anonymisé)
|
||
- <strong>Coût 0</strong> : pas de revalidation métadonnées</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-4">Récapitulatif Section 4</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="5-interactions-et-navigation">5. Interactions et navigation</h2>
|
||
<h3 id="51-file-dattente-et-commande-suivant">5.1 File d'attente et commande "Suivant"</h3>
|
||
<p><strong>Décision</strong> : Pré-calcul 5 contenus avec insertion prioritaire pour points géographiques</p>
|
||
<p><strong>File d'attente</strong> :
|
||
- <strong>5 contenus pré-calculés</strong> en cache (Redis)
|
||
- Recalcul automatique si :
|
||
- Déplacement >10km
|
||
- Toutes les 10 minutes (rafraîchissement contenu)
|
||
- File d'attente <3 contenus restants</p>
|
||
<p><strong>Insertion prioritaire géo-ancrée (mode voiture uniquement)</strong> :</p>
|
||
<p><strong>Détection</strong> :
|
||
- Calcul ETA (Estimated Time of Arrival) via API GPS native iOS/Android
|
||
- Notification déclenchée <strong>7 secondes avant</strong> d'arriver au point GPS
|
||
- Si vitesse < 5 km/h ET distance < 50m → notification immédiate
|
||
- ⚠️ <strong>App doit être ouverte</strong> (pas de détection en arrière-plan en mode voiture)</p>
|
||
<p><strong>Notification</strong> :
|
||
- <strong>Sonore uniquement</strong> : bip court ou son personnalisé RoadWave
|
||
- <strong>Visuelle minimale</strong> : icône selon type de contenu (🏛️ culture, 👨👩👧 famille, 🎵 musique, etc.)
|
||
- <strong>Compteur visible</strong> : 7...6...5...4...3...2...1 (décompte des secondes)
|
||
- <strong>Pas de texte affiché</strong> (éviter distraction conducteur)
|
||
- <strong>Pas de bouton "Annuler"</strong> : seul le bouton "Suivant" permet validation</p>
|
||
<p><strong>Actions utilisateur</strong> :
|
||
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)</p>
|
||
<p><strong>Si user n'appuie pas sur "Suivant"</strong> :
|
||
- Notification disparaît après 7 secondes
|
||
- Contenu géolocalisé est perdu (pas d'insertion dans file)
|
||
- Pas de nouveau contenu géolocalisé pendant <strong>10 minutes</strong> (éviter spam)</p>
|
||
<p><strong>Limitation anti-spam</strong> :
|
||
- Maximum <strong>6 contenus géolocalisés par heure</strong>
|
||
- 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</p>
|
||
<p><strong>Invalidation immédiate</strong> :
|
||
- Utilisateur change ses préférences (curseurs géo/découverte/politique)
|
||
- ⚠️ <strong>Modification bloquée si vitesse GPS >10 km/h</strong> (sécurité routière)
|
||
- Live démarre d'un créateur suivi dans la zone</p>
|
||
<p><strong>Implémentation</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Expérience fluide</strong> : pas de latence au clic "Suivant"
|
||
- <strong>Réactivité géo</strong> : contenu local inséré immédiatement
|
||
- <strong>Coût optimisé</strong> : recalcul uniquement si nécessaire
|
||
- <strong>Sécurité</strong> : pas de modification en conduite</p>
|
||
<hr />
|
||
<h3 id="512-mode-pieton-audio-guides">5.1.2 Mode piéton (audio-guides)</h3>
|
||
<p><strong>Décision</strong> : Notifications push en arrière-plan avec rayon large</p>
|
||
<p><strong>Contexte</strong> :
|
||
- 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
|
||
- ⚠️ <strong>Fonctionnalité optionnelle</strong> : requiert permission "localisation en arrière-plan" (activée par user)</p>
|
||
<p><strong>Détection</strong> :
|
||
- App peut être en arrière-plan (si permission accordée)
|
||
- Rayon de détection : <strong>200 mètres</strong> 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</p>
|
||
<p><strong>Notification push système</strong> :</p>
|
||
<p>Format :</p>
|
||
<pre><code>Titre : "Audio-guide à proximité"
|
||
Body : "[Nom du contenu] - [Nom créateur]"
|
||
Action : Tap → ouvre app sur le contenu
|
||
</code></pre>
|
||
<p>Exemple :</p>
|
||
<pre><code>Audio-guide à proximité
|
||
Musée du Louvre : La Joconde - @paris_museum
|
||
</code></pre>
|
||
<p><strong>Permissions requises</strong> :</p>
|
||
<p>⚠️ <strong>Important</strong> : Permission "Always Location" est <strong>optionnelle</strong> et demandée uniquement si user active le mode piéton dans settings.</p>
|
||
<p>iOS (<code>Info.plist</code>) :</p>
|
||
<pre><code class="language-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>
|
||
</code></pre>
|
||
<p>Android (<code>AndroidManifest.xml</code>) :</p>
|
||
<pre><code class="language-xml"><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||
</code></pre>
|
||
<p><strong>Disclosure avant demande permission</strong> (Android requis, iOS recommandé) :</p>
|
||
<p>Écran affiché avant demande permission "Always Location" :</p>
|
||
<pre><code>┌────────────────────────────────────────┐
|
||
│ 📍 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é│
|
||
└────────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Si user refuse</strong> :
|
||
- 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)</p>
|
||
<p><strong>Comportement après tap sur notification</strong> :
|
||
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)</p>
|
||
<p><strong>Basculement automatique voiture ↔ piéton</strong> :</p>
|
||
<p>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</p>
|
||
<p>Changements de mode :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Mode actuel</th>
|
||
<th>Vitesse détectée</th>
|
||
<th>Nouveau mode</th>
|
||
<th>Effet</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Piéton</td>
|
||
<td>≥ 5 km/h</td>
|
||
<td>Voiture</td>
|
||
<td>Notifications push → sonores + icône (app ouverte requise)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Voiture</td>
|
||
<td>< 5 km/h</td>
|
||
<td>Piéton</td>
|
||
<td>Notifications sonores → push arrière-plan</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Pas de popup confirmation</strong> :
|
||
- Basculement transparent et automatique
|
||
- User n'a rien à faire
|
||
- Hysteresis (10s) pour éviter basculements intempestifs</p>
|
||
<p><strong>Quota anti-spam mode piéton</strong> :
|
||
- Même limitation que mode voiture : <strong>6 contenus/heure</strong>
|
||
- Cooldown 10 min si notification ignorée (app pas ouverte après tap)</p>
|
||
<p><strong>Justification</strong> :
|
||
- ✅ 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</p>
|
||
<hr />
|
||
<h3 id="52-commande-precedent">5.2 Commande "Précédent"</h3>
|
||
<p><strong>Décision</strong> : Comportement smart selon progression écoute</p>
|
||
<p><strong>Règles</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Situation</th>
|
||
<th>Temps écouté</th>
|
||
<th>Action "Précédent"</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Début de contenu</strong></td>
|
||
<td><10 secondes</td>
|
||
<td>Retour au contenu précédent (position exacte)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Milieu/fin</strong></td>
|
||
<td>≥10 secondes</td>
|
||
<td>Replay contenu actuel depuis le début</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Premier de session</strong></td>
|
||
<td>N/A</td>
|
||
<td>Replay depuis début (rien avant)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Historique de navigation</strong> :
|
||
- <strong>10 contenus maximum</strong> en mémoire (Redis List)
|
||
- Structure : <code>[{content_id, position_seconds, listened_at}, ...]</code>
|
||
- FIFO : au-delà de 10, suppression du plus ancien</p>
|
||
<p><strong>Exemple scénario</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Interface (responsabilité front)</strong> :
|
||
- ❌ Pas de message UI
|
||
- ✅ Progress bar revient au début ou à position exacte
|
||
- ✅ Animation fluide (transition 0.3s)</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>UX intuitive</strong> : comportement standard Spotify/YouTube
|
||
- <strong>Pas de frustration</strong> : si début, vraiment revenir en arrière
|
||
- <strong>Simplicité</strong> : règle unique (seuil 10s)</p>
|
||
<hr />
|
||
<h3 id="53-interactions-au-volant-like-automatique-et-engagement">5.3 Interactions au volant : Like automatique et engagement</h3>
|
||
<blockquote>
|
||
<p>⚠️ <strong>Architecture Decision Record</strong> : Voir <a href="#../adr/010-commandes-volant">ADR-010</a> pour les détails techniques complets</p>
|
||
</blockquote>
|
||
<p><strong>Décision</strong> : Like automatique basé sur le temps d'écoute</p>
|
||
<p><strong>Problème technique identifié</strong> :
|
||
- iOS et Android ne supportent <strong>pas nativement</strong> 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 = <strong>non-intuitif</strong> et <strong>risques sécurité</strong> (regarder écran pour feedback)</p>
|
||
<hr />
|
||
<h4 id="commandes-au-volant-simplifiees">Commandes au volant simplifiées</h4>
|
||
<p><strong>Actions disponibles</strong> (100% compatibles tous véhicules) :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Commande physique</th>
|
||
<th>Action RoadWave</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Suivant</strong></td>
|
||
<td>Passer au contenu suivant</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Précédent</strong></td>
|
||
<td>Revenir au contenu précédent (règle 10s, voir section 5.2)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Play/Pause</strong></td>
|
||
<td>Pause/reprise lecture (fade out 0.3s)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Aucune action complexe au volant</strong> → Sécurité routière maximale.</p>
|
||
<hr />
|
||
<h4 id="like-automatique-implicite">Like automatique implicite</h4>
|
||
<p><strong>Principe</strong> : Le système détecte automatiquement l'intérêt utilisateur selon le temps d'écoute.</p>
|
||
<p><strong>Règles d'attribution</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Durée écoutée</th>
|
||
<th>Action automatique</th>
|
||
<th>Points jauge</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>≥ 80% du contenu</strong></td>
|
||
<td>Like renforcé</td>
|
||
<td>+2.0</td>
|
||
<td>Écoute quasi-complète = fort intérêt</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>30-79% du contenu</strong></td>
|
||
<td>Like standard</td>
|
||
<td>+1.0</td>
|
||
<td>Écoute significative = intérêt</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>< 30% du contenu</strong></td>
|
||
<td>Pas de like</td>
|
||
<td>0</td>
|
||
<td>Écoute trop courte</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Skip après <10s</strong></td>
|
||
<td>Signal négatif</td>
|
||
<td>-0.5</td>
|
||
<td>Désintérêt marqué</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Exemples concrets</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<hr />
|
||
<h4 id="actions-complementaires-app-a-larret">Actions complémentaires (app à l'arrêt)</h4>
|
||
<p><strong>Interface mobile</strong> (véhicule arrêté uniquement) :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Moyen</th>
|
||
<th>Effet</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Like explicite</strong></td>
|
||
<td>Bouton cœur</td>
|
||
<td>+2 points jauge (même si déjà liké auto)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Unlike</strong></td>
|
||
<td>Re-clic cœur (toggle)</td>
|
||
<td>-2 points jauge</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Abonnement</strong></td>
|
||
<td>Bouton "S'abonner" profil créateur</td>
|
||
<td>+5 points toutes jauges tags créateur</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Désabonnement</strong></td>
|
||
<td>Bouton "Se désabonner"</td>
|
||
<td>-5 points</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Signalement</strong></td>
|
||
<td>Menu contextuel "⋮"</td>
|
||
<td>Ouverture flux modération</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Feedback visuel</strong> :
|
||
- <strong>Like automatique</strong> : Badge discret "♥ Ajouté à vos favoris" (2s, bas de l'écran)
|
||
- <strong>Like explicite</strong> : Animation cœur rouge + vibration courte
|
||
- <strong>Abonnement</strong> : Animation étoile dorée + badge "Abonné ✓"</p>
|
||
<hr />
|
||
<h4 id="commandes-vocales-optionnel-si-carplayandroid-auto">Commandes vocales (optionnel, si CarPlay/Android Auto)</h4>
|
||
<p><strong>Disponible uniquement avec</strong> :
|
||
- Apple CarPlay (Siri)
|
||
- Android Auto (Google Assistant)
|
||
- ~30-40% du parc automobile EU (2026)</p>
|
||
<p><strong>Exemples de commandes</strong> :</p>
|
||
<pre><code>"Hey Siri, like ce podcast"
|
||
"OK Google, abonne-moi à ce créateur"
|
||
"Hey Siri, passe au contenu suivant"
|
||
"OK Google, signale ce contenu"
|
||
</code></pre>
|
||
<p><strong>Implémentation</strong> : Intents iOS/Android personnalisés (Sprint 5, post-MVP)</p>
|
||
<hr />
|
||
<h4 id="gestion-impacts-jauges-algorithme">Gestion impacts jauges (algorithme)</h4>
|
||
<p><strong>Like automatique</strong> :
|
||
- Like renforcé (≥80%) → <strong>+2% jauges</strong> de tous les tags du contenu
|
||
- Like standard (30-79%) → <strong>+1% jauges</strong> des tags du contenu
|
||
- Signal négatif (skip <10s) → <strong>-0.5% jauges</strong> des tags du contenu</p>
|
||
<p><strong>Actions explicites</strong> :
|
||
- Like manuel → <strong>+2% jauges</strong> (cumulable avec like auto)
|
||
- Unlike → <strong>-2% jauges</strong>
|
||
- Abonnement → <strong>+5% toutes jauges</strong> tags créateur
|
||
- Désabonnement → <strong>-5% toutes jauges</strong></p>
|
||
<p><strong>Persistance</strong> :
|
||
- Événements stockés en base (table <code>listen_events</code>)
|
||
- Mise à jour jauges : <strong>immédiate</strong> (Redis) + <strong>async batch</strong> (PostgreSQL)</p>
|
||
<hr />
|
||
<h4 id="implementation-technique">Implémentation technique</h4>
|
||
<p><strong>Backend</strong> (Go) :</p>
|
||
<pre><code class="language-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
|
||
}
|
||
</code></pre>
|
||
<p><strong>Mobile</strong> (iOS/Android) :</p>
|
||
<pre><code class="language-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
|
||
))
|
||
}
|
||
}
|
||
</code></pre>
|
||
<hr />
|
||
<h4 id="justification_13">Justification</h4>
|
||
<p><strong>Avantages</strong> :
|
||
- ✅ <strong>Sécurité routière maximale</strong> : aucune action complexe au volant
|
||
- ✅ <strong>UX intuitive</strong> : comportement standard industrie (Spotify, YouTube Music, Deezer)
|
||
- ✅ <strong>Compatibilité 100%</strong> : fonctionne sur tous véhicules, tous OS
|
||
- ✅ <strong>Engagement amélioré</strong> : tous les contenus écoutés génèrent des signaux
|
||
- ✅ <strong>Algorithme plus précis</strong> : données granulaires (30%, 50%, 80%, 100%)
|
||
- ✅ <strong>Simplicité développement</strong> : pas de workarounds complexes iOS/Android</p>
|
||
<p><strong>Inconvénients mitigés</strong> :
|
||
- ⚠️ Pas de like explicite en conduite → <strong>Mitigation</strong> : like automatique + vocal (CarPlay/Android Auto)
|
||
- ⚠️ Pas d'abonnement en conduite → <strong>Mitigation</strong> : liste "Créateurs à découvrir" dans app
|
||
- ⚠️ Like automatique peut surprendre → <strong>Mitigation</strong> : onboarding clair + unlike possible</p>
|
||
<hr />
|
||
<h4 id="communication-utilisateurs-onboarding">Communication utilisateurs (onboarding)</h4>
|
||
<p><strong>Écran onboarding 1</strong> :</p>
|
||
<pre><code>🚗 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]
|
||
</code></pre>
|
||
<p><strong>Écran onboarding 2</strong> :</p>
|
||
<pre><code>❤️ Likes automatiques
|
||
|
||
Pas besoin de liker manuellement :
|
||
si vous écoutez >50% d'un contenu,
|
||
on comprend que vous aimez !
|
||
|
||
[Suivant]
|
||
</code></pre>
|
||
<p><strong>Écran onboarding 3</strong> :</p>
|
||
<pre><code>⏸️ Commandes simples
|
||
|
||
Utilisez les boutons au volant :
|
||
• Suivant → Prochain contenu
|
||
• Précédent → Contenu d'avant
|
||
• Pause → Mettre en pause
|
||
|
||
[Commencer]
|
||
</code></pre>
|
||
<hr />
|
||
<h3 id="54-lecture-en-boucle-et-enchainement">5.4 Lecture en boucle et enchaînement</h3>
|
||
<p><strong>Décision</strong> : Passage automatique après 2s + insertion pub paramétrable</p>
|
||
<p><strong>Fin de contenu</strong> :
|
||
1. Audio termine → <strong>Timer 2 secondes</strong> 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</p>
|
||
<p><strong>Délai selon contexte</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Mode</th>
|
||
<th>Délai</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Standard</strong></td>
|
||
<td>2 secondes</td>
|
||
<td>Temps réaction confortable</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Mode Kids</strong></td>
|
||
<td>1 seconde</td>
|
||
<td>Attention courte enfants</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Live</strong></td>
|
||
<td>0 seconde</td>
|
||
<td>Enchaînement immédiat</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Insertion publicité</strong> :
|
||
- Pub s'insère <strong>pendant le délai de 2s</strong> (transition naturelle)
|
||
- Fréquence : <strong>paramétrable admin</strong> (défaut : 1 pub / 5 contenus)
|
||
- Message : "Publicité (15s)" puis lecture pub
|
||
- ⚠️ <strong>Jamais d'interruption</strong> d'un contenu en cours</p>
|
||
<p><strong>Publicité skippable</strong> :
|
||
- Durée minimale visionnage : <strong>paramétrable</strong> (défaut : 5 secondes)
|
||
- Bouton "Passer" apparaît après délai
|
||
- Métriques engagement : taux skip, durée écoute moyenne
|
||
- <strong>Like et abonnement autorisés sur pub</strong> (engagement créateur pub)</p>
|
||
<p><strong>Si aucun contenu disponible</strong> :
|
||
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</p>
|
||
<p><strong>Gestion erreurs</strong> :
|
||
- Échec chargement contenu suivant → <strong>retry 3× avec backoff exponentiel</strong>
|
||
- Si 3 échecs → message "Connexion instable, basculement mode offline"
|
||
- Mode offline → lecture contenus téléchargés uniquement</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Fluidité</strong> : enchaînement naturel sans action utilisateur
|
||
- <strong>Contrôle</strong> : possibilité annuler pendant délai
|
||
- <strong>Paramétrabilité pub</strong> : évite frustration excès publicité
|
||
- <strong>Engagement pub</strong> : like/abonnement autorisé = monétisation créateurs pub</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-5">Récapitulatif Section 5</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="6-publicites">6. Publicités</h2>
|
||
<h3 id="61-systeme-de-campagnes-publicitaires">6.1 Système de campagnes publicitaires</h3>
|
||
<p><strong>Décision</strong> : Interface self-service avec maîtrise budget et métriques détaillées</p>
|
||
<p><strong>Fonctionnalités publicitaire</strong> :</p>
|
||
<h4 id="creation-de-campagne">Création de campagne</h4>
|
||
<p><strong>Paramètres configurables</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Paramètre</th>
|
||
<th>Options</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Budget total</strong></td>
|
||
<td>Montant libre (min 50€)</td>
|
||
<td>Maîtrise coût total</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Durée campagne</strong></td>
|
||
<td>Date début/fin + étalement</td>
|
||
<td>Ex: 300€ sur 2 semaines</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Ciblage géographique</strong></td>
|
||
<td>Point GPS / Ville / Département / Région / National</td>
|
||
<td>Précision selon besoin</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Ciblage horaire</strong></td>
|
||
<td>Plages horaires (ex: 7h-9h, 17h-19h)</td>
|
||
<td>Optimisation trajet domicile-travail</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Centres d'intérêt</strong></td>
|
||
<td>Tags (ex: Automobile, Voyage)</td>
|
||
<td>Ciblage thématique</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tranche d'âge</strong></td>
|
||
<td>Tout public / 13+ / 16+ / 18+</td>
|
||
<td>Respect classifications</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Étalement budget</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Mode de paiement</strong> :
|
||
- ✅ Prépaiement obligatoire (évite impayés)
|
||
- ✅ Carte bancaire uniquement (Mangopay)
|
||
- ✅ Recharge automatique optionnelle (si budget <10%)</p>
|
||
<h4 id="validation-et-moderation">Validation et modération</h4>
|
||
<p><strong>Processus</strong> :
|
||
1. Publicitaire upload audio pub (formats : MP3, AAC)
|
||
2. <strong>Validation manuelle obligatoire</strong> (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</p>
|
||
<p><strong>Contenus interdits en pub</strong> :
|
||
- ❌ Alcool, tabac (réglementation française)
|
||
- ❌ Jeux d'argent
|
||
- ❌ Contenu politique (pendant campagnes électorales)
|
||
- ❌ Contenu sexuel ou violence
|
||
- ✅ Tous commerces/services légaux</p>
|
||
<h4 id="dashboard-metriques-engagement">Dashboard métriques engagement</h4>
|
||
<p><strong>Indicateurs temps réel</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Métrique</th>
|
||
<th>Description</th>
|
||
<th>Utilité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Impressions</strong></td>
|
||
<td>Nombre de diffusions</td>
|
||
<td>Volume exposition</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Écoutes complètes</strong></td>
|
||
<td>Pub écoutée >80%</td>
|
||
<td>Engagement réel</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Taux de skip</strong></td>
|
||
<td>% skip après délai min</td>
|
||
<td>Qualité contenu</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Durée moyenne écoute</strong></td>
|
||
<td>Secondes écoutées</td>
|
||
<td>Rétention attention</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Likes</strong></td>
|
||
<td>Nombre de likes</td>
|
||
<td>Appréciation contenu</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Abonnements</strong></td>
|
||
<td>Abonnements au créateur pub</td>
|
||
<td>Conversion forte</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Coût par écoute</strong></td>
|
||
<td>Budget / écoutes complètes</td>
|
||
<td>ROI campagne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Répartition géographique</strong></td>
|
||
<td>Heatmap diffusions</td>
|
||
<td>Validation ciblage</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Répartition horaire</strong></td>
|
||
<td>Graphique par heure</td>
|
||
<td>Optimisation horaires</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Métriques engagement avancées</strong> :
|
||
- <strong>Taux complétion par tranche d'âge</strong> : identifier audience réceptive
|
||
- <strong>Carte de chaleur GPS</strong> : visualiser zones forte écoute
|
||
- <strong>Comparatif campagnes</strong> : A/B testing créatifs publicitaires</p>
|
||
<p><strong>Export données</strong> :
|
||
- ✅ CSV/Excel pour analyse externe
|
||
- ✅ Graphiques interactifs (Chart.js)
|
||
- ✅ Rapport PDF automatique fin de campagne</p>
|
||
<h4 id="gestion-budget-et-alertes">Gestion budget et alertes</h4>
|
||
<p><strong>Suivi temps réel</strong> :
|
||
- 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)</p>
|
||
<p><strong>Ajustements en cours</strong> :
|
||
- ✅ Pause campagne (budget conservé)
|
||
- ✅ Prolonger campagne (recharge budget)
|
||
- ✅ Modifier ciblage horaire/géo (si <50% budget consommé)
|
||
- ❌ Modifier audio (nécessite nouvelle validation)</p>
|
||
<h4 id="systeme-dencheres-post-mvp">Système d'enchères (post-MVP)</h4>
|
||
<p><strong>Optionnel future</strong> :
|
||
- 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</p>
|
||
<p><strong>Justification décision MVP</strong> :
|
||
- Tarif fixe simple : 0.05€/écoute complète
|
||
- Pas de complexité enchères immédiatement
|
||
- Scalable : passage enchères ultérieur si demande forte</p>
|
||
<hr />
|
||
<h3 id="62-insertion-et-frequence">6.2 Insertion et fréquence</h3>
|
||
<p><strong>Décision</strong> : Paramétrable admin + respect expérience utilisateur</p>
|
||
<p><strong>Fréquence d'insertion</strong> :
|
||
- <strong>Défaut : 1 pub / 5 contenus</strong> (utilisateurs gratuits)
|
||
- <strong>Paramétrable admin</strong> : curseur 1/3 à 1/10
|
||
- <strong>Utilisateurs Premium</strong> : 0 pub (modèle sans publicité)</p>
|
||
<p><strong>Règles strictes</strong> :
|
||
- ⚠️ <strong>Jamais d'interruption</strong> contenu en cours
|
||
- Pub s'insère uniquement <strong>entre deux contenus</strong> (pendant délai 2s)
|
||
- Rotation : même pub max <strong>3 fois/jour</strong> par utilisateur (évite saturation)
|
||
- Limite : max <strong>6 pubs/heure</strong> par utilisateur (évite spam)</p>
|
||
<p><strong>Ciblage intelligent</strong> :
|
||
- 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)</p>
|
||
<p><strong>Volume audio normalisé</strong> :
|
||
- Pub normalisée à <strong>-14 LUFS</strong> (standard broadcast)
|
||
- Évite effet "pub trop forte" (frustration utilisateur)
|
||
- Validation automatique via FFmpeg lors encodage</p>
|
||
<hr />
|
||
<h3 id="63-caracteristiques-publicites">6.3 Caractéristiques publicités</h3>
|
||
<p><strong>Durée</strong> :
|
||
- Minimum : <strong>10 secondes</strong>
|
||
- Maximum : <strong>60 secondes</strong>
|
||
- Recommandé : <strong>15-30 secondes</strong> (sweet spot engagement)</p>
|
||
<p><strong>Skippable</strong> :
|
||
- Délai minimum obligatoire : <strong>5 secondes</strong> (paramétrable admin : 3-10s)
|
||
- Bouton "Passer la publicité" apparaît après délai
|
||
- Durée minimale comptabilisée pour facturation</p>
|
||
<p><strong>Facturation</strong> :
|
||
- <strong>Écoute complète</strong> (>80%) : 0.05€ facturé publicitaire
|
||
- <strong>Skip après délai min</strong> : 0.02€ (exposition partielle)
|
||
- <strong>Skip immédiat</strong> (<5s) : 0€ (pas d'engagement)</p>
|
||
<p><strong>Justification modèle tarif</strong> :
|
||
- Incitatif qualité : pub engageante = coût réduit
|
||
- Équitable : publicitaire paie pour attention réelle
|
||
- Transparent : dashboard montre écoutes complètes vs skips</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-6">Récapitulatif Section 6</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="7-radio-live">7. Radio live</h2>
|
||
<h3 id="71-demarrage-dun-live">7.1 Démarrage d'un live</h3>
|
||
<p><strong>Décision</strong> : Buffer 15s + notification abonnés + limite 8h</p>
|
||
<p><strong>Processus de démarrage</strong> :</p>
|
||
<ol>
|
||
<li>Créateur appuie "Démarrer live" dans l'app</li>
|
||
<li><strong>Vérification pré-live</strong> :</li>
|
||
<li>Connexion ≥1 Mbps upload (warning si insuffisant)</li>
|
||
<li>Micro autorisé</li>
|
||
<li>Zone diffusion déjà définie (ville, département, région, national)</li>
|
||
<li><strong>Buffer initial 15 secondes</strong> avant diffusion publique</li>
|
||
<li>Créateur parle pendant 15s → accumulation buffer serveur</li>
|
||
<li>Message créateur : "Live démarre dans 15s... Testez votre micro"</li>
|
||
<li>Permet vérifier qualité audio avant diffusion</li>
|
||
<li>Après 15s → <strong>Live public</strong>, auditeurs peuvent rejoindre</li>
|
||
</ol>
|
||
<p><strong>Notification abonnés</strong> :
|
||
- ✅ <strong>Push notification immédiate</strong> à 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
|
||
- <strong>Filtrage géographique</strong> : si abonné hors zone, pas de notif (évite frustration)</p>
|
||
<p><strong>Limite de durée</strong> :
|
||
- <strong>Maximum 8 heures</strong> 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)</p>
|
||
<p><strong>Métadonnées obligatoires</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Champ</th>
|
||
<th>Format</th>
|
||
<th>Validation</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Titre</strong></td>
|
||
<td>5-100 caractères</td>
|
||
<td>Ex: "Discussion politique en direct"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tags</strong></td>
|
||
<td>1-3 centres d'intérêt</td>
|
||
<td>Sélection liste prédéfinie</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Classification âge</strong></td>
|
||
<td>Enum</td>
|
||
<td>Tout public / 13+ / 16+ / 18+</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Zone diffusion</strong></td>
|
||
<td>Geo</td>
|
||
<td>Ville / Département / Région / National</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Contenus interdits en live</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
<th>Sanction</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Concert/spectacle</strong></td>
|
||
<td>Diffusion concert en direct depuis la salle</td>
|
||
<td>Strike 2 immédiat + ban temporaire</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Événement sportif payant</strong></td>
|
||
<td>Match, compétition avec droits TV</td>
|
||
<td>Strike 2 immédiat</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Œuvre protégée</strong></td>
|
||
<td>Film, série, musique en fond sans droits</td>
|
||
<td>Strike 1 + suppression live</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenu violent</strong></td>
|
||
<td>Agression, violence physique</td>
|
||
<td>Ban immédiat</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenu illégal</strong></td>
|
||
<td>Apologie terrorisme, pédopornographie</td>
|
||
<td>Ban définitif + signalement autorités</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Exemple usecase interdit</strong> :</p>
|
||
<pre><code>❌ 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
|
||
</code></pre>
|
||
<p><strong>Détection violations</strong> :
|
||
- <strong>Signalement utilisateurs</strong> : bouton "Signaler" accessible pendant live
|
||
- <strong>IA audio fingerprint</strong> : détection musique protégée en arrière-plan (post-MVP)
|
||
- <strong>Modération réactive</strong> : modérateurs peuvent écouter lives signalés en temps réel
|
||
- <strong>Coupure immédiate</strong> : modérateur peut arrêter live si contenu illégal évident</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Buffer 15s</strong> : équilibre entre test qualité et friction minimale
|
||
- <strong>Notification abonnés</strong> : engagement maximal, valeur ajoutée live
|
||
- <strong>8h max</strong> : couvre 99% cas usage (podcasts longs, émissions radio) sans abus
|
||
- <strong>Interdictions strictes</strong> : protection juridique plateforme (DSA EU, droits d'auteur)
|
||
- <strong>Coût</strong> : WebRTC ingestion + HLS distribution (réutilise infra existante)</p>
|
||
<hr />
|
||
<h3 id="72-arret-du-live">7.2 Arrêt du live</h3>
|
||
<p><strong>Décision</strong> : Compte à rebours 5s + tolérance déconnexion 60s + enregistrement auto</p>
|
||
<p><strong>Fin manuelle créateur</strong> :</p>
|
||
<ol>
|
||
<li>Créateur appuie "Arrêter live"</li>
|
||
<li><strong>Compte à rebours 5 secondes</strong> affiché</li>
|
||
<li>Message audio : "Ce live se termine dans 5... 4... 3... 2... 1"</li>
|
||
<li>Permet au créateur de faire un outro propre</li>
|
||
<li>Annulable pendant décompte (bouton "Annuler")</li>
|
||
<li>Timer atteint 0 → arrêt diffusion</li>
|
||
<li><strong>Traitement post-live automatique</strong> démarre (voir ci-dessous)</li>
|
||
</ol>
|
||
<p><strong>Fin automatique si déconnexion</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Durée coupure</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong><60 secondes</strong></td>
|
||
<td>Message auditeurs : "Connexion créateur perdue, reconnexion en cours..."</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>≥60 secondes</strong></td>
|
||
<td>Arrêt automatique live + message : "Le live est terminé suite à une coupure de connexion"</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Enregistrement automatique</strong> :</p>
|
||
<p>✅ <strong>Obligatoire et automatique</strong> (valeur ajoutée énorme)</p>
|
||
<p><strong>Processus</strong> :
|
||
1. Pendant live : enregistrement continu serveur (format Opus raw)
|
||
2. Fin live → <strong>job asynchrone</strong> (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. <strong>Publication automatique</strong> du replay :
|
||
- Titre : "[REPLAY] [Titre live original]"
|
||
- Même zone diffusion, tags, classification
|
||
- Disponible sous <strong>5-10 minutes</strong> après fin live
|
||
- Type géo : automatiquement "Géo-neutre" (replay = contenu pérenne)</p>
|
||
<p><strong>Options créateur</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Option</th>
|
||
<th>Défaut</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Publier replay automatiquement</strong></td>
|
||
<td>✅ OUI</td>
|
||
<td>Désactivable avant démarrage live</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Supprimer replay après coup</strong></td>
|
||
<td>✅ Possible</td>
|
||
<td>Suppression standard contenu</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Modifier replay</strong></td>
|
||
<td>❌ Non</td>
|
||
<td>Intégrité enregistrement</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Conservation fichier source</strong> :
|
||
- Opus raw conservé <strong>7 jours</strong> après fin live (backup)
|
||
- Suppression automatique après 7j (économie stockage)
|
||
- Si replay supprimé par créateur → fichier raw supprimé immédiatement</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Compte à rebours 5s</strong> : outro propre, pas de coupure brutale
|
||
- <strong>Tolérance 60s</strong> : évite arrêts intempestifs (tunnel, changement cellule)
|
||
- <strong>Enregistrement auto</strong> : valorisation contenu éphémère, génération contenu pérenne
|
||
- <strong>MP3 256 kbps</strong> : qualité optimale pour replay (vs 48 kbps live)
|
||
- <strong>Coût</strong> : stockage minimal (Opus → MP3 1× par live, puis suppression raw après 7j)</p>
|
||
<hr />
|
||
<h3 id="73-comportement-auditeur">7.3 Comportement auditeur</h3>
|
||
<p><strong>Décision</strong> : Buffer 15s + continuation hors zone + reconnexion au live actuel + écoute passive uniquement</p>
|
||
<p><strong>Buffer de synchronisation</strong> :</p>
|
||
<ul>
|
||
<li><strong>15 secondes</strong> entre créateur et auditeurs</li>
|
||
<li>Raisons :</li>
|
||
<li>Stabilité réseau mobile (3G/4G fluctuant)</li>
|
||
<li>Synchronisation approximative acceptable (pas besoin temps réel strict)</li>
|
||
<li>Permet buffering anticiper coupures courtes (tunnels)</li>
|
||
</ul>
|
||
<p><strong>Comparaison buffers</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Buffer</th>
|
||
<th>Avantages</th>
|
||
<th>Inconvénients</th>
|
||
<th>Décision</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>5s</td>
|
||
<td>Quasi temps réel</td>
|
||
<td>Instable 3G, coupures fréquentes</td>
|
||
<td>❌</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10s</td>
|
||
<td>Bon compromis</td>
|
||
<td>Légèrement juste pour 3G</td>
|
||
<td>❌</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15s</strong></td>
|
||
<td><strong>Stabilité optimale 3G/4G</strong></td>
|
||
<td>Léger décalage acceptable</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td>20s+</td>
|
||
<td>Très stable</td>
|
||
<td>Décalage trop perceptible</td>
|
||
<td>❌</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Zone géographique pendant live</strong> :</p>
|
||
<ul>
|
||
<li>✅ <strong>Continuation si sortie de zone</strong></li>
|
||
<li>Scénario : auditeur écoute live régional → sort du département → <strong>live continue</strong></li>
|
||
<li>Raisons :</li>
|
||
<li>Pas de coupure brutale (mauvaise UX)</li>
|
||
<li>Écoute engagée = terminer naturellement</li>
|
||
<li>Après fin live → algo normal (pas de contenus hors zone)</li>
|
||
</ul>
|
||
<p><strong>Reconnexion après coupure réseau</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Durée coupure</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong><90 secondes</strong></td>
|
||
<td>Reprend au live actuel (pas au buffer ancien) + saut temporel transparent</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>≥90 secondes</strong></td>
|
||
<td>Message : "Live en cours perdu, passage au contenu suivant" + algo propose contenu normal</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Interactions disponibles</strong> :</p>
|
||
<p><strong>Décision ferme</strong> : ❌ <strong>Aucun chat en direct, ni maintenant ni dans le futur</strong></p>
|
||
<p><strong>Raisons</strong> :
|
||
- <strong>Sécurité routière</strong> : pas de distraction en voiture (focus UX)
|
||
- <strong>Harcèlement</strong> : évite contenu haineux, insultes, trolling
|
||
- <strong>Modération</strong> : pas de coût modération temps réel (impossible à scale)
|
||
- <strong>Simplicité</strong> : écoute passive = expérience uniforme</p>
|
||
<p><strong>Actions autorisées pendant live</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Disponible</th>
|
||
<th>Effet</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Like</strong></td>
|
||
<td>✅</td>
|
||
<td>Bouton cœur interface mobile (véhicule arrêté)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Abonnement créateur</strong></td>
|
||
<td>✅</td>
|
||
<td>Bouton profil créateur (interface mobile)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Skip</strong></td>
|
||
<td>✅</td>
|
||
<td>Passe au contenu suivant, sort du live</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Précédent</strong></td>
|
||
<td>❌</td>
|
||
<td>Pas de sens sur live (flux temps réel)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Chat</strong></td>
|
||
<td>❌</td>
|
||
<td>Jamais implémenté (décision définitive)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Réactions emoji</strong></td>
|
||
<td>❌</td>
|
||
<td>Jamais implémenté (décision définitive)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Messages utilisateur</strong> :
|
||
- "💬 Les discussions ne sont pas disponibles sur RoadWave pour garantir votre sécurité en voiture et éviter le harcèlement."</p>
|
||
<p><strong>Justification décision définitive</strong> :
|
||
- <strong>UX cohérente</strong> : RoadWave = écoute en conduisant, pas réseau social interactif
|
||
- <strong>Bien-être</strong> : évite toxicité, harcèlement, haine (fléau réseaux sociaux)
|
||
- <strong>Juridique</strong> : pas de risque contentieux modération chat (DSA EU)
|
||
- <strong>Coût</strong> : 0€ infra chat, 0€ modération temps réel
|
||
- <strong>Différenciation</strong> : positionnement "audio safe" vs plateformes toxiques</p>
|
||
<hr />
|
||
<h3 id="74-architecture-technique">7.4 Architecture technique</h3>
|
||
<p><strong>Stack</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Flux détaillé</strong> :
|
||
1. <strong>Créateur</strong> → WebRTC OPUS 48 kbps vers serveur Go
|
||
2. <strong>Serveur Go</strong> → Conversion temps réel OPUS → segments HLS (.m3u8 + .ts)
|
||
3. <strong>Bunny CDN</strong> → Distribution HLS avec cache
|
||
4. <strong>Auditeurs</strong> → Lecture HLS native iOS/Android (buffer 15s)
|
||
5. <strong>Enregistrement parallèle</strong> → Opus raw stocké temporairement
|
||
6. <strong>Post-live</strong> → Job async : Opus → MP3 256 kbps → Publication replay</p>
|
||
<p><strong>Dépendances</strong> :
|
||
- ✅ <strong>Pion WebRTC</strong> (Go library, open source, MIT license)
|
||
- ✅ <strong>FFmpeg</strong> (conversion audio, LGPL/GPL)
|
||
- ✅ <strong>Bunny CDN</strong> (distribution HLS, pas Google/Cloudflare)
|
||
- ✅ <strong>PostgreSQL + Redis</strong> (métadonnées live + cache)</p>
|
||
<p><strong>Avantages</strong> :
|
||
- ✅ 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)</p>
|
||
<p><strong>Coût estimé</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Phase</th>
|
||
<th>Utilisateurs</th>
|
||
<th>Infra live</th>
|
||
<th>Coût/mois</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>MVP</strong></td>
|
||
<td>0-100K</td>
|
||
<td>1 instance Go (ingestion 100 lives simultanés)</td>
|
||
<td>+50€ (serveur) + bande passante CDN</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Growth</strong></td>
|
||
<td>100K-1M</td>
|
||
<td>3-5 instances Go (500 lives simultanés)</td>
|
||
<td>+200€ + bande passante</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Scale</strong></td>
|
||
<td>1M-10M</td>
|
||
<td>Kubernetes auto-scale (2000+ lives)</td>
|
||
<td>+1K€ + bande passante</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Bande passante</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-7">Récapitulatif Section 7</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="8-abonnements-et-notifications">8. Abonnements et notifications</h2>
|
||
<h3 id="81-impact-sur-lalgorithme">8.1 Impact sur l'algorithme</h3>
|
||
<p><strong>Décision</strong> : Boost +30% au score + reste dans le mix</p>
|
||
<p><strong>Boost de score abonnements</strong> :
|
||
- <strong>+30% au score final</strong> pour contenus d'un créateur suivi
|
||
- Application : multiplicateur sur le score calculé</p>
|
||
<pre><code>score_final_avec_boost = score_final × 1.3
|
||
</code></pre>
|
||
<p><strong>Reste dans le mix</strong> :
|
||
- ❌ <strong>Pas de priorité absolue</strong> (pas de file dédiée abonnements)
|
||
- ✅ Contenu suivi entre en <strong>compétition avec autres contenus</strong>
|
||
- ✅ Si créateur suivi publie contenu faible engagement → peut être battu par contenu viral non-suivi</p>
|
||
<p><strong>Exemple concret</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Cas où abonnement fait la différence</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Équilibre</strong> : valorise abonnements sans enfermer utilisateur
|
||
- <strong>Découverte</strong> : contenus viraux/locaux peuvent toujours émerger
|
||
- <strong>Prévisible</strong> : boost fixe, pas de logique opaque
|
||
- <strong>Coût 0</strong> : multiplicateur simple dans l'algo</p>
|
||
<hr />
|
||
<h3 id="82-notifications-contextuelles">8.2 Notifications contextuelles</h3>
|
||
<p><strong>Décision</strong> : Push adapté selon contexte (voiture vs à pied) + limite 10/jour</p>
|
||
<p><strong>Détection contexte utilisateur</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Contexte</th>
|
||
<th>Détection</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>En voiture</strong></td>
|
||
<td>Vitesse GPS >10 km/h</td>
|
||
<td>Notifications silencieuses (in-app uniquement) + commandes volant</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>À pied</strong></td>
|
||
<td>Vitesse GPS <5 km/h</td>
|
||
<td>Notifications push actives + interface tactile/vocale</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Notifications activées</strong> :</p>
|
||
<h4 id="en-voiture-mode-conduite">En voiture (mode conduite)</h4>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Événement</th>
|
||
<th>Notification</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Nouveau contenu créateur suivi</strong></td>
|
||
<td>In-app uniquement</td>
|
||
<td>Badge compteur, pas de push (sécurité)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Live créateur suivi</strong></td>
|
||
<td>In-app uniquement</td>
|
||
<td>Badge compteur, pas de push</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Point d'intérêt proche</strong></td>
|
||
<td>Audio notification</td>
|
||
<td>Bip + annonce vocale : "Audio-guide disponible"</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4 id="a-pied-mode-pieton">À pied (mode piéton)</h4>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Événement</th>
|
||
<th>Notification</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Nouveau contenu créateur suivi</strong></td>
|
||
<td>✅ Push</td>
|
||
<td>Si utilisateur dans zone géo du contenu</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Live créateur suivi</strong></td>
|
||
<td>✅ Push</td>
|
||
<td>Si utilisateur dans zone géo</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Audio-guide disponible</strong></td>
|
||
<td>✅ Push</td>
|
||
<td>"📍 Audio-guide disponible : [Lieu]"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Séquence suivante suggérée</strong></td>
|
||
<td>Audio notification</td>
|
||
<td>Annonce vocale : "Pièce suivante disponible"</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Format notifications</strong> :</p>
|
||
<p><strong>Nouveau contenu</strong> :</p>
|
||
<pre><code>🎧 [Nom créateur] a publié : "[Titre contenu]"
|
||
Tap pour écouter
|
||
</code></pre>
|
||
<p><strong>Live en direct</strong> :</p>
|
||
<pre><code>🔴 [Nom créateur] est en direct : "[Titre live]"
|
||
Tap pour rejoindre
|
||
</code></pre>
|
||
<p><strong>Audio-guide à pied</strong> :</p>
|
||
<pre><code>📍 Audio-guide disponible : [Nom du lieu]
|
||
Choisissez parmi 3 guides pour [Musée du Louvre]
|
||
Tap pour explorer
|
||
</code></pre>
|
||
<p><strong>Filtrage géographique</strong> :
|
||
- Si contenu/live hors zone utilisateur → <strong>pas de notification</strong>
|
||
- Évite frustration : "notification pour contenu que je ne peux pas écouter"
|
||
- Exception : contenu national → notifie tous les abonnés</p>
|
||
<p><strong>Fréquence maximale</strong> :
|
||
- <strong>Maximum 10 notifications push/jour</strong> par utilisateur (tous types confondus)
|
||
- Si dépassement : notifications regroupées
|
||
- Message groupé : "🎧 3 nouveaux contenus de créateurs suivis"</p>
|
||
<p><strong>Plages horaires</strong> :
|
||
- <strong>Mode silencieux</strong> : 22h-8h (pas de push, sauf live)
|
||
- Paramétrable utilisateur (désactivation totale possible)
|
||
- Option "Notifications importantes uniquement" (lives uniquement)</p>
|
||
<p><strong>Gestion préférences</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Préférence</th>
|
||
<th>Défaut</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Nouveaux contenus</strong></td>
|
||
<td>✅ Activé</td>
|
||
<td>Push à chaque nouveau contenu (à pied uniquement)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Lives</strong></td>
|
||
<td>✅ Activé</td>
|
||
<td>Push au démarrage live (à pied uniquement)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Audio-guides proximité</strong></td>
|
||
<td>✅ Activé</td>
|
||
<td>Push quand audio-guide détecté à <100m</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Mode silencieux</strong></td>
|
||
<td>✅ Activé (22h-8h)</td>
|
||
<td>Pas de push nocturne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Limite quotidienne</strong></td>
|
||
<td>10</td>
|
||
<td>Modifiable 5-20</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Sécurité routière</strong> : pas de push en conduite (distraction)
|
||
- <strong>Engagement piéton</strong> : push actifs pour audio-guides (valeur ajoutée tourisme)
|
||
- <strong>Pas de spam</strong> : limite 10/jour + mode silencieux
|
||
- <strong>Filtrage géo</strong> : pertinence maximale (pas de notif inutiles)
|
||
- <strong>Coût</strong> : Firebase Cloud Messaging (gratuit jusqu'à volume élevé)</p>
|
||
<hr />
|
||
<h3 id="83-mode-audio-guide-pieton">8.3 Mode Audio-guide (piéton)</h3>
|
||
<p><strong>Décision</strong> : Navigation manuelle multiséquence + choix parmi plusieurs guides</p>
|
||
<p><strong>Fonctionnement</strong> :</p>
|
||
<h4 id="detection-et-proposition">Détection et proposition</h4>
|
||
<ol>
|
||
<li>Utilisateur à pied (<5 km/h) passe à <<strong>100m</strong> d'un lieu avec audio-guides</li>
|
||
<li><strong>Notification push</strong> : "📍 Audio-guide disponible : [Musée du Louvre]"</li>
|
||
<li>Tap notification → <strong>Page de sélection</strong> audio-guides</li>
|
||
</ol>
|
||
<h4 id="page-de-selection">Page de sélection</h4>
|
||
<p><strong>Affichage</strong> :</p>
|
||
<pre><code>📍 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 │
|
||
└─────────────────────────────────┘
|
||
</code></pre>
|
||
<h4 id="interface-audio-guide">Interface audio-guide</h4>
|
||
<p><strong>Après sélection</strong> :</p>
|
||
<pre><code>🎨 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
|
||
</code></pre>
|
||
<p><strong>Navigation</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Geste</th>
|
||
<th>Effet</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Séquence suivante</strong></td>
|
||
<td>Tap "Suivant" ou commande vocale "Suivant"</td>
|
||
<td>Passe à séquence N+1</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Séquence précédente</strong></td>
|
||
<td>Tap "Précédent" ou commande vocale "Précédent"</td>
|
||
<td>Revient à séquence N-1</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Saut direct</strong></td>
|
||
<td>Tap séquence dans liste</td>
|
||
<td>Lecture séquence choisie</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Pause</strong></td>
|
||
<td>Tap bouton pause</td>
|
||
<td>Met en pause, reprise position exacte</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Quitter</strong></td>
|
||
<td>Tap "×"</td>
|
||
<td>Sauvegarde progression, sortie guide</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Guidage vocal automatique</strong> :
|
||
- 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."</p>
|
||
<p><strong>Sauvegarde progression</strong> :
|
||
- 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</p>
|
||
<p><strong>Création audio-guide multiséquence</strong> :</p>
|
||
<p><strong>Processus créateur</strong> :
|
||
1. Créateur upload <strong>plusieurs fichiers audio</strong> (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 <strong>point GPS unique</strong> pour tout le guide (centre du lieu)
|
||
5. Métadonnées : durée totale calculée automatiquement</p>
|
||
<p><strong>Format stockage</strong> :</p>
|
||
<pre><code class="language-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"
|
||
}
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>UX piéton</strong> : navigation tactile adaptée (pas de commandes volant)
|
||
- <strong>Autonomie</strong> : utilisateur maître de son rythme (pas d'enchaînement forcé)
|
||
- <strong>Choix</strong> : plusieurs guides = diversité styles (famille, expert, rapide)
|
||
- <strong>Engagement</strong> : sauvegarde progression = incitation terminer
|
||
- <strong>Coût</strong> : réutilise infra contenu standard (juste métadonnées séquences)</p>
|
||
<hr />
|
||
<h3 id="84-limites-et-desabonnement">8.4 Limites et désabonnement</h3>
|
||
<p><strong>Décision</strong> : 200 abonnements max + désabonnement -5% jauges</p>
|
||
<p><strong>Nombre maximum d'abonnements</strong> :
|
||
- <strong>200 créateurs maximum</strong> par utilisateur
|
||
- Raisons :
|
||
- <strong>Évite spam</strong> : au-delà de 200, notifications ingérables
|
||
- <strong>Usage réaliste</strong> : 200 créateurs = déjà énorme (vs 100-150 sur YouTube/Twitter)
|
||
- <strong>Performance</strong> : requêtes SQL optimisées (index sur 200 max)</p>
|
||
<p><strong>Si limite atteinte</strong> :
|
||
- 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 ?"</p>
|
||
<p><strong>Abonnement initial</strong> :
|
||
- Impact : <strong>+5% toutes jauges tags du créateur</strong> (défini en <a href="#../adr/010-commandes-volant">ADR-010</a>)
|
||
- Action : Bouton "S'abonner" dans profil créateur (interface mobile)
|
||
- Immédiat à l'action</p>
|
||
<p><strong>Désabonnement</strong> :
|
||
- Impact : <strong>-5% toutes jauges tags du créateur</strong> (symétrique)
|
||
- Action : Bouton "Se désabonner" dans profil créateur
|
||
- Immédiat à l'action
|
||
- Pas de confirmation (action réversible)</p>
|
||
<p><strong>Exemple</strong> :</p>
|
||
<pre><code>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%)
|
||
</code></pre>
|
||
<p><strong>Gestion multi-tags</strong> :
|
||
- Si créateur a 3 tags → <strong>+5% sur chacun des 3 tags</strong>
|
||
- Logique : abonnement = signal fort d'affinité à TOUS les sujets du créateur</p>
|
||
<p><strong>Abonnements réciproques</strong> :
|
||
- ❌ <strong>Pas d'abonnement mutuel visible</strong>
|
||
- Créateur ne voit pas qui est abonné (privacy)
|
||
- Créateur voit uniquement : nombre total abonnés (métrique globale)</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Limite 200</strong> : équilibre entre liberté et gestion spam
|
||
- <strong>Symétrie +5%/-5%</strong> : cohérence mathématique, prévisibilité
|
||
- <strong>Privacy</strong> : pas de liste publique abonnés (évite stalking)
|
||
- <strong>Coût</strong> : table abonnements PostgreSQL standard</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-8">Récapitulatif Section 8</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="9-monetisation-createurs">9. Monétisation créateurs</h2>
|
||
<h3 id="91-pourboires">9.1 Pourboires</h3>
|
||
<p><strong>Décision</strong> : ❌ Fonctionnalité abandonnée pour le MVP</p>
|
||
<p><strong>Raisons</strong> :
|
||
- 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</p>
|
||
<p><strong>Post-MVP</strong> : Possible réintégration avec crypto (Bitcoin/Lightning Network) si législation UE l'autorise clairement (régulation MiCA en cours).</p>
|
||
<hr />
|
||
<h3 id="92-conditions-dactivation-de-la-monetisation">9.2 Conditions d'activation de la monétisation</h3>
|
||
<p><strong>Décision</strong> : 5 critères cumulatifs obligatoires</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Critère</th>
|
||
<th>Seuil</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Ancienneté</strong></td>
|
||
<td>Compte créé depuis ≥ 3 mois</td>
|
||
<td>Anti-fraude : temps de détecter comportements suspects</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Popularité</strong></td>
|
||
<td>≥ 500 abonnés</td>
|
||
<td>Garantit audience réelle et engagée</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Engagement</strong></td>
|
||
<td>≥ 10 000 écoutes complètes cumulées</td>
|
||
<td>Créateurs produisant du contenu de qualité</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Fiabilité</strong></td>
|
||
<td>Aucun strike actif, 0 contenu modéré dans les 6 derniers mois</td>
|
||
<td>Historique propre requis</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Régularité</strong></td>
|
||
<td>≥ 5 contenus publiés dans les 90 derniers jours</td>
|
||
<td>Activité constante</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Vérification</strong> : Automatique via requêtes SQL lors de la demande d'activation</p>
|
||
<p><strong>Affichage</strong> :
|
||
- 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</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Anti-fraude</strong> : Le délai de 3 mois permet de détecter les comptes suspects
|
||
- <strong>Qualité</strong> : Seuls les créateurs sérieux avec audience réelle sont monétisés
|
||
- <strong>Coût administratif</strong> : Réduit le nombre de comptes à gérer (KYC, comptabilité, virements)
|
||
- <strong>Légitimité</strong> : Audience organique prouvée</p>
|
||
<hr />
|
||
<h3 id="93-kyc-know-your-customer-et-inscription">9.3 KYC (Know Your Customer) et inscription</h3>
|
||
<p><strong>Décision</strong> : Statut juridique professionnel obligatoire</p>
|
||
<p><strong>Statuts acceptés</strong> :
|
||
- Auto-entrepreneur (micro-BNC pour artistes/créateurs de contenu)
|
||
- SARL/SAS/SASU (sociétés)</p>
|
||
<p><strong>Documents requis</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Document</th>
|
||
<th>Obligatoire</th>
|
||
<th>Format</th>
|
||
<th>Validité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>SIRET</strong></td>
|
||
<td>✅</td>
|
||
<td>14 chiffres</td>
|
||
<td>Permanent</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>RIB professionnel</strong></td>
|
||
<td>✅</td>
|
||
<td>IBAN FR</td>
|
||
<td>Permanent</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Pièce d'identité</strong></td>
|
||
<td>✅</td>
|
||
<td>CNI/Passeport</td>
|
||
<td>En cours de validité</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Numéro TVA intracommunautaire</strong></td>
|
||
<td>⚠️ Si applicable</td>
|
||
<td>FR + 11 chiffres</td>
|
||
<td>Permanent</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Kbis <3 mois</strong></td>
|
||
<td>⚠️ Si société</td>
|
||
<td>PDF</td>
|
||
<td><3 mois</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Vérification</strong> : Via Mangopay (KYC intégré + vérification bancaire)</p>
|
||
<p><strong>Délai</strong> : 24-72h si documents conformes</p>
|
||
<p><strong>Rejet possible si</strong> :
|
||
- Documents invalides/illisibles
|
||
- Identité ne correspond pas au compte RoadWave
|
||
- Liste noire anti-blanchiment (vérification automatique Mangopay)
|
||
- RIB non professionnel (particulier)</p>
|
||
<p><strong>Base légale</strong> :
|
||
- <strong>Conformité fiscale</strong> : L'État français impose déclaration revenus >1200€/an (DAS2)
|
||
- <strong>Anti-blanchiment</strong> : Directive EU 2018/843 (5ème directive LCB-FT)
|
||
- <strong>RGPD</strong> : Données hébergées EU via Mangopay (conforme)</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Responsabilité légale</strong> : RoadWave doit pouvoir prouver identité réelle créateurs monétisés
|
||
- <strong>Automatisation</strong> : Mangopay gère tout (KYC, vérifications, conformité, e-wallets)
|
||
- <strong>KYC gratuit</strong> : inclus dans l'offre Mangopay (vs 1.20€ chez Stripe)
|
||
- <strong>Souveraineté EU</strong> : Mangopay est européen (France/Luxembourg), régulé ACPR</p>
|
||
<hr />
|
||
<h3 id="94-sources-de-revenus-createurs">9.4 Sources de revenus créateurs</h3>
|
||
<h4 id="a-publicites-utilisateurs-gratuits">A) Publicités (utilisateurs gratuits)</h4>
|
||
<p><strong>Formule</strong> : <strong>3€ / 1000 écoutes complètes</strong> (CPM créateur)</p>
|
||
<p><strong>Répartition économique</strong> :</p>
|
||
<pre><code>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€
|
||
</code></pre>
|
||
<p><strong>Exemple concret</strong> :
|
||
- 10 000 écoutes/mois → créateur touche <strong>30€</strong>
|
||
- 50 000 écoutes/mois → créateur touche <strong>150€</strong>
|
||
- 100 000 écoutes/mois → créateur touche <strong>300€</strong></p>
|
||
<p><strong>Comparaison industrie</strong> :
|
||
- YouTube : 3-5€/1000 vues
|
||
- Spotify : 3-4€/1000 écoutes
|
||
- RoadWave : 3€/1000 écoutes (aligné)</p>
|
||
<p><strong>Règles comptabilisation</strong> :
|
||
- ✅ É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)</p>
|
||
<hr />
|
||
<h4 id="b-abonnes-premium">B) Abonnés Premium</h4>
|
||
<p><strong>Formule</strong> : <strong>70% au créateur, 30% à la plateforme</strong></p>
|
||
<p><strong>Répartition proportionnelle au temps d'écoute effectif</strong> :</p>
|
||
<pre><code>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€
|
||
</code></pre>
|
||
<p><strong>Calcul technique</strong> :</p>
|
||
<pre><code class="language-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;
|
||
</code></pre>
|
||
<p><strong>Comparaison industrie</strong> :
|
||
- YouTube Premium : 70/30
|
||
- Spotify : 70/30
|
||
- Apple Music : 52/48 (moins avantageux)
|
||
- RoadWave : 70/30 (standard)</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Standard industrie</strong> : ratio équitable éprouvé
|
||
- <strong>Incitation qualité</strong> : créateurs les plus écoutés gagnent plus
|
||
- <strong>Équité</strong> : pas de "winner takes all", chaque créateur écouté reçoit sa part
|
||
- <strong>Marge plateforme</strong> : 30% couvre absence revenus pub sur Premium</p>
|
||
<hr />
|
||
<h3 id="95-paiement-des-createurs">9.5 Paiement des créateurs</h3>
|
||
<p><strong>Seuil minimum</strong> : 50€</p>
|
||
<ul>
|
||
<li>En dessous → solde reporté mois suivant</li>
|
||
<li>Évite frais bancaires sur micro-sommes</li>
|
||
<li>Standard industrie (YouTube/Twitch/Spotify = 50-100€)</li>
|
||
</ul>
|
||
<p><strong>Fréquence</strong> : Mensuelle</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Date</th>
|
||
<th>Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Dernier jour du mois</strong> (ex: 31 janvier)</td>
|
||
<td>Calcul revenus du mois via SQL</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>1-14 du mois suivant</strong></td>
|
||
<td>Traitement contestations/fraudes éventuelles</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15 du mois suivant</strong> (ex: 15 février)</td>
|
||
<td>Virement SEPA via Mangopay (Payout)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16-18 du mois suivant</strong></td>
|
||
<td>Réception virement (1-3 jours ouvrés SEPA)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Virement via Mangopay</strong> :
|
||
- SEPA pour comptes EU (gratuit, 1-3 jours)
|
||
- Virement international hors EU (frais variables selon pays, rare en pratique)
|
||
- <strong>E-wallets automatiques</strong> : chaque créateur possède un wallet Mangopay où ses revenus sont transférés automatiquement</p>
|
||
<p><strong>Tableau de bord créateur</strong> (temps réel) :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Métrique</th>
|
||
<th>Description</th>
|
||
<th>Mise à jour</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Revenus pub</strong></td>
|
||
<td>Écoutes × CPM</td>
|
||
<td>Temps réel</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Revenus premium</strong></td>
|
||
<td>Abonnés actifs × ratio écoute</td>
|
||
<td>Temps réel</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Solde disponible</strong></td>
|
||
<td>Total revenus mois en cours</td>
|
||
<td>Temps réel</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Solde en attente</strong></td>
|
||
<td>Revenus mois précédent (paiement le 15)</td>
|
||
<td>Figé fin de mois</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Historique virements</strong></td>
|
||
<td>Liste des paiements reçus</td>
|
||
<td>Permanent</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Export comptable CSV</strong></td>
|
||
<td>Données pour expert-comptable</td>
|
||
<td>Téléchargement</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Gestion échecs virement</strong> :
|
||
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)</p>
|
||
<hr />
|
||
<h3 id="96-contenus-premium-exclusifs">9.6 Contenus Premium exclusifs</h3>
|
||
<p><strong>Décision</strong> : Créateur décide individuellement pour chaque contenu</p>
|
||
<p><strong>Fonctionnement</strong> :
|
||
- Toggle "Réservé Premium" lors création/édition contenu
|
||
- <strong>Aucune limite imposée</strong> : créateur peut mettre 0%, 50% ou 100% en premium
|
||
- Badge 👑 visible sur interface utilisateur</p>
|
||
<p><strong>Comportement utilisateurs gratuits</strong> :
|
||
- 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"</p>
|
||
<p><strong>Comportement algorithme</strong> :
|
||
- Contenus premium inclus dans recommandations
|
||
- Si user gratuit → contenu skippé automatiquement (ne consomme pas de slot)
|
||
- Si user premium → diffusé normalement</p>
|
||
<p><strong>Métadonnées</strong> :
|
||
- Champ <code>is_premium</code> (boolean) en base
|
||
- Index sur ce champ pour requêtes rapides
|
||
- Cache Redis : <code>content:{id}:premium</code> (TTL 1h)</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Liberté créateur</strong> : chaque créateur choisit sa stratégie (freemium, tout gratuit, tout premium)
|
||
- <strong>Incitation Premium</strong> : contenu exclusif = argument fort pour s'abonner
|
||
- <strong>Équité</strong> : un petit créateur peut tout mettre en premium, un gros peut tout offrir gratuitement</p>
|
||
<hr />
|
||
<h3 id="97-obligations-fiscales">9.7 Obligations fiscales</h3>
|
||
<p><strong>RoadWave génère automatiquement</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Document</th>
|
||
<th>Fréquence</th>
|
||
<th>Destinataire</th>
|
||
<th>Base légale</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Relevé mensuel PDF</strong></td>
|
||
<td>Chaque mois</td>
|
||
<td>Créateur</td>
|
||
<td>Transparence</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Export CSV comptable</strong></td>
|
||
<td>À la demande</td>
|
||
<td>Créateur + expert-comptable</td>
|
||
<td>Facilitation déclarations</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>DAS2 annuel</strong></td>
|
||
<td>Si >1200€/an</td>
|
||
<td>Impôts (DGFIP)</td>
|
||
<td>Obligation légale France</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Créateur responsable de</strong> :
|
||
- 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 <strong>10 ans</strong> (obligation légale comptable)</p>
|
||
<p><strong>Mangopay transmet automatiquement</strong> :
|
||
- Données aux autorités fiscales EU via <strong>DAC7</strong> (directive 2021/514)
|
||
- Justificatif de chaque virement (preuve bancaire pour comptabilité créateur)</p>
|
||
<p><strong>Exemple DAS2</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Conformité légale</strong> : RoadWave doit déclarer revenus versés (DAS2, DAC7)
|
||
- <strong>Responsabilité fiscale</strong> : Le créateur reste responsable de sa déclaration (impossible de gérer pour lui)
|
||
- <strong>Automatisation</strong> : Minimise charge administrative côtés créateur et plateforme</p>
|
||
<hr />
|
||
<h3 id="98-desactivation-et-suspension-monetisation">9.8 Désactivation et suspension monétisation</h3>
|
||
<p><strong>Créateur peut</strong> :
|
||
- Désactiver temporairement (vacances, pause création)
|
||
- Réactiver sans refaire KYC si données à jour (<2 ans)
|
||
- Solde conservé pendant désactivation</p>
|
||
<p><strong>Plateforme suspend automatiquement si</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Motif</th>
|
||
<th>Action</th>
|
||
<th>Réversible</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Strike 3+ actif</strong></td>
|
||
<td>Suspension immédiate</td>
|
||
<td>Oui, après résolution strikes</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Compte bancaire invalide</strong></td>
|
||
<td>Suspension après 3 échecs virement</td>
|
||
<td>Oui, après mise à jour RIB</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Documents KYC expirés</strong></td>
|
||
<td>Suspension avec préavis 30j</td>
|
||
<td>Oui, après renouvellement docs</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Fraude détectée</strong></td>
|
||
<td>Suspension immédiate + enquête</td>
|
||
<td>Cas par cas</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Suppression définitive si</strong> :
|
||
- Demande du créateur (solde versé sous 30 jours)
|
||
- Inactivité 24 mois + solde <50€ (purge RGPD)
|
||
- Ban définitif compte (Strike 4)</p>
|
||
<p><strong>Notification</strong> :
|
||
- Email + in-app pour toute suspension
|
||
- Raison explicite fournie
|
||
- Procédure de réactivation indiquée</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Flexibilité</strong> : créateur peut faire pause sans perdre statut
|
||
- <strong>Sécurité</strong> : plateforme doit pouvoir suspendre en cas problème légal/technique
|
||
- <strong>RGPD</strong> : suppression auto données inactives après délai raisonnable</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-9">Récapitulatif Section 9</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="10-premium_1">10. Premium</h2>
|
||
<h3 id="101-offre-et-tarification">10.1 Offre et tarification</h3>
|
||
<p><strong>Décision</strong> : Deux formules sans essai gratuit</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Formule</th>
|
||
<th>Prix</th>
|
||
<th>Économie</th>
|
||
<th>Prix effectif</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Mensuel</strong></td>
|
||
<td>4.99€/mois</td>
|
||
<td>-</td>
|
||
<td>4.99€/mois</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Annuel</strong></td>
|
||
<td>49.99€/an</td>
|
||
<td>2 mois offerts</td>
|
||
<td>4.16€/mois</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>❌ Pas d'essai gratuit</strong></p>
|
||
<p><strong>Raisons</strong> :
|
||
- <strong>Anti-abus vacances</strong> : évite inscriptions opportunistes (essai 14j avant road trip vacances, puis annulation)
|
||
- <strong>Protection revenus créateurs</strong> : les écoutes Premium rémunèrent créateurs dès jour 1
|
||
- <strong>Simplicité</strong> : pas de gestion période trial + conversion
|
||
- <strong>Engagement</strong> : utilisateur qui paie dès début = plus engagé</p>
|
||
<p><strong>❌ Pas de partage familial (MVP)</strong></p>
|
||
<p><strong>Raisons</strong> :
|
||
- 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)
|
||
- <strong>Post-MVP</strong> : Si forte demande, offre "Famille" à 9.99€/mois pour 5 comptes</p>
|
||
<p><strong>Justification tarif</strong> :
|
||
- <strong>Aligné marché bas</strong> : Spotify = 10.99€, YouTube Premium = 11.99€, Apple Music = 10.99€
|
||
- <strong>Prix accessible</strong> : cible conducteurs quotidiens (budget raisonnable)
|
||
- <strong>Incitation annuel</strong> : 2 mois offerts = engagement long terme + réduction churn</p>
|
||
<hr />
|
||
<h3 id="102-multi-devices-et-detection-simultanee">10.2 Multi-devices et détection simultanée</h3>
|
||
<p><strong>Décision</strong> : 1 seul stream actif par compte à tout moment</p>
|
||
<p><strong>Détection connexion simultanée</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Implémentation technique</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Exceptions</strong> :
|
||
- Contenus téléchargés (offline) ne comptent pas comme stream actif
|
||
- Transition rapide device (<10s) tolérée (changement voiture → maison)</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Anti-partage compte</strong> : empêche 2 personnes d'utiliser même compte Premium
|
||
- <strong>Protection revenus créateurs</strong> : 1 abonnement = 1 personne = 1 écoute
|
||
- <strong>UX claire</strong> : message explicite, pas de coupure brutale</p>
|
||
<hr />
|
||
<h3 id="103-contenus-exclusifs-premium">10.3 Contenus exclusifs Premium</h3>
|
||
<p><strong>Décision</strong> : Créateur décide (déjà couvert section 9.6)</p>
|
||
<p><strong>Rappel règles</strong> :
|
||
- Toggle "Réservé Premium" par contenu
|
||
- Aucune limite de ratio gratuit/premium
|
||
- Badge 👑 visible
|
||
- Users gratuits : lecture bloquée avec CTA "Passez Premium"</p>
|
||
<p><strong>Impact algorithme</strong> :
|
||
- Contenus premium inclus dans recommandations
|
||
- Si user gratuit → skip automatique (ne consomme pas slot)
|
||
- Si user premium → diffusé normalement selon score</p>
|
||
<hr />
|
||
<h3 id="104-avantages-premium">10.4 Avantages Premium</h3>
|
||
<p><strong>Inclus dans l'abonnement</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Avantage</th>
|
||
<th>Gratuit</th>
|
||
<th>Premium</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Publicités</strong></td>
|
||
<td>1/5 contenus</td>
|
||
<td>0 (aucune)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenus exclusifs</strong></td>
|
||
<td>❌ Bloqués</td>
|
||
<td>✅ Accès complet</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Qualité audio</strong></td>
|
||
<td>48 kbps Opus</td>
|
||
<td>64 kbps Opus</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Mode offline</strong></td>
|
||
<td>50 contenus max</td>
|
||
<td>Illimité</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Historique écoute</strong></td>
|
||
<td>100 derniers</td>
|
||
<td>Illimité</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Qualité audio</strong> :
|
||
- Gratuit : 48 kbps Opus (~20 MB/h) = très correct pour voix
|
||
- Premium : 64 kbps Opus (~30 MB/h) = excellente qualité</p>
|
||
<p><strong>Justification différences</strong> :
|
||
- <strong>0 pub</strong> = argument principal (confort écoute)
|
||
- <strong>Qualité audio</strong> = avantage tangible audiophiles
|
||
- <strong>Offline illimité</strong> = use case road trips longs
|
||
- <strong>Pas d'over-engineering</strong> : pas de badges cosmétiques, fonctionnalités sociales, etc. (focus essentiel)</p>
|
||
<hr />
|
||
<h3 id="105-gestion-abonnement">10.5 Gestion abonnement</h3>
|
||
<p><strong>Souscription</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Canal</th>
|
||
<th>Prestataire</th>
|
||
<th>Prix</th>
|
||
<th>Commission</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Web (desktop/mobile)</strong></td>
|
||
<td>Mangopay</td>
|
||
<td>4.99€</td>
|
||
<td>1.8% + 0.18€ = 0.27€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>iOS App</strong></td>
|
||
<td>Apple In-App Purchase</td>
|
||
<td>5.99€</td>
|
||
<td>30% (Apple)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Android App</strong></td>
|
||
<td>Google Play Billing</td>
|
||
<td>5.99€</td>
|
||
<td>30% (Google)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Majoration mobile (5.99€)</strong> :
|
||
- Apple/Google prennent 30% de commission
|
||
- RoadWave majore prix de 20% pour compenser
|
||
- <strong>Incitation web</strong> : Email aux users "Abonnez-vous sur roadwave.com pour 4.99€/mois" (38% moins cher en frais !)</p>
|
||
<p><strong>Renouvellement automatique</strong> :
|
||
- Email rappel <strong>7 jours avant</strong> renouvellement
|
||
- Email confirmation <strong>après</strong> renouvellement réussi
|
||
- Retry automatique si échec paiement (3 tentatives sur 7 jours)
|
||
- Annulation automatique après 3 échecs</p>
|
||
<p><strong>Annulation</strong> :
|
||
- Self-service dans Settings app : "Abonnement > Annuler"
|
||
- Accès Premium maintenu jusqu'à <strong>fin période payée</strong>
|
||
- Pas de remboursement prorata (standard industrie)
|
||
- Email confirmation annulation avec date fin d'accès</p>
|
||
<p><strong>Réabonnement</strong> :
|
||
- Possibilité immédiate
|
||
- ❌ Pas de nouvelle période d'essai (pas d'essai du tout)</p>
|
||
<p><strong>Architecture données</strong> :</p>
|
||
<pre><code class="language-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()
|
||
);
|
||
</code></pre>
|
||
<p><strong>Vérification Premium en temps réel</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-10">Récapitulatif Section 10</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="11-mode-offline_1">11. Mode offline</h2>
|
||
<h3 id="111-telechargement">11.1 Téléchargement</h3>
|
||
<p><strong>Zone géographique</strong> : Choix manuel utilisateur</p>
|
||
<p><strong>Options prédéfinies</strong> :
|
||
- "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.</p>
|
||
<p><strong>Nombre de contenus téléchargeables</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Statut</th>
|
||
<th>Limite</th>
|
||
<th>Affichage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Gratuit</strong></td>
|
||
<td>50 contenus max</td>
|
||
<td>"12/50 contenus téléchargés"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Premium</strong></td>
|
||
<td>Illimité</td>
|
||
<td>"245 contenus (3.2 GB)"</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Calcul temps disponible</strong> :
|
||
- 50 contenus × 5 min moyenne = 250 min = <strong>4h d'écoute</strong> (suffisant pour gratuits)
|
||
- Premium illimité = limité uniquement par espace disque device</p>
|
||
<p><strong>Connexion WiFi/Mobile</strong> :</p>
|
||
<p><strong>Par défaut</strong> : WiFi uniquement</p>
|
||
<p><strong>Sur données mobiles</strong> :
|
||
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 <strong>X MB</strong>. Continuer ?"
|
||
4. Boutons : "Attendre WiFi" / "Continuer"</p>
|
||
<p><strong>Calcul estimation</strong> :</p>
|
||
<pre><code>Nombre contenus × durée moyenne × bitrate qualité
|
||
Exemple : 20 contenus × 5 min × 48 kbps = ~72 MB
|
||
</code></pre>
|
||
<p><strong>Qualité audio téléchargement</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Qualité</th>
|
||
<th>Bitrate</th>
|
||
<th>Taille</th>
|
||
<th>Disponibilité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Basse</strong></td>
|
||
<td>24 kbps</td>
|
||
<td>~10 MB/h</td>
|
||
<td>Gratuit + Premium</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Standard</strong></td>
|
||
<td>48 kbps</td>
|
||
<td>~20 MB/h</td>
|
||
<td>Gratuit + Premium (défaut)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Haute</strong></td>
|
||
<td>64 kbps</td>
|
||
<td>~30 MB/h</td>
|
||
<td><strong>Premium uniquement</strong></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Justification</strong> :
|
||
- 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é</p>
|
||
<hr />
|
||
<h3 id="112-validite-et-renouvellement">11.2 Validité et renouvellement</h3>
|
||
<p><strong>Durée de validité</strong> : 30 jours après téléchargement</p>
|
||
<p><strong>Standard industrie</strong> :
|
||
- Spotify : 30 jours
|
||
- YouTube Music : 30 jours
|
||
- Deezer : 30 jours</p>
|
||
<p><strong>Renouvellement automatique</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Notification avant expiration</strong> :
|
||
- <strong>J-3</strong> : "X contenus expirent dans 3 jours. Connectez-vous en WiFi pour les renouveler"
|
||
- <strong>J-0</strong> : Suppression automatique
|
||
- <strong>J+0</strong> : Toast "15 contenus expirés ont été supprimés"</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Force reconnexion</strong> : vérifier abonnement actif, contenus légaux
|
||
- <strong>Évite stockage obsolète</strong> : contenus supprimés/modérés ne restent pas
|
||
- <strong>UX transparente</strong> : renouvellement silencieux si WiFi régulier</p>
|
||
<hr />
|
||
<h3 id="113-synchronisation-actions-offline">11.3 Synchronisation actions offline</h3>
|
||
<p><strong>Actions stockées localement (SQLite)</strong> :
|
||
- Likes/unlikes
|
||
- Abonnements/désabonnements
|
||
- Signalements
|
||
- Progression audio-guides</p>
|
||
<p><strong>Sync automatique à la reconnexion</strong> :</p>
|
||
<pre><code>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"
|
||
</code></pre>
|
||
<p><strong>Gestion erreurs sync</strong> :
|
||
- Si échec après 3 tentatives → notification : "Impossible de synchroniser. Réessayez plus tard"
|
||
- Actions conservées jusqu'à sync réussie (pas de perte)
|
||
- <strong>Rétention max 7 jours</strong> : après = purge (évite queue infinie)</p>
|
||
<p><strong>Conflits contenus supprimés</strong> :</p>
|
||
<pre><code>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)"
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Pas de conflit possible</strong> : actions unilatérales user (likes/abonnements)
|
||
- <strong>UX fluide</strong> : pas de blocage offline
|
||
- <strong>Batch = économie</strong> : requêtes HTTP groupées
|
||
- <strong>Conformité modération</strong> : contenu illégal disparaît même offline</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-11">Récapitulatif Section 11</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Aspect</th>
|
||
<th>Décision</th>
|
||
<th>Valeur</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Zone téléchargement</strong></td>
|
||
<td>Choix</td>
|
||
<td>Manuel (autour/ville/département/région/recherche)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Limite gratuit</strong></td>
|
||
<td>Contenus</td>
|
||
<td>50 max</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Limite Premium</strong></td>
|
||
<td>Contenus</td>
|
||
<td>Illimité (espace disque)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Connexion</strong></td>
|
||
<td>Par défaut</td>
|
||
<td>WiFi (mobile avec confirmation)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Qualité Standard</strong></td>
|
||
<td>Bitrate</td>
|
||
<td>48 kbps Opus</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Qualité Haute</strong></td>
|
||
<td>Bitrate</td>
|
||
<td>64 kbps (Premium uniquement)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Validité</strong></td>
|
||
<td>Durée</td>
|
||
<td>30 jours</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Renouvellement</strong></td>
|
||
<td>Mode</td>
|
||
<td>Automatique si WiFi</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Notification expiration</strong></td>
|
||
<td>Délai</td>
|
||
<td>J-3</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Sync actions</strong></td>
|
||
<td>Mode</td>
|
||
<td>Batch automatique reconnexion</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Rétention queue</strong></td>
|
||
<td>Durée</td>
|
||
<td>7 jours max</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="12-gestion-des-erreurs_1">12. Gestion des erreurs</h2>
|
||
<h3 id="121-aucun-contenu-disponible">12.1 Aucun contenu disponible</h3>
|
||
<p><strong>Stratégie</strong> : Élargissement automatique progressif</p>
|
||
<p><strong>Flow</strong> :</p>
|
||
<pre><code>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)
|
||
</code></pre>
|
||
<p><strong>Messages adaptatifs</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Cas</th>
|
||
<th>Message</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Trouvé à 100 km</strong></td>
|
||
<td>"Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Trouvé département</strong></td>
|
||
<td>"Aucun contenu local disponible. Voici du contenu dans votre département"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenu national</strong></td>
|
||
<td>"Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser"</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>UX fluide</strong> : pas de message d'erreur bloquant "Aucun contenu"
|
||
- <strong>User ne reste jamais sans contenu</strong>
|
||
- <strong>Contenu national = filet de sécurité</strong> : actualités Le Monde, podcasts génériques</p>
|
||
<hr />
|
||
<h3 id="122-contenu-signalesupprime-pendant-lecoute">12.2 Contenu signalé/supprimé pendant l'écoute</h3>
|
||
<p><strong>Décision</strong> : Pas d'interruption brutale</p>
|
||
<p><strong>Flow</strong> :</p>
|
||
<pre><code>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)"
|
||
</code></pre>
|
||
<p><strong>Si tentative "Précédent" manuellement</strong> :
|
||
- Message : "Ce contenu n'est plus disponible"
|
||
- Retour au contenu actuel</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Sécurité routière</strong> : pas d'interruption brutale pendant conduite
|
||
- <strong>User informé mais pas alarmé</strong> : message discret
|
||
- <strong>Empêche réécoute</strong> : contenu modéré inaccessible</p>
|
||
<hr />
|
||
<h3 id="123-perte-de-reseau">12.3 Perte de réseau</h3>
|
||
<p><strong>Buffer adaptatif</strong> (cf. TECHNICAL.md) :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Réseau</th>
|
||
<th>Buffer min</th>
|
||
<th>Buffer cible</th>
|
||
<th>Buffer max</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>WiFi</strong></td>
|
||
<td>5s</td>
|
||
<td>30s</td>
|
||
<td>120s</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>4G/5G</strong></td>
|
||
<td>10s</td>
|
||
<td>45s</td>
|
||
<td>120s</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>3G</strong></td>
|
||
<td>30s</td>
|
||
<td>90s</td>
|
||
<td>300s</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Comportement détaillé</strong> :</p>
|
||
<p><strong>Phase 1 : Connexion instable</strong> (latence élevée, paquets perdus)
|
||
- Aucun message immédiat
|
||
- Lecture continue sur buffer
|
||
- Si > 10s latence : toast discret "Connexion instable"</p>
|
||
<p><strong>Phase 2 : Perte totale réseau</strong>
|
||
- Lecture continue jusqu'à épuisement buffer
|
||
- Toast : "Hors ligne, lecture sur buffer (30s restantes)"
|
||
- Compte à rebours visible</p>
|
||
<p><strong>Phase 3 : Buffer épuisé sans reconnexion</strong>
|
||
- Pause automatique
|
||
- Overlay : "Connexion perdue. Reconnexion en cours..."
|
||
- Retry automatique toutes les 5s (max 6 tentatives = 30s)</p>
|
||
<p><strong>Phase 4 : Basculement mode offline</strong> (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</p>
|
||
<p><strong>Reconnexion réussie</strong> :
|
||
- Reprise automatique lecture au point d'arrêt exact
|
||
- Toast : "Connexion rétablie"</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Expérience fluide zones blanches</strong> (tunnels, campagne)
|
||
- <strong>Buffer généreux</strong> : absorbe fluctuations réseau mobile
|
||
- <strong>Mode offline secours</strong> : si coupure prolongée</p>
|
||
<hr />
|
||
<h3 id="124-geolocalisation-desactivee">12.4 Géolocalisation désactivée</h3>
|
||
<p><strong>Mode dégradé automatique</strong></p>
|
||
<p><strong>Contenu disponible</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type contenu</th>
|
||
<th>Disponible</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Contenu national</strong> (podcasts, actualités)</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenu téléchargé</strong> (offline)</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenus "Neutre"</strong> géographiquement</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenu géolocalisé</strong> (Ancré/Contextuel)</td>
|
||
<td>❌</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Audio-guides</strong></td>
|
||
<td>❌</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Notifications push géo-déclenchées</strong></td>
|
||
<td>❌</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Popup au lancement</strong> :
|
||
- <strong>Apparition</strong> : Premier lancement après refus géolocalisation
|
||
- <strong>Message</strong> : "RoadWave fonctionne mieux avec la géolocalisation activée. Sans elle, seul le contenu national sera disponible."
|
||
- <strong>Boutons</strong> :
|
||
- "Activer" → Redirection paramètres OS
|
||
- "Continuer sans" → Mode dégradé
|
||
- <strong>Checkbox</strong> : "Ne plus me demander"</p>
|
||
<p><strong>Banner permanent si refus</strong> :
|
||
- Bandeau haut écran : "Mode limité : géolocalisation désactivée. [Activer]"
|
||
- Pas intrusif mais rappel constant
|
||
- Disparaît si géolocalisation réactivée</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>App reste fonctionnelle</strong> sans GPS (pas de blocage)
|
||
- <strong>Incitation forte</strong> à activer (meilleure UX)
|
||
- <strong>Respecte choix user</strong> (RGPD : consentement libre)</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-12">Récapitulatif Section 12</h2>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="13-conformite-rgpd_1">13. Conformité RGPD</h2>
|
||
<h3 id="131-gestion-du-consentement">13.1 Gestion du consentement</h3>
|
||
<p><strong>Décision</strong> : Tarteaucitron.js + PostgreSQL backend</p>
|
||
<p><strong>Implémentation web</strong> :
|
||
- ✅ Tarteaucitron.js (opensource, self-hosted)
|
||
- ✅ Banner RGPD français, customisable
|
||
- ✅ Granularité : fonctionnel / analytique / marketing</p>
|
||
<p><strong>Implémentation backend</strong> :
|
||
- Table <code>user_consents</code> avec versioning
|
||
- Champs : user_id, consent_type, version, accepted, timestamp
|
||
- Historique complet conservé (preuve légale)</p>
|
||
<p><strong>Consentements requis</strong> :
|
||
- <strong>Géolocalisation précise</strong> : obligatoire (banner + permission OS)
|
||
- <strong>Analytics</strong> : optionnel (Matomo)
|
||
- <strong>Notifications push</strong> : optionnel (permission OS)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Opensource, 0€, conformité RGPD garantie
|
||
- Historique backend = preuve légale en cas de contrôle
|
||
- Granularité conforme recommandations CNIL</p>
|
||
<hr />
|
||
<h3 id="132-anonymisation-des-donnees-gps">13.2 Anonymisation des données GPS</h3>
|
||
<p><strong>Décision</strong> : Geohash après 24h</p>
|
||
<p><strong>Processus</strong> :
|
||
1. Données précises conservées <strong>24h</strong> (recommandation personnalisée)
|
||
2. Après 24h : conversion en geohash précision 5 (~5km²)
|
||
3. Coordonnées originales supprimées définitivement</p>
|
||
<p><strong>Implémentation PostGIS</strong> :</p>
|
||
<pre><code class="language-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;
|
||
</code></pre>
|
||
<p><strong>Exceptions</strong> :
|
||
- ✅ Historique personnel visible (liste trajets) : conservation intégrale tant que compte actif
|
||
- ❌ Analytics globales : uniquement geohash anonyme</p>
|
||
<p><strong>Justification</strong> :
|
||
- Vraie anonymisation RGPD (CNIL compliant)
|
||
- Permet analytics agrégées (heatmaps trafic)
|
||
- PostGIS natif, 0€</p>
|
||
<hr />
|
||
<h3 id="133-export-des-donnees-portabilite">13.3 Export des données (portabilité)</h3>
|
||
<p><strong>Décision</strong> : JSON + HTML + ZIP, génération asynchrone</p>
|
||
<p><strong>Contenu de l'export</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Données exportées</strong> :
|
||
- 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</p>
|
||
<p><strong>Processus</strong> :
|
||
1. Demande via paramètres compte
|
||
2. Génération asynchrone (worker background)
|
||
3. Email avec lien download (expire <strong>7 jours</strong>)
|
||
4. Délai : <strong>48h maximum</strong> (conformité RGPD)</p>
|
||
<p><strong>Limite</strong> :
|
||
- Maximum <strong>1 export/mois</strong> (anti-abus)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Conformité article 20 RGPD (portabilité)
|
||
- Double format (human + machine)
|
||
- Worker asynchrone évite timeout</p>
|
||
<hr />
|
||
<h3 id="134-suppression-du-compte">13.4 Suppression du compte</h3>
|
||
<p><strong>Décision</strong> : Grace period 30j + anonymisation contenus</p>
|
||
<p><strong>Processus</strong> :
|
||
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</p>
|
||
<p><strong>Suppression effective</strong> :
|
||
- ✅ Compte utilisateur supprimé (données personnelles)
|
||
- ✅ Historique d'écoute supprimé
|
||
- ✅ GPS historique supprimé
|
||
- ✅ Sessions et tokens révoqués
|
||
- ⚠️ Contenus créés <strong>anonymisés</strong> (créateur = "Utilisateur supprimé")
|
||
- ⚠️ Likes et abonnements supprimés (mais compteurs préservés)</p>
|
||
<p><strong>Contenus conservés anonymement</strong> :
|
||
- Audio files (CDN)
|
||
- Métadonnées (titre, description, tags, géolocalisation)
|
||
- Statistiques d'écoute</p>
|
||
<p><strong>Justification</strong> :
|
||
- Grace period évite suppressions impulsives
|
||
- Anonymisation contenus = intérêt légitime communauté
|
||
- Conforme RGPD si créateur = donnée supprimée</p>
|
||
<hr />
|
||
<h3 id="135-mode-degrade-sans-gps-precis">13.5 Mode dégradé (sans GPS précis)</h3>
|
||
<p><strong>Décision</strong> : GeoIP par défaut, GPS optionnel</p>
|
||
<p><strong>Niveaux de précision</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Niveau</th>
|
||
<th>Technologie</th>
|
||
<th>Contenus accessibles</th>
|
||
<th>Consentement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Pays</strong></td>
|
||
<td>Aucune géoloc</td>
|
||
<td>Contenus nationaux uniquement</td>
|
||
<td>❌ Non requis</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Ville</strong></td>
|
||
<td>GeoIP (MaxMind)</td>
|
||
<td>Contenus régionaux/ville</td>
|
||
<td>❌ Non requis</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Précis</strong></td>
|
||
<td>GPS</td>
|
||
<td>Tous contenus (hyperlocaux inclus)</td>
|
||
<td>✅ Requis</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Implémentation</strong> :
|
||
- 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</p>
|
||
<p><strong>API GeoIP</strong> :
|
||
- MaxMind GeoLite2 (gratuit, self-hosted)
|
||
- Update DB mensuelle automatique
|
||
- Précision ~80% au niveau ville</p>
|
||
<p><strong>Justification</strong> :
|
||
- RGPD : pas de consentement requis pour GeoIP (pas de donnée personnelle)
|
||
- UX dégradée acceptable (contenus disponibles)
|
||
- Progressive disclosure (upgrade optionnel)</p>
|
||
<hr />
|
||
<h3 id="136-duree-de-conservation-des-donnees">13.6 Durée de conservation des données</h3>
|
||
<p><strong>Décision</strong> : 5 ans inactivité → purge automatique</p>
|
||
<p><strong>Règles</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type de compte</th>
|
||
<th>Seuil inactivité</th>
|
||
<th>Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Auditeur uniquement</strong></td>
|
||
<td>5 ans sans connexion</td>
|
||
<td>Suppression automatique</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Créateur avec contenus actifs</strong></td>
|
||
<td>Jamais (tant qu'écoutes)</td>
|
||
<td>Conservation indéfinie</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Créateur inactif</strong></td>
|
||
<td>5 ans sans connexion + 2 ans sans écoute</td>
|
||
<td>Suppression automatique</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Notifications avant suppression</strong> :
|
||
- Email + push : <strong>90 jours</strong> avant
|
||
- Email + push : <strong>30 jours</strong> avant
|
||
- Email + push : <strong>7 jours</strong> avant
|
||
- Toute connexion = reset compteur inactivité</p>
|
||
<p><strong>Contenu conservé</strong> :
|
||
- Contenus créés par comptes supprimés (anonymisés) : conservation indéfinie</p>
|
||
<p><strong>Justification</strong> :
|
||
- Conformité principe minimisation RGPD
|
||
- 5 ans = équilibre raisonnable (standard industrie)
|
||
- Exception créateurs actifs = intérêt légitime plateforme</p>
|
||
<hr />
|
||
<h3 id="137-cookies-et-trackers-web">13.7 Cookies et trackers web</h3>
|
||
<p><strong>Décision</strong> : Matomo self-hosted, zéro cookie tiers</p>
|
||
<p><strong>Cookies utilisés</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Cookie</th>
|
||
<th>Type</th>
|
||
<th>Durée</th>
|
||
<th>Finalité</th>
|
||
<th>Consentement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>session</code></td>
|
||
<td>Technique</td>
|
||
<td>30j</td>
|
||
<td>Authentification</td>
|
||
<td>❌ Non requis</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>refresh_token</code></td>
|
||
<td>Technique</td>
|
||
<td>30j</td>
|
||
<td>Session persistante</td>
|
||
<td>❌ Non requis</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>_pk_id</code></td>
|
||
<td>Analytique</td>
|
||
<td>13 mois</td>
|
||
<td>Matomo (IP anonyme)</td>
|
||
<td>✅ Requis</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Analytics : Matomo self-hosted</strong> :
|
||
- 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)</p>
|
||
<p><strong>Trackers interdits</strong> :
|
||
- ❌ Google Analytics
|
||
- ❌ Facebook Pixel
|
||
- ❌ Hotjar, Mixpanel, etc.</p>
|
||
<p><strong>Justification</strong> :
|
||
- Souveraineté données (pas de transfert US)
|
||
- Conformité RGPD max (CNIL compatible)
|
||
- Matomo = opensource, 0€ infra</p>
|
||
<hr />
|
||
<h3 id="138-registre-des-traitements">13.8 Registre des traitements</h3>
|
||
<p><strong>Décision</strong> : Document Markdown versionné Git (MVP)</p>
|
||
<p><strong>Emplacement</strong> :
|
||
- <code>docs/rgpd/registre-traitements.md</code>
|
||
- Versionné Git (historique modifications)</p>
|
||
<p><strong>Contenu obligatoire par traitement</strong> :
|
||
- 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)</p>
|
||
<p><strong>Responsable</strong> :
|
||
- DPO / Fondateur
|
||
- Review trimestrielle obligatoire
|
||
- Update immédiate si nouveau traitement</p>
|
||
<p><strong>Migration future</strong> :
|
||
- Si > 100K utilisateurs : interface admin PostgreSQL</p>
|
||
<p><strong>Justification</strong> :
|
||
- Obligation RGPD Article 30
|
||
- Markdown = simple, versionné, auditable
|
||
- 0€</p>
|
||
<hr />
|
||
<h3 id="139-notification-violations-de-donnees-breach">13.9 Notification violations de données (breach)</h3>
|
||
<p><strong>Décision</strong> : Monitoring + alertes + runbook</p>
|
||
<p><strong>Détection automatique</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Événement</th>
|
||
<th>Outil</th>
|
||
<th>Alerte</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Erreurs backend critiques</td>
|
||
<td>Sentry</td>
|
||
<td>Discord/Slack immédiat</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Pic requêtes anormal</td>
|
||
<td>Grafana</td>
|
||
<td>Email équipe</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Accès non autorisé DB</td>
|
||
<td>PostgreSQL logs</td>
|
||
<td>SMS fondateur</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Authentification suspecte</td>
|
||
<td>Zitadel alerts</td>
|
||
<td>Email équipe</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Procédure breach</strong> :
|
||
- Runbook : <code>docs/rgpd/procedure-breach.md</code>
|
||
- 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é</p>
|
||
<p><strong>Contact CNIL</strong> :
|
||
- Email pré-rédigé (template)
|
||
- Formulaire en ligne (account CNIL créé)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Obligation RGPD Article 33 (notification 72h)
|
||
- Monitoring proactif évite découverte tardive
|
||
- Sentry gratuit < 5K events/mois</p>
|
||
<hr />
|
||
<h3 id="1310-dpo-delegue-a-la-protection-des-donnees">13.10 DPO (Délégué à la Protection des Données)</h3>
|
||
<p><strong>Décision</strong> : Fondateur = DPO temporaire (MVP)</p>
|
||
<p><strong>Raison légale</strong> :
|
||
- Non obligatoire si :
|
||
- < 250 employés
|
||
- Pas de traitement à grande échelle de données sensibles
|
||
- RoadWave : données localisation = sensible MAIS échelle MVP</p>
|
||
<p><strong>Formation</strong> :
|
||
- CNIL : formation gratuite en ligne (4h)
|
||
- Certification CNIL "Atelier RGPD" (gratuit)</p>
|
||
<p><strong>Contact</strong> :
|
||
- Email : dpo@roadwave.fr
|
||
- Publié dans CGU et mentions légales
|
||
- Délai réponse : <strong>1 mois</strong> (RGPD)</p>
|
||
<p><strong>Migration future</strong> :
|
||
- Si > 100K utilisateurs : DPO externe mutualisé (~200€/mois)
|
||
- Ou recrutement DPO interne si > 10 employés</p>
|
||
<p><strong>Justification</strong> :
|
||
- Conforme RGPD (non obligatoire en phase MVP)
|
||
- 0€, contrôle total
|
||
- Bonne pratique : avoir un contact identifié</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-13">Récapitulatif Section 13</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Mesure</th>
|
||
<th>Implémentation</th>
|
||
<th>Coût</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Consentement</strong></td>
|
||
<td>Tarteaucitron.js + PostgreSQL</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Anonymisation GPS</strong></td>
|
||
<td>Geohash PostGIS (24h)</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Export données</strong></td>
|
||
<td>JSON+HTML+ZIP asynchrone</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Suppression compte</strong></td>
|
||
<td>Grace period 30j + anonymisation</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Mode dégradé</strong></td>
|
||
<td>GeoIP MaxMind + GPS optionnel</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Conservation</strong></td>
|
||
<td>Purge auto 5 ans inactivité</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Analytics</strong></td>
|
||
<td>Matomo self-hosted</td>
|
||
<td>~5€/mois</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Registre traitements</strong></td>
|
||
<td>Markdown Git</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Breach detection</strong></td>
|
||
<td>Sentry + Grafana + runbook</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>DPO</strong></td>
|
||
<td>Fondateur formé CNIL</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Coût total RGPD : ~5€/mois</strong></p>
|
||
<hr />
|
||
<h2 id="points-dattention-pour-gherkin">Points d'attention pour Gherkin</h2>
|
||
<ul>
|
||
<li>Tester consentement géolocalisation (accept/refuse → contenus différents)</li>
|
||
<li>Tester anonymisation GPS après 24h (job cron)</li>
|
||
<li>Tester export données (génération complète + vérification contenu)</li>
|
||
<li>Tester grace period suppression (annulation possible)</li>
|
||
<li>Tester mode GeoIP (ville détectée correctement)</li>
|
||
<li>Tester purge automatique (5 ans inactivité)</li>
|
||
<li>Tester notifications avant purge (90j/30j/7j)</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="14-moderation-flows-operationnels_1">14. Modération - Flows opérationnels</h2>
|
||
<h3 id="141-signalement">14.1 Signalement</h3>
|
||
<p><strong>Décision</strong> : Formulaire simple avec 7 catégories prédéfinies</p>
|
||
<h4 id="1411-categories-de-signalement">14.1.1 Catégories de signalement</h4>
|
||
<p>Liste déroulante avec 7 options :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Catégorie</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>🚫 <strong>Haine & violence</strong></td>
|
||
<td>Incitation à la haine, discrimination, menaces</td>
|
||
</tr>
|
||
<tr>
|
||
<td>🔞 <strong>Contenu sexuel</strong></td>
|
||
<td>Pornographie, contenu explicite</td>
|
||
</tr>
|
||
<tr>
|
||
<td>⚖️ <strong>Illégalité</strong></td>
|
||
<td>Terrorisme, apologie de crimes</td>
|
||
</tr>
|
||
<tr>
|
||
<td>🎵 <strong>Droits d'auteur</strong></td>
|
||
<td>Musique/contenu protégé non autorisé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>📧 <strong>Spam</strong></td>
|
||
<td>Publicité non sollicitée, répétition</td>
|
||
</tr>
|
||
<tr>
|
||
<td>❌ <strong>Fausse information</strong></td>
|
||
<td>Désinformation sur santé, sécurité routière</td>
|
||
</tr>
|
||
<tr>
|
||
<td>🔧 <strong>Autre</strong></td>
|
||
<td>Champ texte obligatoire si sélectionné</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Justification</strong> :
|
||
- Équilibre entre simplicité (pas trop de choix) et précision (aide les modérateurs)
|
||
- Coût : 0€ (liste déroulante standard)</p>
|
||
<hr />
|
||
<h4 id="1412-commentaire-du-signaleur">14.1.2 Commentaire du signaleur</h4>
|
||
<p><strong>Décision</strong> : Optionnel avec incitation</p>
|
||
<ul>
|
||
<li>Champ texte libre (0-500 caractères)</li>
|
||
<li>Placeholder : "Décrivez le problème (optionnel mais recommandé)"</li>
|
||
<li>Non bloquant : le signalement peut être envoyé sans commentaire</li>
|
||
</ul>
|
||
<p><strong>Justification</strong> :
|
||
- Encourage la qualité des signalements sans créer de friction
|
||
- Aide les modérateurs à comprendre le contexte
|
||
- Pas de risque d'abandon du processus</p>
|
||
<hr />
|
||
<h4 id="1413-confirmation-apres-signalement">14.1.3 Confirmation après signalement</h4>
|
||
<p><strong>Décision</strong> : Toast in-app avec lien historique</p>
|
||
<p><strong>Affichage</strong> :
|
||
- Toast notification : "✓ Signalement envoyé. Nous l'examinerons sous 24-48h."
|
||
- Durée affichage : 5 secondes
|
||
- Bouton optionnel "Voir mes signalements" (accès historique)</p>
|
||
<p><strong>Historique personnel</strong> :
|
||
- Liste des signalements envoyés par l'utilisateur
|
||
- Statut : En cours / Traité / Rejeté
|
||
- Notification in-app si action prise (contenu retiré, signalement rejeté)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Transparence maximale
|
||
- Coût : 0€ (aucun email automatique)
|
||
- Bonne UX</p>
|
||
<hr />
|
||
<h3 id="142-traitement-des-signalements">14.2 Traitement des signalements</h3>
|
||
<h4 id="1421-ia-pre-filtre-transcription-analyse">14.2.1 IA pré-filtre (transcription + analyse)</h4>
|
||
<p><strong>Décision</strong> : OpenAI Whisper open source + NLP</p>
|
||
<p><strong>Stack technique</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Composant</th>
|
||
<th>Technologie</th>
|
||
<th>Hébergement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Transcription</strong></td>
|
||
<td>Whisper large-v3</td>
|
||
<td>Self-hosted (CPU MVP, GPU scale)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Analyse sentiment</strong></td>
|
||
<td>distilbert-base-uncased</td>
|
||
<td>Self-hosted</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Détection haine</strong></td>
|
||
<td>facebook/roberta-hate-speech</td>
|
||
<td>Self-hosted</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Mots-clés</strong></td>
|
||
<td>Liste noire FR/EN + regex</td>
|
||
<td>PostgreSQL</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Processus</strong> :
|
||
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</p>
|
||
<p><strong>Délais</strong> :
|
||
- Audio <5 min : 1-3 minutes
|
||
- Audio 5-30 min : 3-10 minutes
|
||
- Audio >30 min : 10-20 minutes</p>
|
||
<p><strong>Coût</strong> :
|
||
- <strong>MVP</strong> : 0€ (CPU standard, processing asynchrone)
|
||
- <strong>Scale</strong> : 50-200€/mois (GPU VPS si >1000 signalements/jour)</p>
|
||
<p><strong>Justification</strong> :
|
||
- 100% open source, pas de dépendance GAFAM
|
||
- Coût maîtrisé (scaling progressif)
|
||
- Gain productivité modérateurs ×3-5</p>
|
||
<hr />
|
||
<h4 id="1422-delais-de-traitement-sla">14.2.2 Délais de traitement (SLA)</h4>
|
||
<p><strong>Décision</strong> : SLA progressif selon priorité</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Priorité</th>
|
||
<th>Délai cible</th>
|
||
<th>Traitement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>CRITIQUE</strong></td>
|
||
<td><2h (24/7)</td>
|
||
<td>Violence, suicide, mise en danger → Astreinte modérateur senior</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>HAUTE</strong></td>
|
||
<td><24h (jours ouvrés)</td>
|
||
<td>Haine, harcèlement, désinformation → Modérateur junior/senior</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>MOYENNE</strong></td>
|
||
<td><24h (jours ouvrés)</td>
|
||
<td>Spam, contenu inapproprié → Modérateur junior</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>BASSE</strong></td>
|
||
<td><72h (jours ouvrés)</td>
|
||
<td>Qualité audio, tags incorrects → Modérateur junior</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Traitement automatique</strong> :
|
||
- Score IA >95% + catégorie évidente (ex: spam répété) → Action automatique immédiate
|
||
- Notification créateur + possibilité d'appel</p>
|
||
<p><strong>Justification</strong> :
|
||
- Réaliste et conforme DSA (Digital Services Act)
|
||
- Scalable : priorisation automatique
|
||
- Ressources humaines optimisées</p>
|
||
<hr />
|
||
<h4 id="1423-priorisation-automatique">14.2.3 Priorisation automatique</h4>
|
||
<p><strong>Décision</strong> : File d'attente intelligente basée sur score IA</p>
|
||
<p><strong>Calcul de priorité</strong> :</p>
|
||
<pre><code>Priorité = (Score_IA × 0.7) + (Signalements_cumulés × 0.2) + (Fiabilité_signaleur × 0.1)
|
||
</code></pre>
|
||
<p><strong>Détails</strong> :
|
||
- <strong>Score_IA</strong> : 0-100% (confiance analyse automatique)
|
||
- <strong>Signalements_cumulés</strong> : nombre de signalements du même contenu (boost priorité)
|
||
- <strong>Fiabilité_signaleur</strong> : score utilisateur (historique signalements pertinents)</p>
|
||
<p><strong>Classification résultante</strong> :
|
||
- Priorité ≥90 → <strong>CRITIQUE</strong> (traitement immédiat)
|
||
- Priorité 70-89 → <strong>HAUTE</strong> (file prioritaire)
|
||
- Priorité 40-69 → <strong>MOYENNE</strong> (file normale)
|
||
- Priorité <40 → <strong>BASSE</strong> (file différée)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Optimise le temps des modérateurs
|
||
- Traite les cas graves en priorité
|
||
- Coût : 0€ (algorithme simple)</p>
|
||
<hr />
|
||
<h3 id="143-sanctions">14.3 Sanctions</h3>
|
||
<h4 id="1431-notification-au-createur">14.3.1 Notification au créateur</h4>
|
||
<p><strong>Décision</strong> : Multi-canal (email + push + in-app)</p>
|
||
<p><strong>Canaux utilisés</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Canal</th>
|
||
<th>Timing</th>
|
||
<th>Contenu</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Push notification</strong></td>
|
||
<td>Immédiat</td>
|
||
<td>Alerte courte : "Votre contenu a été modéré"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>In-app</strong></td>
|
||
<td>Au prochain lancement</td>
|
||
<td>Popup détaillée avec bouton "Voir détails"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Email</strong></td>
|
||
<td>Dans l'heure</td>
|
||
<td>Notification complète avec lien vers formulaire d'appel</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Contenu email</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Coût</strong> :
|
||
- Email : ~0.001€/notification (Brevo, Resend)
|
||
- Push : 0€ (Firebase Cloud Messaging / APNs)
|
||
- In-app : 0€</p>
|
||
<p><strong>Justification</strong> :
|
||
- Conformité DSA (transparence obligatoire)
|
||
- Multi-canal garantit réception
|
||
- Coût négligeable</p>
|
||
<hr />
|
||
<h4 id="1432-detail-de-la-sanction">14.3.2 Détail de la sanction</h4>
|
||
<p><strong>Décision</strong> : Notification complète avec preuves</p>
|
||
<p><strong>Éléments inclus obligatoirement</strong> :</p>
|
||
<ol>
|
||
<li><strong>Catégorie violée</strong> : référence précise CGU (ex: "Article 3.2 - Haine & violence")</li>
|
||
<li><strong>Raison détaillée</strong> : explication en langage clair (non juridique)</li>
|
||
<li><strong>Extrait audio</strong> : timestamp exact du passage problématique (ex: "3:42-4:15")</li>
|
||
<li><strong>Transcription</strong> : texte problématique surligné en rouge</li>
|
||
<li><strong>Gravité</strong> : Strike actuel + conséquences (ex: "Strike 2/4 - Suspension 7 jours")</li>
|
||
<li><strong>Recours</strong> : lien direct vers formulaire d'appel + délai (7 jours)</li>
|
||
</ol>
|
||
<p><strong>Exemple visuel in-app</strong> :</p>
|
||
<pre><code>┌─────────────────────────────────────┐
|
||
│ ⚠️ 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] │
|
||
└─────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- Transparence maximale (obligation DSA)
|
||
- Créateur comprend l'erreur → amélioration future
|
||
- Réduit les appels non fondés</p>
|
||
<hr />
|
||
<h4 id="1433-processus-dappel">14.3.3 Processus d'appel</h4>
|
||
<p><strong>Décision</strong> : Formulaire in-app structuré</p>
|
||
<p><strong>Accès</strong> :
|
||
- Bouton "Contester cette décision" dans notification
|
||
- Section "Mes sanctions" dans profil créateur</p>
|
||
<p><strong>Formulaire d'appel</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Champ</th>
|
||
<th>Type</th>
|
||
<th>Obligatoire</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Sanction contestée</strong></td>
|
||
<td>Pré-rempli (non modifiable)</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Raison de l'appel</strong></td>
|
||
<td>Texte libre (50-1000 caractères)</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Arguments</strong></td>
|
||
<td>Zone texte enrichie</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Preuves</strong></td>
|
||
<td>Upload fichiers (max 5, 10 MB total)</td>
|
||
<td>❌</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Après soumission</strong> :
|
||
- Génération numéro de ticket unique (ex: <code>#MOD-2026-00142</code>)
|
||
- Email confirmation : "Votre appel sera traité sous 72h"
|
||
- Statut visible dans l'app : "En cours d'examen"</p>
|
||
<p><strong>Délai de soumission</strong> :
|
||
- Maximum <strong>7 jours</strong> après notification de sanction
|
||
- Après 7 jours : appel automatiquement refusé</p>
|
||
<p><strong>Justification</strong> :
|
||
- Professionnel et traçable
|
||
- Intégration complète avec système modération
|
||
- Coût : 0€ (formulaire custom backend)</p>
|
||
<hr />
|
||
<h4 id="1434-delai-de-reponse-pour-appel">14.3.4 Délai de réponse pour appel</h4>
|
||
<p><strong>Décision</strong> : SLA 72h garanti</p>
|
||
<p><strong>Délais</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type d'appel</th>
|
||
<th>Délai</th>
|
||
<th>Responsable</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Standard</strong></td>
|
||
<td>72h max (3 jours ouvrés)</td>
|
||
<td>Modérateur senior</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Complexe</strong></td>
|
||
<td>5 jours ouvrés + notification intermédiaire J+3</td>
|
||
<td>Modérateur senior + Admin modération</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Critique</strong></td>
|
||
<td>24h (cas suspension longue/ban)</td>
|
||
<td>Admin modération</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Notification intermédiaire</strong> (si délai >72h) :
|
||
- Email J+3 : "Votre appel #MOD-XXX est en cours d'examen approfondi. Réponse sous 2 jours."</p>
|
||
<p><strong>Réponse finale</strong> :</p>
|
||
<p>Email détaillé avec :
|
||
1. <strong>Décision</strong> : Maintien / Annulation / Réduction de sanction
|
||
2. <strong>Justification</strong> : explication de la décision d'appel
|
||
3. <strong>Actions</strong> : Strike retiré / Suspension annulée / Contenu rétabli (si applicable)
|
||
4. <strong>Définitif</strong> : mention "Cette décision est définitive" (pas de second appel)</p>
|
||
<p><strong>Suivi in-app</strong> :
|
||
- Mise à jour statut : "Appel accepté ✓" ou "Appel rejeté ✗"
|
||
- Badge notification</p>
|
||
<p><strong>Justification</strong> :
|
||
- Équilibre entre rapidité et qualité de traitement
|
||
- Conforme pratiques industrie (YouTube, TikTok : 5-7 jours)
|
||
- Ressources humaines réalistes</p>
|
||
<hr />
|
||
<h3 id="144-outils-moderateurs">14.4 Outils modérateurs</h3>
|
||
<p><strong>Stack technique complète</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Outil</th>
|
||
<th>Technologie</th>
|
||
<th>Fonction</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Dashboard</strong></td>
|
||
<td>React + TanStack Table</td>
|
||
<td>Interface modération</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>File signalements</strong></td>
|
||
<td>PostgreSQL + Redis</td>
|
||
<td>Priorisation temps réel</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Player audio</strong></td>
|
||
<td>Wavesurfer.js</td>
|
||
<td>Lecture avec waveform + annotations</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Transcription</strong></td>
|
||
<td>Whisper large-v3</td>
|
||
<td>Conversion audio → texte</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Historique créateur</strong></td>
|
||
<td>Vue 360°</td>
|
||
<td>Contenus, strikes, appels, métriques</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Actions rapides</strong></td>
|
||
<td>Shortcuts clavier</td>
|
||
<td>Approuver (A), Rejeter (R), Escalade (E)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Logs audit</strong></td>
|
||
<td>PostgreSQL + export</td>
|
||
<td>Traçabilité complète (DSA)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Collaboration</strong></td>
|
||
<td>Système de commentaires</td>
|
||
<td>Modérateurs peuvent s'entraider sur cas complexes</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Fonctionnalités clés</strong> :</p>
|
||
<ol>
|
||
<li><strong>Lecture accélérée</strong> : 0.75x à 2x (gain productivité)</li>
|
||
<li><strong>Marqueurs temporels</strong> : annotation directe sur waveform</li>
|
||
<li><strong>Historique créateur</strong> : vue rapide contenus précédents + strikes</li>
|
||
<li><strong>Statistiques</strong> : signalements traités/jour, temps moyen, précision</li>
|
||
<li><strong>Fil d'activité</strong> : actions récentes équipe (temps réel)</li>
|
||
</ol>
|
||
<p><strong>Coût infrastructure</strong> :
|
||
- MVP : 0-50€/mois (serveur CPU)
|
||
- Scale : 50-200€/mois (GPU + Redis Cluster)</p>
|
||
<hr />
|
||
<h3 id="145-moderation-preventive-rappel">14.5 Modération préventive (rappel)</h3>
|
||
<p><strong>Nouveaux créateurs</strong> :
|
||
- Validation manuelle des <strong>3 premiers contenus</strong>
|
||
- Délai : 24-48h (jours ouvrés)
|
||
- Transcription automatique pour aide modérateur</p>
|
||
<p><strong>Score de confiance</strong> :
|
||
- Évolution dynamique selon historique
|
||
- Créateur fiable (0 strike depuis 6 mois) → validation automatique
|
||
- Créateur suspect (strikes récents) → validation manuelle systématique</p>
|
||
<p><strong>Publicités</strong> :
|
||
- Validation manuelle obligatoire 24-48h (responsabilité juridique)
|
||
- Transcription + analyse métadonnées (ciblage, durée, volume)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Prévention > réaction (économie modération)
|
||
- Qualité plateforme préservée dès le début</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-14">Récapitulatif Section 14</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Point</th>
|
||
<th>Décision</th>
|
||
<th>Coût</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Catégories signalement</strong></td>
|
||
<td>7 catégories prédéfinies + champ libre</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Commentaire signaleur</strong></td>
|
||
<td>Optionnel avec incitation</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Confirmation</strong></td>
|
||
<td>Toast in-app + historique personnel</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>IA pré-filtre</strong></td>
|
||
<td>Whisper (CPU MVP, GPU scale) + NLP open source</td>
|
||
<td>0-200€/mois</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Délais traitement</strong></td>
|
||
<td>SLA progressif : 2h/24h/72h selon priorité</td>
|
||
<td>Dépend équipe</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Priorisation</strong></td>
|
||
<td>File intelligente basée score IA</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Notification sanction</strong></td>
|
||
<td>Email + push + in-app (multi-canal)</td>
|
||
<td>~0.001€/notif</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Détail sanction</strong></td>
|
||
<td>Complet : raison + extrait + transcription</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Processus appel</strong></td>
|
||
<td>Formulaire in-app structuré</td>
|
||
<td>0€</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Délai appel</strong></td>
|
||
<td>72h garanti (standard)</td>
|
||
<td>Dépend équipe</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Outils modérateurs</strong></td>
|
||
<td>Dashboard React + Whisper + Wavesurfer.js</td>
|
||
<td>0-200€/mois</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Coût total MVP</strong> : <strong>0-200€/mois</strong> (infrastructure IA optionnelle)</p>
|
||
<p><strong>Conformité</strong> :
|
||
- ✅ 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)</p>
|
||
<p><strong>Scalabilité</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<p><strong>Prochaine section à clarifier</strong> : Section 11 (Mode offline) ou Section 12 (Gestion des erreurs)</p>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="15-autres-comportements_1">15. Autres comportements</h2>
|
||
<h3 id="151-partage-de-contenu">15.1 Partage de contenu</h3>
|
||
<p><strong>Décision</strong> : Système de partage complet avec web player</p>
|
||
<h4 id="1511-bouton-partager">15.1.1 Bouton "Partager"</h4>
|
||
<p><strong>Disponibilité</strong> : Partout dans l'application</p>
|
||
<p><strong>Emplacements</strong> :
|
||
- Player en lecture (bouton dans contrôles)
|
||
- Page profil créateur (sur chaque contenu)
|
||
- Liste de recherche (menu contextuel)
|
||
- Historique personnel</p>
|
||
<p><strong>Icône</strong> : ⬆️ (universelle iOS/Android)</p>
|
||
<p><strong>Menu options</strong> :
|
||
- Copier le lien
|
||
- WhatsApp
|
||
- Email
|
||
- SMS
|
||
- Plus... (sheet natif OS)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Viralité = croissance organique gratuite
|
||
- Aucune friction, partage universel</p>
|
||
<hr />
|
||
<h4 id="1512-comportement-du-lien-partage">15.1.2 Comportement du lien partagé</h4>
|
||
<p><strong>Format URL</strong> : <code>https://roadwave.fr/share/c/[content_id]</code></p>
|
||
<p><strong>Comportement multi-plateforme</strong> :</p>
|
||
<pre><code>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│
|
||
└─────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Contenu de la page web</strong> :</p>
|
||
<pre><code class="language-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] │
|
||
└───────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Métadonnées Open Graph (SEO)</strong> :</p>
|
||
<pre><code class="language-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]">
|
||
</code></pre>
|
||
<p><strong>Deep linking</strong> :
|
||
- iOS : Universal Links (configuration <code>apple-app-site-association</code>)
|
||
- Android : App Links (configuration <code>assetlinks.json</code>)
|
||
- URL scheme : <code>roadwave://content/[content_id]</code></p>
|
||
<p><strong>Justification</strong> :
|
||
- Meilleure viralité (partage social optimisé)
|
||
- SEO (contenus indexés Google)
|
||
- UX optimale (web + app)
|
||
- Coût : 0€ (backend simple + CDN existant)</p>
|
||
<hr />
|
||
<h4 id="1513-contenus-premium-partages">15.1.3 Contenus Premium partagés</h4>
|
||
<p><strong>Décision</strong> : Preview 30 secondes + paywall</p>
|
||
<p><strong>Comportement</strong> :</p>
|
||
<ol>
|
||
<li>User clique lien contenu Premium partagé</li>
|
||
<li>Page web affiche badge "👑 Contenu Premium"</li>
|
||
<li>Player démarre automatiquement</li>
|
||
<li>Après <strong>30 secondes exactement</strong> :</li>
|
||
<li>Fade out audio (2 secondes)</li>
|
||
<li>Overlay apparaît :</li>
|
||
</ol>
|
||
<pre><code>┌─────────────────────────────────┐
|
||
│ 👑 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] │
|
||
└─────────────────────────────────┘
|
||
</code></pre>
|
||
<ol>
|
||
<li>Utilisateur peut :</li>
|
||
<li>S'abonner Premium (redirection web Mangopay)</li>
|
||
<li>Télécharger l'app (redirection stores)</li>
|
||
<li>Rejouer les 30 premières secondes (illimité)</li>
|
||
</ol>
|
||
<p><strong>Tracking</strong> :
|
||
- Métriques créateur : "Partages Premium" + "Conversions Premium"
|
||
- Créateur touche sa part si conversion (70%)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Équilibre viralité / monétisation
|
||
- 30s = assez pour donner envie, pas assez pour satisfaire
|
||
- Protège revenus créateurs</p>
|
||
<hr />
|
||
<h3 id="152-profil-createur">15.2 Profil créateur</h3>
|
||
<p><strong>Décision</strong> : Profil public complet et transparent</p>
|
||
<h4 id="1521-structure-de-la-page-profil">15.2.1 Structure de la page profil</h4>
|
||
<p><strong>URL</strong> : <code>https://roadwave.fr/@[pseudo]</code></p>
|
||
<p><strong>Layout</strong> :</p>
|
||
<pre><code>┌────────────────────────────────────────┐
|
||
│ [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] │
|
||
└────────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Informations affichées</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Élément</th>
|
||
<th>Visibilité</th>
|
||
<th>Détails</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Photo + pseudo</strong></td>
|
||
<td>✅ Public</td>
|
||
<td>Identité visuelle</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Badge vérifié ✓</strong></td>
|
||
<td>✅ Public (si applicable)</td>
|
||
<td>Compte authentique</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Bio</strong></td>
|
||
<td>✅ Public</td>
|
||
<td>0-300 caractères, markdown basique (gras, italique, liens)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Nombre abonnés</strong></td>
|
||
<td>✅ Public</td>
|
||
<td>Arrondi si >1000 (ex: 1.2K, 54K)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Nombre contenus</strong></td>
|
||
<td>✅ Public</td>
|
||
<td>Exact</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Durée totale créée</strong></td>
|
||
<td>✅ Public</td>
|
||
<td>Arrondi en heures (ex: 18h, 142h)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Écoutes totales</strong></td>
|
||
<td>✅ Public</td>
|
||
<td>Arrondi (ex: 54K, 1.2M)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Liste abonnés</strong></td>
|
||
<td>❌ Privé</td>
|
||
<td>Protection vie privée (RGPD)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Revenus</strong></td>
|
||
<td>❌ Privé</td>
|
||
<td>Confidentialité financière</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Localisation précise</strong></td>
|
||
<td>❌ Privé</td>
|
||
<td>Sécurité</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Email</strong></td>
|
||
<td>❌ Privé</td>
|
||
<td>Anti-spam</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Tri des contenus</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Option</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Plus récents</strong></td>
|
||
<td>Date publication DESC (défaut)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Plus populaires</strong></td>
|
||
<td>Écoutes complètes × (1 + (date_publication - now) / 90 jours)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Plus anciens</strong></td>
|
||
<td>Date publication ASC</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Par tag</strong></td>
|
||
<td>Filtre multi-sélection tags</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Recherche locale</strong> :
|
||
- Barre recherche dans profil : "Rechercher dans les contenus de @pseudo"
|
||
- Recherche full-text sur titres + descriptions</p>
|
||
<p><strong>Actions menu [•••]</strong> :
|
||
- Partager profil
|
||
- Signaler profil (spam, usurpation)
|
||
- Bloquer créateur (masque tous ses contenus)</p>
|
||
<hr />
|
||
<h4 id="1522-statistiques-publiques">15.2.2 Statistiques publiques</h4>
|
||
<p><strong>Décision</strong> : Stats arrondies et motivantes</p>
|
||
<p><strong>Affichage public</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Métrique</th>
|
||
<th>Format affichage</th>
|
||
<th>Exemple</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Abonnés</strong></td>
|
||
<td>Exact si <1000, arrondi sinon</td>
|
||
<td>342 / 1.2K / 54K / 1.2M</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Écoutes totales</strong></td>
|
||
<td>Arrondi dès 1000</td>
|
||
<td>842 / 5.4K / 142K / 2.1M</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Contenus publiés</strong></td>
|
||
<td>Exact</td>
|
||
<td>42 contenus</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Durée totale</strong></td>
|
||
<td>Arrondi en heures</td>
|
||
<td>18h / 142h de contenu</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Métriques PRIVÉES (créateur uniquement)</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Métrique</th>
|
||
<th>Disponible dans dashboard créateur</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Taux complétion moyen</strong></td>
|
||
<td>78% (écoutes >80% / écoutes totales)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Évolution abonnés</strong></td>
|
||
<td>Graphique 30j / 90j / 1 an</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Écoutes par contenu</strong></td>
|
||
<td>Tableau détaillé</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Revenus</strong></td>
|
||
<td>Dashboard monétisation dédié</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Taux conversion Premium</strong></td>
|
||
<td>Partages → conversions</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Démographie</strong></td>
|
||
<td>Âge / zone géo (agrégée, anonymisée)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Justification</strong> :
|
||
- Arrondi = évite comparaisons anxiogènes
|
||
- Preuve sociale pour nouveaux auditeurs (trust)
|
||
- Gamification douce (motivation créateurs)
|
||
- Privacy by design</p>
|
||
<hr />
|
||
<h4 id="1523-badge-verifie">15.2.3 Badge vérifié</h4>
|
||
<p><strong>Décision</strong> : Badge unique ✓ (vérifié officiel)</p>
|
||
<p><strong>Critères d'attribution</strong> (au moins UN des critères) :</p>
|
||
<ol>
|
||
<li><strong>KYC monétisation validé</strong> : identité vérifiée via Mangopay KYC</li>
|
||
<li><strong>Célébrité / Média officiel</strong> : validation manuelle équipe RoadWave</li>
|
||
<li><strong>Communauté significative</strong> : ≥10K abonnés + compte actif >6 mois</li>
|
||
</ol>
|
||
<p><strong>Affichage</strong> :
|
||
- Badge bleu <strong>✓</strong> accolé au pseudo (partout : profil, player, recherche)
|
||
- Tooltip au survol/appui long : "Compte vérifié"</p>
|
||
<p><strong>Processus d'obtention</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Processus</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Automatique (KYC)</strong></td>
|
||
<td>Badge attribué dès validation documents Mangopay</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Manuel (célébrité)</strong></td>
|
||
<td>Formulaire demande → équipe vérifie identité → validation 48-72h</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Automatique (10K)</strong></td>
|
||
<td>Badge attribué automatiquement à 10K abonnés si compte >6 mois</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Retrait du badge</strong> :
|
||
- Suspension monétisation → badge retiré temporairement
|
||
- Strikes multiples → badge retiré définitivement
|
||
- Usurpation identité détectée → ban + retrait</p>
|
||
<p><strong>Justification</strong> :
|
||
- 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 <code>verified</code> en DB)</p>
|
||
<hr />
|
||
<h3 id="153-recherche">15.3 Recherche</h3>
|
||
<p><strong>Décision</strong> : Recherche full-text + géo + filtres avancés</p>
|
||
<h4 id="1531-recherche-par-mot-cle">15.3.1 Recherche par mot-clé</h4>
|
||
<p><strong>Implémentation</strong> : PostgreSQL full-text search (français)</p>
|
||
<p><strong>Configuration technique</strong> :</p>
|
||
<pre><code class="language-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;
|
||
</code></pre>
|
||
<p><strong>Champs indexés</strong> :
|
||
- Titre du contenu (poids × 3)
|
||
- Description (poids × 1)
|
||
- Pseudo créateur (poids × 2)
|
||
- Tags (poids × 1.5)</p>
|
||
<p><strong>Fonctionnalités</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Feature</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Stemming français</strong></td>
|
||
<td>"voyages" trouve "voyage", "voyager", etc.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Correction auto</strong></td>
|
||
<td>Suggestion si 0 résultat</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Recherches populaires</strong></td>
|
||
<td>"Essayez plutôt : balade paris, audio-guide louvre"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Historique personnel</strong></td>
|
||
<td>10 dernières recherches sauvegardées</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Autocomplete</strong></td>
|
||
<td>Suggestions pendant frappe (top 5)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Coût</strong> : 0€ (PostgreSQL natif)</p>
|
||
<p><strong>Migration future</strong> :
|
||
- Si >100K contenus : Meilisearch (typo-tolerance avancée, ~20-50€/mois)
|
||
- Si >1M contenus : Elasticsearch cluster</p>
|
||
<p><strong>Justification</strong> :
|
||
- PostgreSQL full-text = performant jusqu'à 500K contenus
|
||
- Stemming français natif
|
||
- 0€, aucune dépendance externe</p>
|
||
<hr />
|
||
<h4 id="1532-recherche-geographique">15.3.2 Recherche géographique</h4>
|
||
<p><strong>Décision</strong> : Recherche lieu + rayon paramétrable</p>
|
||
<p><strong>Interface utilisateur</strong> :</p>
|
||
<pre><code>┌─────────────────────────────────────┐
|
||
│ 🔍 Recherche contenu... │
|
||
├─────────────────────────────────────┤
|
||
│ <20><> 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] │
|
||
└─────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Géocodage</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Service</th>
|
||
<th>Usage</th>
|
||
<th>Coût</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Nominatim (OSM)</strong></td>
|
||
<td>MVP (API publique)</td>
|
||
<td>0€ (rate limit 1 req/s)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Nominatim self-hosted</strong></td>
|
||
<td>Scale (Docker)</td>
|
||
<td>20-50€/mois VPS</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Mapbox Geocoding</strong></td>
|
||
<td>Fallback premium</td>
|
||
<td>0.50€ / 1000 requêtes</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Processus de recherche géo</strong> :</p>
|
||
<ol>
|
||
<li>User tape "Louvre" ou "Paris"</li>
|
||
<li>Autocomplete via Nominatim → liste suggestions</li>
|
||
<li>User sélectionne → récupération coordonnées (lat, lon)</li>
|
||
<li>Requête PostGIS :</li>
|
||
</ol>
|
||
<pre><code class="language-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;
|
||
</code></pre>
|
||
<p><strong>Affichage résultats</strong> :
|
||
- Tri par défaut : distance croissante
|
||
- Indication distance : "À 2.3 km" / "À 15 km" / "À 142 km"
|
||
- Option carte : markers cliquables (clustering si >50 résultats)</p>
|
||
<p><strong>Coût</strong> :
|
||
- MVP : 0€ (Nominatim public)
|
||
- Scale : 20-50€/mois (Nominatim self-hosted Docker)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Essentiel pour tourisme / planification trajet
|
||
- OpenStreetMap = pas de dépendance Google
|
||
- PostGIS = performant (index GIST natif)</p>
|
||
<hr />
|
||
<h4 id="1533-filtres-avances">15.3.3 Filtres avancés</h4>
|
||
<p><strong>Décision</strong> : 7 catégories de filtres combinables</p>
|
||
<p><strong>Interface filtres</strong> :</p>
|
||
<pre><code>┌─────────────────────────────────────┐
|
||
│ 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] │
|
||
└─────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Options de tri</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Tri</th>
|
||
<th>Algorithme</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Pertinence</strong></td>
|
||
<td>Score recherche × (1 + log(listen_count + 1))</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Popularité</strong></td>
|
||
<td>Écoutes complètes derniers 30j DESC</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Récent</strong></td>
|
||
<td>Date publication DESC</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Proximité</strong></td>
|
||
<td>Distance GPS ASC (si recherche géo active)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Durée</strong></td>
|
||
<td>Durée audio ASC ou DESC</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Sauvegarde de recherches</strong> :</p>
|
||
<ul>
|
||
<li>Bouton "💾 Sauvegarder cette recherche"</li>
|
||
<li>Nom personnalisable : "Podcasts voyage Paris"</li>
|
||
<li>Maximum <strong>5 recherches sauvegardées</strong></li>
|
||
<li>Accès rapide : onglet "Recherches sauvegardées" dans page recherche</li>
|
||
<li>Notifications optionnelles : "3 nouveaux contenus dans 'Podcasts voyage Paris'"</li>
|
||
</ul>
|
||
<p><strong>Performances</strong> :</p>
|
||
<pre><code class="language-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);
|
||
</code></pre>
|
||
<p><strong>Coût</strong> : 0€ (PostgreSQL + index standards)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Filtres essentiels pour découvrabilité
|
||
- Combinables = puissance maximale
|
||
- Sauvegarde = gain temps utilisateurs réguliers</p>
|
||
<hr />
|
||
<h4 id="1534-page-de-resultats">15.3.4 Page de résultats</h4>
|
||
<p><strong>Décision</strong> : Liste avec previews enrichies</p>
|
||
<p><strong>Layout résultats</strong> :</p>
|
||
<pre><code>┌─────────────────────────────────────────┐
|
||
│ 🔍 "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) │
|
||
└─────────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Informations par résultat</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Élément</th>
|
||
<th>Affichage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Cover image</strong></td>
|
||
<td>16:9, 120×68 px, lazy loading</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Titre</strong></td>
|
||
<td>Tronqué 2 lignes max</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Créateur</strong></td>
|
||
<td>@pseudo + badge ✓ si vérifié, cliquable → profil</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Durée</strong></td>
|
||
<td>Format : "3 min" / "12 min" / "1h 24 min"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Écoutes</strong></td>
|
||
<td>Arrondi : "2.3K" / "54K" / "1.2M"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Localisation</strong></td>
|
||
<td>Ville + type géo (Ancré/Contextuel/Neutre)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tags</strong></td>
|
||
<td>Maximum 3 premiers tags</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Badge Premium</strong></td>
|
||
<td>👑 si contenu premium</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Distance</strong></td>
|
||
<td>Si recherche géo : "À 2.3 km"</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Actions contextuelles [⋮]</strong> :
|
||
- Partager
|
||
- Ajouter à une playlist (future feature)
|
||
- Télécharger (offline)
|
||
- Signaler</p>
|
||
<p><strong>Pagination</strong> :
|
||
- <strong>20 résultats</strong> par page
|
||
- Infinite scroll (charger automatiquement si scroll >80%)
|
||
- Bouton "Charger 20 suivants" en bas (fallback si scroll auto désactivé)</p>
|
||
<p><strong>Vue carte (alternative)</strong> :
|
||
- Bouton toggle "Liste / Carte"
|
||
- Map Leaflet (OpenStreetMap)
|
||
- Markers cliquables → popup avec preview
|
||
- Clustering si >50 résultats proches</p>
|
||
<p><strong>Coût</strong> : 0€ (Leaflet open source + OSM tiles gratuit)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Équilibre information / compacité
|
||
- Lazy loading = performances
|
||
- Infinite scroll = UX moderne</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-15">Récapitulatif Section 15</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Point</th>
|
||
<th>Décision</th>
|
||
<th>Coût</th>
|
||
<th>Complexité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>15.1.1</strong> Bouton partager</td>
|
||
<td>Disponible partout (⬆️), menu natif OS</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.1.2</strong> Lien partagé</td>
|
||
<td>Web player + deep link + Open Graph SEO</td>
|
||
<td>0€</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.1.3</strong> Premium partagé</td>
|
||
<td>Preview 30s + paywall overlay</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.2.1</strong> Page profil</td>
|
||
<td>Profil public complet (stats + bio + contenus + tri)</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.2.2</strong> Stats publiques</td>
|
||
<td>Arrondies (abonnés, écoutes, durée totale)</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.2.3</strong> Badge vérifié</td>
|
||
<td>✓ si KYC/célébrité/>10K abonnés</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.3.1</strong> Recherche texte</td>
|
||
<td>PostgreSQL full-text french + stemming</td>
|
||
<td>0€</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.3.2</strong> Recherche géo</td>
|
||
<td>Lieu + rayon (Nominatim OSM)</td>
|
||
<td>0-50€/mois</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.3.3</strong> Filtres</td>
|
||
<td>7 catégories combinables + sauvegarde recherches</td>
|
||
<td>0€</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>15.3.4</strong> Page résultats</td>
|
||
<td>Liste enrichie + vue carte Leaflet + infinite scroll</td>
|
||
<td>0€</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Coût total MVP : 0-50€/mois</strong> (Nominatim self-hosted optionnel)</p>
|
||
<hr />
|
||
<h2 id="points-dattention-pour-gherkin_1">Points d'attention pour Gherkin</h2>
|
||
<ul>
|
||
<li>Tester partage contenu public vs Premium (preview 30s)</li>
|
||
<li>Tester deep linking iOS/Android (ouverture app si installée)</li>
|
||
<li>Tester Open Graph (aperçu correct sur WhatsApp, Twitter, Facebook)</li>
|
||
<li>Tester profil public (stats arrondies, badge vérifié)</li>
|
||
<li>Tester recherche full-text français (stemming, accents)</li>
|
||
<li>Tester recherche géo + rayon (PostGIS distance)</li>
|
||
<li>Tester combinaison filtres multiples (AND logic)</li>
|
||
<li>Tester sauvegarde recherches (max 5)</li>
|
||
<li>Tester pagination infinite scroll + fallback bouton</li>
|
||
<li>Tester vue carte Leaflet (clustering, markers cliquables)</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h2 id="16-audio-guides-multi-sequences_1">16. Audio-guides multi-séquences</h2>
|
||
<h3 id="161-types-daudio-guides-et-modes-de-deplacement">16.1 Types d'audio-guides et modes de déplacement</h3>
|
||
<p><strong>Décision</strong> : 4 modes distincts avec détection automatique</p>
|
||
<h4 id="1611-classification-par-mode">16.1.1 Classification par mode</h4>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Mode</th>
|
||
<th>Vitesse détection</th>
|
||
<th>Déclenchement</th>
|
||
<th>Use case</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>🚶 Piéton</strong></td>
|
||
<td><5 km/h</td>
|
||
<td>Manuel (bouton "Suivant")</td>
|
||
<td>Musées, visites urbaines, monuments</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>🚗 Voiture</strong></td>
|
||
<td>>10 km/h</td>
|
||
<td>Auto GPS + Manuel possible</td>
|
||
<td>Safari-parc, routes touristiques, circuits auto</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>🚴 Vélo</strong></td>
|
||
<td>5-25 km/h</td>
|
||
<td>Auto GPS + Manuel possible</td>
|
||
<td>Pistes cyclables, circuits vélo, parcours nature</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>🚌 Transport</strong></td>
|
||
<td>Variable</td>
|
||
<td>Auto GPS + Manuel possible</td>
|
||
<td>Bus touristiques, trains panoramiques</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Détection automatique</strong> :
|
||
- 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)</p>
|
||
<p><strong>Justification</strong> :
|
||
- 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)</p>
|
||
<hr />
|
||
<h4 id="1612-creation-dun-audio-guide-cote-createur">16.1.2 Création d'un audio-guide (côté créateur)</h4>
|
||
<p><strong>Formulaire création</strong> :</p>
|
||
<pre><code>┌────────────────────────────────────────┐
|
||
│ 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] │
|
||
└────────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Métadonnées obligatoires</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Champ</th>
|
||
<th>Requis</th>
|
||
<th>Détails</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Titre audio-guide</strong></td>
|
||
<td>✅</td>
|
||
<td>5-100 caractères</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Description</strong></td>
|
||
<td>✅</td>
|
||
<td>10-500 caractères</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Mode déplacement</strong></td>
|
||
<td>✅</td>
|
||
<td>Piéton / Voiture / Vélo / Transport</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Nombre séquences</strong></td>
|
||
<td>✅</td>
|
||
<td>Minimum 2, maximum 50</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Point GPS par séquence</strong></td>
|
||
<td>✅ (sauf piéton)</td>
|
||
<td>Latitude, longitude (WGS84)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Rayon déclenchement</strong></td>
|
||
<td>✅ (sauf piéton)</td>
|
||
<td>10-100m selon mode</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Vitesse recommandée</strong></td>
|
||
<td>❌</td>
|
||
<td>Optionnel, affichée utilisateur</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tags</strong></td>
|
||
<td>✅</td>
|
||
<td>1-3 parmi liste prédéfinie</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Classification âge</strong></td>
|
||
<td>✅</td>
|
||
<td>Tout public / 13+ / 16+ / 18+</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Zone diffusion</strong></td>
|
||
<td>✅</td>
|
||
<td>Polygon géographique</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Wizard de création</strong> :
|
||
- É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)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Contrôle total créateur sur expérience
|
||
- Carte preview aide visualiser parcours
|
||
- Wizard guidé = réduction friction création</p>
|
||
<hr />
|
||
<h3 id="162-mode-pieton-manuel">16.2 Mode Piéton (manuel)</h3>
|
||
<p><strong>Décision</strong> : Navigation manuelle avec pub auto-play</p>
|
||
<h4 id="1621-passage-entre-sequences">16.2.1 Passage entre séquences</h4>
|
||
<p><strong>Séquence normale (sans pub)</strong> :</p>
|
||
<ol>
|
||
<li>Séquence 1 se termine</li>
|
||
<li>Player se met en <strong>pause automatique</strong></li>
|
||
<li>Message affiché : "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt."</li>
|
||
<li>User appuie sur [▶|] → Séquence 2 démarre immédiatement</li>
|
||
</ol>
|
||
<p><strong>Séquence avec publicité</strong> (1 pub / 5 séquences) :</p>
|
||
<ol>
|
||
<li>Séquence 2 se termine</li>
|
||
<li><strong>Publicité s'enchaîne automatiquement</strong> (pas d'attente bouton)</li>
|
||
<li>Pub se lit (skippable après 5s)</li>
|
||
<li>Pub se termine → Player se met en <strong>pause automatique</strong></li>
|
||
<li>Message : "Séquence 3 prête. Appuyez sur Suivant."</li>
|
||
<li>User appuie sur [▶|] → Séquence 3 démarre</li>
|
||
</ol>
|
||
<p><strong>Schéma flux</strong> :</p>
|
||
<pre><code>Séquence 1 [fin] → PAUSE → User clique → Séquence 2 [fin] → PUB AUTO-PLAY → PAUSE → User clique → Séquence 3
|
||
</code></pre>
|
||
<p><strong>Fréquence pub</strong> :
|
||
- Gratuits : 1 pub toutes les 5 séquences (paramétrable admin 1/3 à 1/10)
|
||
- Premium : 0 pub</p>
|
||
<p><strong>Justification</strong> :
|
||
- 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)</p>
|
||
<hr />
|
||
<h4 id="1622-navigation-et-controles">16.2.2 Navigation et contrôles</h4>
|
||
<p><strong>Décision</strong> : Liberté totale utilisateur</p>
|
||
<p><strong>Contrôles disponibles</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Bouton</th>
|
||
<th>Fonction</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>[▶|] Suivant</strong></td>
|
||
<td>Passe séquence suivante</td>
|
||
<td>Immédiat, même si séquence actuelle pas terminée</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>[|◀] Précédent</strong></td>
|
||
<td>Retour séquence précédente</td>
|
||
<td>Saut direct séquence avant (pas de logique "replay si >10s")</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>[⏸️] Pause</strong></td>
|
||
<td>Pause temporaire</td>
|
||
<td>Reprend à position exacte</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>[▶️] Play</strong></td>
|
||
<td>Reprend lecture</td>
|
||
<td>Continue position actuelle</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Liste séquences</strong></td>
|
||
<td>Navigation libre</td>
|
||
<td>Tap séquence → saut direct (même séquences non écoutées)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Interface liste séquences</strong> :</p>
|
||
<pre><code>┌────────────────────────────────────────┐
|
||
│ 🚶 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 ▼] │
|
||
└────────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Navigation libre</strong> :
|
||
- 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)</p>
|
||
<p><strong>Sauvegarde progression</strong> :
|
||
- Checkmarks ✅ sur séquences écoutées >80%
|
||
- Position exacte sauvegardée dans séquence en cours</p>
|
||
<p><strong>Justification</strong> :
|
||
- 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)</p>
|
||
<hr />
|
||
<h3 id="163-mode-voiture-gps-automatique">16.3 Mode Voiture (GPS automatique)</h3>
|
||
<p><strong>Décision</strong> : GPS auto avec navigation manuelle conservée</p>
|
||
<h4 id="1631-declenchement-et-controles">16.3.1 Déclenchement et contrôles</h4>
|
||
<p><strong>Distinction audio-guides vs contenus géolocalisés simples</strong> :</p>
|
||
<p>⚠️ <strong>Important</strong> : Les audio-guides multi-séquences fonctionnent différemment des contenus géolocalisés simples.</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Type</th>
|
||
<th>Séquences</th>
|
||
<th>Déclenchement</th>
|
||
<th>Notification</th>
|
||
<th>Enchaînement</th>
|
||
<th>Comptabilité quota</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Contenu géolocalisé simple</strong></td>
|
||
<td>1 séquence unique</td>
|
||
<td>Notification 7s avant (temps ETA)</td>
|
||
<td>Sonore + icône</td>
|
||
<td>Fin → retour buffer normal</td>
|
||
<td>1 contenu = 1 quota</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Audio-guide multi-séquences</strong></td>
|
||
<td>2 à 50 séquences</td>
|
||
<td>Au point GPS exact (distance 30m)</td>
|
||
<td>Ding + toast 2s</td>
|
||
<td>Séquences s'enchaînent auto</td>
|
||
<td>1 audio-guide = 1 quota (toutes séquences)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Fonctionnement GPS automatique</strong> :</p>
|
||
<ol>
|
||
<li>User démarre audio-guide en voiture (voir section 16.1 pour démarrage)</li>
|
||
<li>Séquence 1 démarre automatiquement au point GPS défini (rayon 30m)</li>
|
||
<li>Séquence 1 se termine</li>
|
||
<li><strong>Affichage progress bar</strong> : distance temps réel + ETA jusqu'au prochain point</li>
|
||
<li>User roule vers point GPS suivant</li>
|
||
<li>Arrivée au point GPS suivant (rayon 30m) → <strong>déclenchement automatique</strong> séquence suivante</li>
|
||
<li>Notification sonore discrète : "Ding" (0.3s) + toast 2s : "Enclos des girafes"</li>
|
||
<li>Séquence suivante démarre immédiatement (pas de décompte)</li>
|
||
</ol>
|
||
<p><strong>Pas de système "7 secondes avant" pour les audio-guides</strong> :
|
||
- Contrairement aux contenus géolocalisés simples (voir <a href="#05-interactions-navigation.md#511-file-dattente-et-commande-suivant">05-interactions-navigation.md</a>)
|
||
- Les séquences se déclenchent <strong>au point GPS exact</strong> (rayon 30m)
|
||
- Raison : expérience guidée continue, user sait qu'il suit un parcours</p>
|
||
<p><strong>Navigation manuelle CONSERVÉE</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Bouton</th>
|
||
<th>État</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>[▶|] Suivant</strong></td>
|
||
<td>✅ Toujours actif</td>
|
||
<td>Passe séquence suivante immédiatement (même hors point GPS)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>[|◀] Précédent</strong></td>
|
||
<td>✅ Toujours actif</td>
|
||
<td>Retour séquence précédente (même hors point GPS)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>[⏸️] Pause</strong></td>
|
||
<td>✅</td>
|
||
<td>Pause temporaire</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Liste séquences</strong></td>
|
||
<td>✅</td>
|
||
<td>Saut direct possible</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Use cases navigation manuelle</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Situation</th>
|
||
<th>Solution manuelle</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Embouteillage (séquence finie, point GPS loin)</td>
|
||
<td>User clique Suivant → avance manuellement</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Point GPS inaccessible (route fermée)</td>
|
||
<td>User clique Suivant → skip point</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Envie réécouter séquence précédente</td>
|
||
<td>User clique Précédent → retour</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Passager manipule l'app</td>
|
||
<td>Passager navigue librement</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Avertissement sécurité</strong> :</p>
|
||
<ul>
|
||
<li>Si vitesse <strong>>10 km/h</strong> ET user clique bouton (Suivant/Précédent) :</li>
|
||
<li>Toast 3 secondes : "⚠️ Manipulation en conduite détectée. Pour votre sécurité, demandez à un passager."</li>
|
||
<li><strong>Action quand même exécutée</strong> (pas de blocage)</li>
|
||
<li>Justification : sensibilisation sans bloquer (passager peut légitimement manipuler)</li>
|
||
</ul>
|
||
<p><strong>Schéma flux</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- 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)</p>
|
||
<hr />
|
||
<h4 id="1632-affichage-distance-et-guidage">16.3.2 Affichage distance et guidage</h4>
|
||
<p><strong>Décision</strong> : Distance + direction (PAS de carte miniature)</p>
|
||
<p><strong>Interface en conduite</strong> :</p>
|
||
<pre><code>┌────────────────────────────────────────┐
|
||
│ 🚗 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] │
|
||
└────────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Affichage entre deux séquences</strong> :</p>
|
||
<p>Quand une séquence se termine et qu'il reste un point GPS suivant, l'interface bascule en mode "attente prochain point" :</p>
|
||
<pre><code>┌────────────────────────────────────────┐
|
||
│ 🚗 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.] [▶|] │
|
||
└────────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Progress bar dynamique</strong> :
|
||
- Se remplit au fur et à mesure qu'on se rapproche du point
|
||
- Calcul : <code>progress = 100 - (distance_actuelle / distance_initiale * 100)</code>
|
||
- Exemple : distance initiale 500m, distance actuelle 175m → progress = 65%
|
||
- Couleur : vert (#4CAF50) pour la partie remplie, gris (#E0E0E0) pour le reste</p>
|
||
<p><strong>Bouton "Rejouer séq."</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<p><strong>Informations affichées</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Info</th>
|
||
<th>Mise à jour</th>
|
||
<th>Format</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Distance</strong></td>
|
||
<td>Chaque seconde</td>
|
||
<td>"320 m" / "1.2 km"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>ETA</strong></td>
|
||
<td>Chaque seconde</td>
|
||
<td>"≈ 40 secondes" / "≈ 2 minutes"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Direction</strong></td>
|
||
<td>Chaque 5s</td>
|
||
<td>Flèche indique direction (8 directions : ↑ ↗ → ↘ ↓ ↙ ← ↖)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Vitesse actuelle</strong></td>
|
||
<td>Chaque seconde</td>
|
||
<td>"28 km/h"</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Vitesse recommandée</strong></td>
|
||
<td>Statique</td>
|
||
<td>"20-30 km/h" (définie par créateur)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Progress bar</strong></td>
|
||
<td>Chaque seconde</td>
|
||
<td>Pourcentage parcouru vers prochain point</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Calcul direction</strong> :</p>
|
||
<pre><code class="language-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];
|
||
</code></pre>
|
||
<p><strong>Calcul ETA</strong> :</p>
|
||
<pre><code class="language-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";
|
||
}
|
||
</code></pre>
|
||
<p><strong>Justification</strong> :
|
||
- 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)</p>
|
||
<hr />
|
||
<h4 id="1633-rayon-de-declenchement-et-tolerance">16.3.3 Rayon de déclenchement et tolérance</h4>
|
||
<p><strong>Décision</strong> : Rayon configurable créateur avec défauts intelligents</p>
|
||
<p><strong>Rayons par défaut</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Mode</th>
|
||
<th>Rayon déclenchement</th>
|
||
<th>Rayon "point manqué"</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>🚗 Voiture</strong></td>
|
||
<td>30 mètres</td>
|
||
<td>100 mètres</td>
|
||
<td>Vitesse élevée = anticipation</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>🚴 Vélo</strong></td>
|
||
<td>50 mètres</td>
|
||
<td>75 mètres</td>
|
||
<td>Vitesse variable, arrêts fréquents</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>🚌 Transport</strong></td>
|
||
<td>100 mètres</td>
|
||
<td>150 mètres</td>
|
||
<td>Arrêts bus/train, moins précis</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Configuration créateur</strong> :</p>
|
||
<ul>
|
||
<li>Curseur rayon : <strong>10m → 200m</strong></li>
|
||
<li>Défaut pré-sélectionné selon mode choisi</li>
|
||
<li>Preview visuel : cercle sur carte (lors création)</li>
|
||
<li>Suggestion auto : "Recommandé : 30m pour voiture à 30 km/h"</li>
|
||
</ul>
|
||
<p><strong>Gestion point manqué</strong> :</p>
|
||
<pre><code>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] │
|
||
└────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Actions popup</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Bouton</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Écouter quand même</strong></td>
|
||
<td>Lance séquence immédiatement (même hors zone)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Passer au suivant</strong></td>
|
||
<td>Skip séquence, continue vers prochain point</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Faire demi-tour</strong></td>
|
||
<td>Lance navigation GPS externe (Google Maps / Waze) vers point manqué</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Si user au-delà rayon tolérance (>100m)</strong> :
|
||
- Aucun popup (point trop loin, probablement hors itinéraire)
|
||
- User peut naviguer manuellement (bouton Suivant)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Flexibilité créateur (ajuste selon terrain, vitesse prévue)
|
||
- Gestion intelligente imprévus (détours, routes fermées)
|
||
- User pas bloqué (toujours moyen avancer)</p>
|
||
<hr />
|
||
<h3 id="164-modes-velo-et-transport">16.4 Modes Vélo et Transport</h3>
|
||
<p><strong>Décision</strong> : Même logique voiture avec tolérances ajustées</p>
|
||
<p><strong>Différences par rapport à mode voiture</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Paramètre</th>
|
||
<th>Voiture</th>
|
||
<th>Vélo</th>
|
||
<th>Transport</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Rayon déclenchement</strong></td>
|
||
<td>30m</td>
|
||
<td>50m</td>
|
||
<td>100m</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Rayon tolérance "point manqué"</strong></td>
|
||
<td>100m</td>
|
||
<td>75m</td>
|
||
<td>150m</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Vitesse recommandée affichée</strong></td>
|
||
<td>20-50 km/h</td>
|
||
<td>10-25 km/h</td>
|
||
<td>Variable (selon ligne)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Warning sécurité</strong></td>
|
||
<td>>10 km/h</td>
|
||
<td>>5 km/h</td>
|
||
<td>Désactivé</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Mode Vélo spécificités</strong> :</p>
|
||
<ul>
|
||
<li>Rayon plus large : vitesse variable, nombreux arrêts (feux, piétons)</li>
|
||
<li>Warning sécurité dès 5 km/h (vélo en mouvement)</li>
|
||
<li>Tolérance GPS moins stricte (tracé moins prévisible qu'auto)</li>
|
||
</ul>
|
||
<p><strong>Mode Transport spécificités</strong> :</p>
|
||
<ul>
|
||
<li>Rayon très large : arrêts fréquents (bus, train), ligne fixe</li>
|
||
<li>Pas de warning sécurité (user = passager, pas conducteur)</li>
|
||
<li>Vitesse recommandée = "Selon ligne" (pas de valeur fixe)</li>
|
||
<li>Tolérance horaire : si bus en retard, point peut se déclencher avec 2-3 min de délai</li>
|
||
</ul>
|
||
<p><strong>Comportement identique voiture</strong> :</p>
|
||
<ul>
|
||
<li>Navigation manuelle conservée (boutons actifs)</li>
|
||
<li>Affichage distance + ETA + direction</li>
|
||
<li>Gestion point manqué</li>
|
||
<li>Pub entre séquences</li>
|
||
</ul>
|
||
<p><strong>Justification</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<h3 id="165-publicites-dans-audio-guides">16.5 Publicités dans audio-guides</h3>
|
||
<p><strong>Décision</strong> : Pub auto-play entre séquences TOUS modes</p>
|
||
<h4 id="1651-regles-universelles">16.5.1 Règles universelles</h4>
|
||
<p><strong>Insertion publicité</strong> :</p>
|
||
<ul>
|
||
<li>Fréquence : <strong>1 pub toutes les 5 séquences</strong> (paramétrable admin 1/3 à 1/10)</li>
|
||
<li>Gratuits uniquement, <strong>Premium 0 pub</strong></li>
|
||
<li>Pub s'enchaîne <strong>automatiquement</strong> après séquence</li>
|
||
<li>Skippable après <strong>5 secondes</strong> (règle standard RoadWave)</li>
|
||
<li>Volume normalisé -14 LUFS (comme pubs normales)</li>
|
||
</ul>
|
||
<p><strong>Comportement MODE PIÉTON</strong> :</p>
|
||
<pre><code>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
|
||
</code></pre>
|
||
<p><strong>Comportement MODE VOITURE/VÉLO/TRANSPORT</strong> :</p>
|
||
<pre><code>Séquence 2 [fin]
|
||
→ Pub AUTO-PLAY
|
||
→ Pub se termine
|
||
→ ATTENTE point GPS suivant OU user clique Suivant
|
||
→ Séquence 3 démarre
|
||
</code></pre>
|
||
<p><strong>Schéma complet</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Mode</th>
|
||
<th>Après séquence normale</th>
|
||
<th>Après pub</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Piéton</strong></td>
|
||
<td>Pause + attente user</td>
|
||
<td>Pause + attente user</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Voiture</strong></td>
|
||
<td>Attente GPS OU user clique Suivant</td>
|
||
<td>Attente GPS OU user clique Suivant</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Vélo</strong></td>
|
||
<td>Attente GPS OU user clique Suivant</td>
|
||
<td>Attente GPS OU user clique Suivant</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Transport</strong></td>
|
||
<td>Attente GPS OU user clique Suivant</td>
|
||
<td>Attente GPS OU user clique Suivant</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Justification</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<h4 id="1652-metriques-pub-audio-guides">16.5.2 Métriques pub audio-guides</h4>
|
||
<p><strong>Dashboard créateur</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Métrique</th>
|
||
<th>Affichage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Impressions pub</strong></td>
|
||
<td>Nombre de pubs insérées dans audio-guides</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Écoutes complètes pub</strong></td>
|
||
<td>Nombre de pubs écoutées >80%</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Taux skip pub</strong></td>
|
||
<td>% pubs skippées avant 5s vs après</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Revenus pub audio-guides</strong></td>
|
||
<td>3€ / 1000 écoutes complètes (6% CA pub)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Distinction contenus normaux vs audio-guides</strong> :
|
||
- Dashboard sépare : "Revenus contenus classiques" / "Revenus audio-guides"
|
||
- Permet créateur voir performance par type</p>
|
||
<p><strong>Justification</strong> :
|
||
- Transparence créateur (comprend revenus)
|
||
- Incite création audio-guides (nouvelle source revenus)</p>
|
||
<hr />
|
||
<h3 id="166-reprise-et-sauvegarde-progression">16.6 Reprise et sauvegarde progression</h3>
|
||
<p><strong>Décision</strong> : Sauvegarde complète automatique avec popup intelligente</p>
|
||
<h4 id="1661-sauvegarde-automatique">16.6.1 Sauvegarde automatique</h4>
|
||
<p><strong>Données sauvegardées</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Info</th>
|
||
<th>Détail</th>
|
||
<th>Utilité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Audio-guide ID</strong></td>
|
||
<td>Identifiant unique</td>
|
||
<td>Retrouver audio-guide</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Séquence actuelle</strong></td>
|
||
<td>Index (ex: 3/12)</td>
|
||
<td>Reprise position</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Position dans séquence</strong></td>
|
||
<td>Timestamp exact (ex: 1:42/3:20)</td>
|
||
<td>Reprise exacte</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Séquences écoutées</strong></td>
|
||
<td>Liste avec checkmarks ✅</td>
|
||
<td>Historique progression</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Date dernière écoute</strong></td>
|
||
<td>Timestamp</td>
|
||
<td>Proposer reprise si <30j</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>GPS dernière position</strong></td>
|
||
<td>Coordonnées optionnelles</td>
|
||
<td>Info contextuelle (non utilisée pour reprise)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Stockage</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Environnement</th>
|
||
<th>Technologie</th>
|
||
<th>Utilité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Local</strong></td>
|
||
<td>SQLite mobile</td>
|
||
<td>Fonctionnement offline</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Cloud</strong></td>
|
||
<td>PostgreSQL (sync auto)</td>
|
||
<td>Multi-device (reprendre sur autre appareil)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Synchronisation</strong> :
|
||
- Sauvegarde locale : chaque fin de séquence + chaque 30s
|
||
- Sync cloud : à la reconnexion réseau (batch)</p>
|
||
<p><strong>Justification</strong> :
|
||
- Expérience fluide (pas de perte progression)
|
||
- Multi-device (démarrer sur iPhone, continuer sur iPad)
|
||
- Offline-first (fonctionne sans réseau)</p>
|
||
<hr />
|
||
<h4 id="1662-interface-de-reprise">16.6.2 Interface de reprise</h4>
|
||
<p><strong>Conditions popup</strong> :
|
||
- Dernière écoute <strong><30 jours</strong>
|
||
- Progression <strong>>0%</strong> et <strong><100%</strong> (pas terminé)</p>
|
||
<p><strong>Popup reprise</strong> :</p>
|
||
<pre><code>┌────────────────────────────────────────┐
|
||
│ 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] │
|
||
└────────────────────────────────────────┘
|
||
</code></pre>
|
||
<p><strong>Actions</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Bouton</th>
|
||
<th>Comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Reprendre</strong></td>
|
||
<td>Continue séquence 3 à position 1:42 exacte</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Recommencer</strong></td>
|
||
<td>Reset progression, démarre séquence 1 depuis 0:00</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Voir séquences</strong></td>
|
||
<td>Affiche liste complète, user choisit séquence départ</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Expiration progression</strong> :
|
||
- Progression conservée <strong>30 jours</strong>
|
||
- Après 30j : popup "Audio-guide expiré. Recommencez depuis le début ?"
|
||
- Suppression données progression (mais historique "écouté" préservé)</p>
|
||
<p><strong>Justification</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<h4 id="1663-multi-device">16.6.3 Multi-device</h4>
|
||
<p><strong>Scénario</strong> :</p>
|
||
<ol>
|
||
<li>User démarre audio-guide sur iPhone (séquences 1-3)</li>
|
||
<li>Progression sync cloud</li>
|
||
<li>Lendemain : user ouvre app sur iPad</li>
|
||
<li>Popup : "Reprendre Safari du Paugre sur cet appareil ?"</li>
|
||
<li>User clique Reprendre → continue séquence 4</li>
|
||
</ol>
|
||
<p><strong>Conflit de version</strong> :
|
||
- Si modifications simultanées 2 appareils (rare) : <strong>dernière modification gagne</strong>
|
||
- Toast : "Progression mise à jour depuis votre autre appareil"</p>
|
||
<p><strong>Justification</strong> :
|
||
- Confort utilisateur (change d'appareil librement)
|
||
- Use case réel : planning trajet sur tablette, écoute sur smartphone en voiture</p>
|
||
<hr />
|
||
<h2 id="recapitulatif-section-16">Récapitulatif Section 16</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Point</th>
|
||
<th>Décision</th>
|
||
<th>Coût</th>
|
||
<th>Complexité</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>16.1</strong> Types audio-guides</td>
|
||
<td>4 modes (piéton/voiture/vélo/transport) avec détection auto</td>
|
||
<td>0€</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.1.2</strong> Création</td>
|
||
<td>Formulaire séquences + GPS + rayon + wizard guidé</td>
|
||
<td>0€</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.2.1</strong> Piéton - Passages</td>
|
||
<td>Manuel AVEC pub auto-play entre séquences, pause après</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.2.2</strong> Piéton - Navigation</td>
|
||
<td>Liberté totale (skip, retour, saut direct liste)</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.3.1</strong> Voiture - Déclenchement</td>
|
||
<td>GPS auto + boutons manuels actifs (warning sécurité si >10 km/h)</td>
|
||
<td>0€</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.3.2</strong> Voiture - Affichage</td>
|
||
<td>Distance + ETA + direction (flèche) + vitesse (PAS de carte)</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.3.3</strong> Voiture - Rayon</td>
|
||
<td>Configurable créateur (défauts 30m/50m/100m selon mode)</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.4</strong> Vélo & Transport</td>
|
||
<td>Mêmes règles avec tolérances ajustées + warning adapté</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.5</strong> Publicités</td>
|
||
<td>1/5 séquences, auto-play TOUS modes, skippable 5s</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.6.1</strong> Sauvegarde</td>
|
||
<td>Complète (séquence + position + historique) local + cloud</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.6.2</strong> Reprise</td>
|
||
<td>Popup intelligente avec choix (reprendre/recommencer), expiration 30j</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>16.6.3</strong> Multi-device</td>
|
||
<td>Sync cloud PostgreSQL (reprendre sur autre appareil)</td>
|
||
<td>0€</td>
|
||
<td>Faible</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Coût total MVP : 0€</strong> (GPS natif, calcul distance PostGIS)</p>
|
||
<hr />
|
||
<h2 id="points-dattention-pour-gherkin_2">Points d'attention pour Gherkin</h2>
|
||
<ul>
|
||
<li>Tester 4 modes audio-guides (détection vitesse auto)</li>
|
||
<li>Tester création séquences avec points GPS + rayon configurable</li>
|
||
<li>Tester mode piéton : pause après séquence + pub auto-play + pause après pub + clic Suivant</li>
|
||
<li>Tester navigation libre piéton (skip, retour, saut direct liste)</li>
|
||
<li>Tester mode voiture : déclenchement GPS auto rayon 30m</li>
|
||
<li>Tester navigation manuelle voiture : boutons actifs + warning si vitesse >10 km/h</li>
|
||
<li>Tester affichage distance + ETA + direction (flèche 8 directions)</li>
|
||
<li>Tester rayon tolérance "point manqué" (popup 3 actions)</li>
|
||
<li>Tester mode vélo (rayon 50m) et transport (rayon 100m)</li>
|
||
<li>Tester insertion pub 1/5 séquences tous modes avec auto-play</li>
|
||
<li>Tester sauvegarde progression locale + sync cloud</li>
|
||
<li>Tester popup reprise (3 boutons : reprendre/recommencer/voir liste)</li>
|
||
<li>Tester expiration progression 30 jours</li>
|
||
<li>Tester multi-device : démarrer iPhone, continuer iPad</li>
|
||
<li>Tester gestion conflit progression simultanée 2 appareils</li>
|
||
</ul>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="annexe-fonctionnalites-reportees-post-mvp">Annexe : Fonctionnalités reportées Post-MVP</h1>
|
||
<p><strong>Date</strong> : 2026-01-19
|
||
<strong>Statut</strong> : Fonctionnalités validées mais reportées après le MVP</p>
|
||
<hr />
|
||
<h2 id="sommaire">Sommaire</h2>
|
||
<ol>
|
||
<li><a href="##1-classification-politique-et-équilibre-éditorial">Classification politique et équilibre éditorial</a></li>
|
||
<li><a href="##2-système-de-pourboires-créateurs">Système de pourboires créateurs</a></li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="1-classification-politique-et-equilibre-editorial">1. Classification politique et équilibre éditorial</h2>
|
||
<blockquote>
|
||
<p>⚠️ <strong>Reporté post-MVP</strong> pour raisons de coût, complexité et risques juridiques.</p>
|
||
</blockquote>
|
||
<h3 id="contexte-du-report">Contexte du report</h3>
|
||
<p><strong>Raisons</strong> :
|
||
- <strong>Coût modération</strong> : Classification manuelle humaine très coûteuse (~2000€/mois pour 1-2 modérateurs senior full-time)
|
||
- <strong>Risque juridique</strong> : Accusations de biais éditorial, contentieux DSA
|
||
- <strong>Complexité technique</strong> : Dashboard audit, logs 3 ans, alertes déséquilibre
|
||
- <strong>Controverse</strong> : Peut créer polémique dès le lancement
|
||
- <strong>Pas essentiel MVP</strong> : L'application fonctionne sans ce système</p>
|
||
<p><strong>Version MVP</strong> (actuelle) :
|
||
- Tag "Politique" simple (comme "Économie", "Sport")
|
||
- Pas de classification gauche/droite
|
||
- Pas d'équilibrage imposé
|
||
- Option utilisateur "Masquer politique" → 0% contenus politiques</p>
|
||
<hr />
|
||
<h3 id="specifications-completes-future-implementation">Spécifications complètes (future implémentation)</h3>
|
||
<p><strong>Échelle de classification</strong> (5 niveaux) :
|
||
- 🔴 <strong>Extrême gauche</strong> (anticapitalisme radical, révolution)
|
||
- 🟠 <strong>Gauche</strong> (écologie, social, critique capitalisme modérée)
|
||
- ⚪ <strong>Centre/Neutre</strong> (pas de positionnement politique clair)
|
||
- 🔵 <strong>Droite</strong> (sécurité, tradition, économie libérale)
|
||
- 🟣 <strong>Extrême droite</strong> (nationalisme radical, conservatisme extrême)
|
||
- 🟢 <strong>Non politique</strong> (enfants, musique, fiction, culture générale)</p>
|
||
<p><strong>Qui classifie</strong> :
|
||
- ❌ 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</p>
|
||
<p><strong>Affichage</strong> :
|
||
- Badge politique visible : <strong>au choix de l'utilisateur</strong> (paramètre "Afficher orientation politique")
|
||
- Par défaut : badges masqués (UX neutre)</p>
|
||
<p><strong>Règles de diffusion (équilibre imposé)</strong> :</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Préférence utilisateur</th>
|
||
<th>Répartition</th>
|
||
<th>Justification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Équilibré</strong> (défaut)</td>
|
||
<td>35% gauche / 35% droite / 30% centre-neutre</td>
|
||
<td>Neutralité plateforme</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Plutôt gauche</strong></td>
|
||
<td>50% gauche / 20% droite / 30% centre-neutre</td>
|
||
<td>Préférence respectée avec minimum opposition</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Plutôt droite</strong></td>
|
||
<td>50% droite / 20% gauche / 30% centre-neutre</td>
|
||
<td>Préférence respectée avec minimum opposition</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Masquer politique</strong></td>
|
||
<td>0% gauche / 0% droite / 100% centre-neutre + non politique</td>
|
||
<td>Option apolitique</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Audit et conformité DSA</strong> :
|
||
- Rapport hebdomadaire automatique : % gauche/droite/centre diffusé par utilisateur
|
||
- Alerte si déséquilibre global plateforme (>55% d'un bord)
|
||
- Logs conservés <strong>3 ans</strong> (exigence Digital Services Act EU)
|
||
- Dashboard admin : visualisation répartition temps réel</p>
|
||
<p><strong>Sanctions mauvaise classification</strong> :
|
||
- Classification volontairement incorrecte = Strike 1
|
||
- Récidive = Strike 2 (suspension 7j)
|
||
- Détection via signalements utilisateurs + audit modération</p>
|
||
<p><strong>Justification</strong> :
|
||
- <strong>Conformité juridique DSA</strong> (obligation neutralité plateforme EU)
|
||
- Protection contre accusations de biais éditorial
|
||
- Transparence auditable
|
||
- Coût : temps modération humaine (incompressible)</p>
|
||
<hr />
|
||
<h3 id="conditions-de-reintegration">Conditions de réintégration</h3>
|
||
<p><strong>Prérequis</strong> :
|
||
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</p>
|
||
<p><strong>Chronologie estimée</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<h2 id="2-systeme-de-pourboires-createurs">2. Système de pourboires créateurs</h2>
|
||
<blockquote>
|
||
<p>⚠️ <strong>Reporté post-MVP</strong> - Fonctionnalité crypto (Lightning Network) prévue ultérieurement.</p>
|
||
</blockquote>
|
||
<h3 id="contexte-du-report_1">Contexte du report</h3>
|
||
<p><strong>Raisons</strong> :
|
||
- <strong>Complexité technique</strong> : Intégration Lightning Network, gestion wallets crypto
|
||
- <strong>Réglementation</strong> : Incertitude juridique crypto en EU (MiCA 2025)
|
||
- <strong>Focus MVP</strong> : Priorité sur monétisation via abonnements Premium et publicités
|
||
- <strong>Adoption utilisateurs</strong> : Nécessite éducation et adoption crypto préalables</p>
|
||
<p><strong>Version MVP</strong> (actuelle) :
|
||
- Monétisation créateurs via :
|
||
- Partage revenus publicités (3€ CPM)
|
||
- 70% revenus abonnements Premium</p>
|
||
<hr />
|
||
<h3 id="specifications-completes-future-implementation_1">Spécifications complètes (future implémentation)</h3>
|
||
<p><strong>Système prévu</strong> : Micro-dons via Lightning Network (Bitcoin Layer 2)</p>
|
||
<p><strong>Fonctionnement</strong> :
|
||
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)</p>
|
||
<p><strong>Avantages Lightning Network</strong> :
|
||
- ✅ 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)</p>
|
||
<p><strong>Contraintes</strong> :
|
||
- ❌ 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</p>
|
||
<p><strong>Alternatives étudiées</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<h3 id="conditions-de-reintegration_1">Conditions de réintégration</h3>
|
||
<p><strong>Prérequis</strong> :
|
||
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</p>
|
||
<p><strong>Chronologie estimée</strong> :
|
||
- 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</p>
|
||
<hr />
|
||
<h2 id="autres-fonctionnalites-candidates-post-mvp">Autres fonctionnalités candidates Post-MVP</h2>
|
||
<p>Liste non exhaustive de fonctionnalités évoquées mais non encore spécifiées :</p>
|
||
<ul>
|
||
<li><strong>Mode offline avancé</strong> : Téléchargement automatique zones fréquentes</li>
|
||
<li><strong>Playlists collaboratives</strong> : Co-création de playlists géolocalisées</li>
|
||
<li><strong>API publique créateurs</strong> : Intégration RSS, podcasts existants</li>
|
||
<li><strong>Gamification</strong> : Badges, défis géolocalisés, leaderboards</li>
|
||
<li><strong>Mode nuit</strong> : Interface sombre automatique</li>
|
||
<li><strong>Statistiques avancées créateurs</strong> : Démographie, retention, heatmaps GPS</li>
|
||
</ul>
|
||
<p>Ces fonctionnalités seront spécifiées et priorisées selon les retours utilisateurs MVP.</p>
|
||
<hr />
|
||
<h2 id="suivi-et-validation">Suivi et validation</h2>
|
||
<p><strong>Responsable</strong> : Product Owner
|
||
<strong>Révision</strong> : Trimestrielle
|
||
<strong>Critères de priorisation</strong> :
|
||
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</p>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="audio-guides-multi-sequences-pour-pietons">Audio-guides multi-séquences pour piétons</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur à pied</em>
|
||
<em>Je veux profiter d'audio-guides structurés lors de mes visites</em>
|
||
<em>Afin de découvrir des lieux de manière autonome et à mon rythme</em></p>
|
||
</blockquote>
|
||
<p><strong>29 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant qu'auditeur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis en mode piéton (vitesse <5 km/h)</p>
|
||
</blockquote>
|
||
<h2 id="1-detection-daudio-guide-a-proximite">1. Détection d'audio-guide à proximité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me trouve à 80 mètres du Musée du Louvre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 3 audio-guides sont disponibles pour ce lieu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte ma position</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push:</p>
|
||
<hr />
|
||
<h2 id="2-rayon-de-detection-de-100m">2. Rayon de détection de 100m</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide est centré aux coordonnées GPS du Louvre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis à exactement 100m du centre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification est déclenchée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> quand je suis à 101m, aucune notification n'est envoyée</p>
|
||
<hr />
|
||
<h2 id="3-page-de-selection-des-audio-guides">3. Page de sélection des audio-guides</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai tapé sur la notification audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page de sélection s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une liste de guides disponibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="4-selection-dun-audio-guide">4. Sélection d'un audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis sur la page de sélection</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape sur "Visite complète (45 min)"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'interface de lecture d'audio-guide s'ouvre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la séquence 1 commence automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois la liste complète des 12 séquences</p>
|
||
<hr />
|
||
<h2 id="5-interface-de-lecture-audio-guide">5. Interface de lecture audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai sélectionné un audio-guide de 12 séquences</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="6-navigation-vers-sequence-suivante">6. Navigation vers séquence suivante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la séquence 2</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 3 commence immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le titre de la séquence s'affiche: "Vénus de Milo"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la barre de progression se réinitialise</p>
|
||
<hr />
|
||
<h2 id="7-navigation-vers-sequence-precedente">7. Navigation vers séquence précédente</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la séquence 5</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 recommence depuis le début
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux réécouter cette séquence</p>
|
||
<hr />
|
||
<h2 id="8-saut-direct-a-une-sequence-specifique">8. Saut direct à une séquence spécifique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la séquence 2
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la liste des séquences est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape sur "7. Peintures Renaissance"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 7 démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je passe directement de la séquence 2 à la 7</p>
|
||
<hr />
|
||
<h2 id="9-commande-vocale-suivant">9. Commande vocale "Suivant"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la séquence 3</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Suivant" via la commande vocale</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la commande vocale fonctionne même si l'écran est verrouillé</p>
|
||
<hr />
|
||
<h2 id="10-commande-vocale-precedent">10. Commande vocale "Précédent"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la séquence 6</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Précédent" via la commande vocale</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 5 démarre depuis le début</p>
|
||
<hr />
|
||
<h2 id="11-pause-et-reprise-a-la-position-exacte">11. Pause et reprise à la position exacte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la séquence 4 à la position 2:30</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je mets en pause
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'attends 5 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je reprends la lecture</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence reprend exactement à 2:30
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée n'est perdue</p>
|
||
<hr />
|
||
<h2 id="12-guidage-vocal-automatique-entre-sequences">12. Guidage vocal automatique entre séquences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 2 se termine</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la transition vers la séquence 3 se produit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> j'entends un message vocal:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la séquence 3 ne démarre pas automatiquement (navigation manuelle)</p>
|
||
<hr />
|
||
<h2 id="13-avertissement-si-eloignement-du-point-dinteret">13. Avertissement si éloignement du point d'intérêt</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis dans le guide du Louvre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je devrais être devant la Vénus de Milo (séquence 3)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'éloigne de plus de 50m de ce point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> j'entends un message vocal:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Voir le plan" apparaît dans l'interface</p>
|
||
<hr />
|
||
<h2 id="14-sauvegarde-automatique-de-la-progression">14. Sauvegarde automatique de la progression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la séquence 5 à la position 1:45</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je ferme l'application brutalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je la rouvre 10 minutes plus tard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une popup "Reprendre la visite du Musée du Louvre ?"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je choisis "Reprendre", je retourne à la séquence 5 à 1:45</p>
|
||
<hr />
|
||
<h2 id="15-option-de-recommencer-depuis-le-debut">15. Option de recommencer depuis le début</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai une progression sauvegardée à la séquence 7</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je rouvre le guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois 2 options:</p>
|
||
<pre><code>| option | action |
|
||
|---|---|
|
||
| Reprendre à la séquence 7 | Reprend à la position exacte |
|
||
| Recommencer depuis le début | Retourne à la séquence 1 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="16-expiration-de-la-sauvegarde-apres-30-jours">16. Expiration de la sauvegarde après 30 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai une progression sauvegardée depuis 30 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de reprendre le guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la sauvegarde est considérée comme expirée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je recommence depuis la séquence 1
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Votre précédente visite date de plus de 30 jours. Recommençons depuis le début."</p>
|
||
<hr />
|
||
<h2 id="17-synchronisation-multi-device-de-la-progression">17. Synchronisation multi-device de la progression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un guide sur mon iPhone à la séquence 4</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je ferme l'app et ouvre sur mon iPad</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la progression synchronisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux reprendre à la séquence 4 sur l'iPad</p>
|
||
<hr />
|
||
<h2 id="18-marquage-termine-apres-toutes-les-sequences">18. Marquage "Terminé" après toutes les séquences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la dernière séquence (12/12)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> cette séquence se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le guide est marqué "✅ Terminé" dans mon historique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois un message de félicitation:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur gagne les statistiques d'écoute complète</p>
|
||
<hr />
|
||
<h2 id="19-creation-daudio-guide-par-un-createur">19. Création d'audio-guide par un créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée un nouvel audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> la durée totale est calculée automatiquement</p>
|
||
<hr />
|
||
<h2 id="20-structure-json-de-stockage-audio-guide">20. Structure JSON de stockage audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur publie un audio-guide du Louvre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les métadonnées sont stockées en base</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le format JSON contient:</p>
|
||
<hr />
|
||
<h2 id="21-limitation-du-nombre-de-sequences">21. Limitation du nombre de séquences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'ajouter plus de 50 séquences</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Maximum 50 séquences par audio-guide"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois structurer mon contenu différemment ou créer plusieurs guides</p>
|
||
<hr />
|
||
<h2 id="22-quitter-le-guide-et-sauvegarder">22. Quitter le guide et sauvegarder</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute la séquence 6</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape sur le bouton "×" (fermer)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une confirmation:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je confirme, la progression est enregistrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je retourne à l'écran principal</p>
|
||
<hr />
|
||
<h2 id="23-statistiques-createur-pour-audio-guides">23. Statistiques créateur pour audio-guides</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur d'un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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) |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="24-audio-guide-multilingue-post-mvp">24. Audio-guide multilingue (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur peut publier plusieurs versions linguistiques</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un touriste anglophone visite le Louvre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit les guides disponibles en anglais
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> peut choisir parmi les guides traduits
|
||
<span style="color: #F44336"><strong>Mais</strong></span> cette fonctionnalité n'est pas disponible en MVP</p>
|
||
<hr />
|
||
<h2 id="25-publicite-entre-sequences-daudio-guide">25. Publicité entre séquences d'audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je passe de la séquence 5 à la séquence 6</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une publicité peut être insérée (1 pub toutes les 5 séquences)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publicité est skippable après 5 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs Premium ne voient pas de publicité</p>
|
||
<hr />
|
||
<h2 id="26-audio-guide-en-mode-offline">26. Audio-guide en mode offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé un audio-guide complet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je visite le lieu sans connexion internet</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les séquences sont disponibles hors ligne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la navigation fonctionne normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule la sauvegarde cloud est différée jusqu'à reconnexion</p>
|
||
<hr />
|
||
<h2 id="27-notation-dun-audio-guide-apres-ecoute">27. Notation d'un audio-guide après écoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai terminé un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je ferme l'interface</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une popup "Notez cette visite"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux donner une note de 1 à 5 étoiles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette note contribue à la note globale visible par les autres utilisateurs</p>
|
||
<hr />
|
||
<h2 id="28-filtrage-par-langue-dans-la-page-de-selection">28. Filtrage par langue dans la page de sélection</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que plusieurs audio-guides sont disponibles en différentes langues</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à la page de sélection</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux filtrer par langue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> par défaut, les guides dans ma langue système sont affichés en premier</p>
|
||
<hr />
|
||
<h2 id="29-reutilisation-de-linfrastructure-existante">29. Réutilisation de l'infrastructure existante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide est techniquement un contenu structuré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il réutilise:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> aucune infrastructure dédiée n'est nécessaire</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="impact-des-abonnements-sur-lalgorithme">Impact des abonnements sur l'algorithme</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur</em>
|
||
<em>Je veux que les contenus de mes créateurs suivis soient favorisés</em>
|
||
<em>Afin de ne pas rater leurs publications tout en découvrant de nouveaux contenus</em></p>
|
||
</blockquote>
|
||
<p><strong>16 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant qu'auditeur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis abonné au créateur "JeanDupont"</p>
|
||
</blockquote>
|
||
<h2 id="1-boost-de-30-applique-au-score-final">1. Boost de +30% appliqué au score final</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu du créateur "JeanDupont" avec:</p>
|
||
<pre><code>| score_geo | 0.5 |
|
||
|---|---|
|
||
| score_interet | 0.6 |
|
||
| score_engage | 0.5 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le score final est calculé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score de base est 0.53
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le boost abonnement de +30% est appliqué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le score final avec boost est 0.69</p>
|
||
<hr />
|
||
<h2 id="2-contenu-non-suivi-peut-battre-contenu-suivi">2. Contenu non-suivi peut battre contenu suivi</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à Paris
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 2 contenus sont disponibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme sélectionne le prochain contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le Contenu A est proposé en premier</p>
|
||
<hr />
|
||
<h2 id="3-contenu-suivi-remporte-grace-au-boost">3. Contenu suivi remporte grâce au boost</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à Paris
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 2 contenus sont disponibles:</p>
|
||
<pre><code>| contenu | createur_suivi | score_final_base | score_avec_boost |
|
||
|---|---|---|---|
|
||
| Contenu A | Non | 0.70 | 0.70 |
|
||
| Contenu B | Oui | 0.60 | 0.78 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme sélectionne le prochain contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le Contenu B est proposé en premier</p>
|
||
<hr />
|
||
<h2 id="4-contenu-suivi-avec-faible-engagement-ne-domine-pas">4. Contenu suivi avec faible engagement ne domine pas</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "CreateurMoyen"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il publie un contenu avec très faible engagement (score 0.30)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu viral d'un créateur non-suivi a un score de 0.85</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme sélectionne le prochain contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu viral est proposé en premier (0.85)</p>
|
||
<hr />
|
||
<h2 id="5-pas-de-file-dediee-aux-abonnements">5. Pas de file dédiée aux abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 50 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère ma file d'attente de 5 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus suivis et non-suivis sont mélangés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous entrent en compétition selon leurs scores (avec boost si abonnement)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de section séparée "Contenus de vos abonnements"</p>
|
||
<hr />
|
||
<h2 id="6-verification-du-calcul-du-boost">6. Vérification du calcul du boost</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu d'un créateur suivi
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le score final de base est calculé à 0.65</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le boost abonnement est appliqué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le multiplicateur utilisé est exactement 1.3
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le score final avec boost est 0.845 (0.65 × 1.3)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le résultat est arrondi à 2 décimales: 0.85</p>
|
||
<hr />
|
||
<h2 id="7-boost-applique-a-tous-les-contenus-du-createur-suivi">7. Boost appliqué à tous les contenus du créateur suivi</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "JeanDupont"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il a publié 10 contenus différents</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme évalue chacun de ces contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le boost de +30% est appliqué à tous les 10 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque contenu bénéficie du même multiplicateur 1.3</p>
|
||
<hr />
|
||
<h2 id="8-plusieurs-createurs-suivis-en-competition">8. Plusieurs créateurs suivis en compétition</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à "Créateur A" et "Créateur B"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que les 2 ont des contenus disponibles dans ma zone:</p>
|
||
<pre><code>| createur | score_base | score_avec_boost |
|
||
|---|---|---|
|
||
| Créateur A | 0.70 | 0.91 |
|
||
| Créateur B | 0.65 | 0.85 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme sélectionne le prochain contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu du Créateur A est proposé en premier (0.91 > 0.85)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 2 bénéficient du boost, mais le meilleur score gagne</p>
|
||
<hr />
|
||
<h2 id="9-contenu-national-dun-createur-suivi">9. Contenu national d'un créateur suivi</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à "MediaNational"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il publie un contenu de type "National" (score_geo 0.2)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le score est calculé avec:</p>
|
||
<pre><code>| score_geo | score_interet | score_engage |
|
||
|---|---|---|
|
||
| 0.2 | 0.7 | 0.6 |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score de base est environ 0.50
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> avec le boost abonnement, le score devient 0.65
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu peut être proposé malgré son score géo faible</p>
|
||
<hr />
|
||
<h2 id="10-transparence-du-boost-dans-les-parametres">10. Transparence du boost dans les paramètres</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède aux paramètres de l'algorithme de recommandation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'information: "Les contenus de vos créateurs suivis bénéficient d'un boost de +30%"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je comprends que ce n'est pas une priorité absolue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la découverte de nouveaux contenus reste possible</p>
|
||
<hr />
|
||
<h2 id="11-boost-desactive-si-desabonnement">11. Boost désactivé si désabonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "JeanDupont"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un de ses contenus bénéficiait du boost +30%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me désabonne de "JeanDupont"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ses contenus n'ont plus le boost
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> leur score revient au score de base sans multiplicateur</p>
|
||
<hr />
|
||
<h2 id="12-contenu-dun-createur-nouvellement-suivi">12. Contenu d'un créateur nouvellement suivi</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de m'abonner à "NouveauCreateur"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il a publié un contenu il y a 2 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme recalcule les scores</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le boost de +30% est immédiatement appliqué à ce contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut apparaître dans ma prochaine file d'attente</p>
|
||
<hr />
|
||
<h2 id="13-impact-sur-le-taux-de-contenu-suivi-dans-le-feed">13. Impact sur le taux de contenu suivi dans le feed</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 30 créateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute 100 contenus sur une semaine</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'analyse la répartition</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> environ 40-50% des contenus proviennent de créateurs suivis
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 50-60% proviennent de créateurs non-suivis (découverte)</p>
|
||
<hr />
|
||
<h2 id="14-contenu-suivi-hors-zone-geographique">14. Contenu suivi hors zone géographique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à Paris
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis abonné à un créateur de Marseille
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il publie un contenu ancré à Marseille (hors de portée)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme évalue ce contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score géo est quasi nul (0.05)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> même avec boost +30%, le score reste très faible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu n'est probablement pas proposé</p>
|
||
<hr />
|
||
<h2 id="15-performance-de-calcul-du-boost">15. Performance de calcul du boost</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 100 créateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'algorithme évalue 1000 contenus potentiels</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le calcul des scores avec boost est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le temps de calcul reste inférieur à 50ms
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la requête SQL utilise un JOIN sur la table abonnements</p>
|
||
<hr />
|
||
<h2 id="16-boost-combine-avec-dautres-facteurs">16. Boost combiné avec d'autres facteurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu d'un créateur suivi
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu bénéficie aussi de:</p>
|
||
<pre><code>| facteur | impact |
|
||
|---|---|
|
||
| Score d'engagement élevé | +20% |
|
||
| Contenu récent (<24h) | +10% |
|
||
| Boost abonnement | +30% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le score final est calculé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le boost abonnement s'applique au score final (après tous les autres calculs)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les boosts ne s'additionnent pas, le boost abonnement est un multiplicateur final</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="limites-dabonnements-et-desabonnement">Limites d'abonnements et désabonnement</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur</em>
|
||
<em>Je veux gérer mes abonnements de manière équilibrée</em>
|
||
<em>Afin de suivre mes créateurs préférés sans être submergé</em></p>
|
||
</blockquote>
|
||
<p><strong>27 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant qu'auditeur</p>
|
||
</blockquote>
|
||
<h2 id="1-limite-maximale-de-200-abonnements">1. Limite maximale de 200 abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 199 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de m'abonner à un 200ème créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'abonnement réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis maintenant abonné à 200 créateurs</p>
|
||
<hr />
|
||
<h2 id="2-impossible-de-depasser-200-abonnements">2. Impossible de dépasser 200 abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis déjà abonné à 200 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de m'abonner à un nouveau créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message:</p>
|
||
<hr />
|
||
<h2 id="3-suggestion-de-desabonnement-de-createurs-inactifs">3. Suggestion de désabonnement de créateurs inactifs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 200 créateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'essaie de m'abonner à un nouveau créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vois le message de limite atteinte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois aussi une suggestion:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Désabonner" est proposé pour ce créateur</p>
|
||
<hr />
|
||
<h2 id="4-liste-triable-des-abonnements">4. Liste triable des abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 150 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à ma liste d'abonnements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux trier par:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="5-abonnement-initial-augmente-les-jauges-de-5">5. Abonnement initial augmente les jauges de +5%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes jauges d'intérêt sont:</p>
|
||
<pre><code>| catégorie | valeur initiale |
|
||
|---|---|
|
||
| Automobile | 60% |
|
||
| Voyage | 55% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> qu'un créateur tague ses contenus "Automobile" et "Voyage"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'abonne à ce créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes jauges évoluent:</p>
|
||
<pre><code>| catégorie | nouvelle valeur |
|
||
|---|---|
|
||
| Automobile | 65% (+5%) |
|
||
| Voyage | 60% (+5%) |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="6-abonnement-avec-createur-ayant-3-tags">6. Abonnement avec créateur ayant 3 tags</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur tague ses contenus:</p>
|
||
<pre><code>| tags |
|
||
|---|
|
||
| Automobile, Voyage, Technologie |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges sont toutes à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'abonne à ce créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 3 jauges augmentent de +5%:</p>
|
||
<pre><code>| catégorie | nouvelle valeur |
|
||
|---|---|
|
||
| Automobile | 55% |
|
||
| Voyage | 55% |
|
||
| Technologie | 55% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="7-desabonnement-diminue-les-jauges-de-5">7. Désabonnement diminue les jauges de -5%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à un créateur avec tags "Politique" et "Économie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges sont:</p>
|
||
<pre><code>| catégorie | valeur actuelle |
|
||
|---|---|
|
||
| Politique | 70% |
|
||
| Économie | 65% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me désabonne de ce créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes jauges évoluent:</p>
|
||
<pre><code>| catégorie | nouvelle valeur |
|
||
|---|---|
|
||
| Politique | 65% (-5%) |
|
||
| Économie | 60% (-5%) |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="8-desabonnement-sans-confirmation">8. Désabonnement sans confirmation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte le profil d'un créateur suivi</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Se désabonner"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le désabonnement est immédiat
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune popup de confirmation n'apparaît</p>
|
||
<hr />
|
||
<h2 id="9-reabonnement-possible-immediatement">9. Réabonnement possible immédiatement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de me désabonner d'un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte à nouveau son profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "S'abonner" est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux me réabonner immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes jauges augmentent à nouveau de +5%</p>
|
||
<hr />
|
||
<h2 id="10-effet-symetrique-abonnementdesabonnement">10. Effet symétrique abonnement/désabonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a les tags "Musique" et "Culture"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge Musique est à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'abonne puis me désabonne immédiatement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge revient exactement à 50%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de perte ou gain net</p>
|
||
<hr />
|
||
<h2 id="11-abonnement-ne-depasse-pas-100-de-jauge">11. Abonnement ne dépasse pas 100% de jauge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma jauge Automobile est à 97%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un créateur tague ses contenus "Automobile"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'abonne à ce créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge Automobile passe à 100% (limite max)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'augmentation effective est de +3% seulement</p>
|
||
<hr />
|
||
<h2 id="12-desabonnement-ne-descend-pas-sous-0">12. Désabonnement ne descend pas sous 0%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma jauge Politique est à 3%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis abonné à un créateur avec tag "Politique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me désabonne de ce créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge Politique passe à 0% (limite min)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la diminution effective est de -3% seulement</p>
|
||
<hr />
|
||
<h2 id="13-createur-ne-voit-pas-qui-est-abonne-privacy">13. Créateur ne voit pas qui est abonné (privacy)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "JeanDupont"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "JeanDupont" consulte ses statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit le nombre total d'abonnés (ex: "1,247 abonnés")
|
||
<span style="color: #F44336"><strong>Mais</strong></span> il ne voit pas la liste des utilisateurs abonnés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon identité reste privée</p>
|
||
<hr />
|
||
<h2 id="14-createur-voit-uniquement-le-nombre-total-dabonnes">14. Créateur voit uniquement le nombre total d'abonnés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai 523 abonnés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "523 abonnés"
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je ne peux pas:</p>
|
||
<pre><code>| action interdite |
|
||
|---|
|
||
| Voir la liste des abonnés |
|
||
| Contacter mes abonnés individuellement |
|
||
| Voir leurs profils |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="15-pas-dabonnement-mutuel-visible">15. Pas d'abonnement mutuel visible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "Alice"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'"Alice" est abonnée à mon compte créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le profil d'"Alice"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne vois pas d'indication qu'elle est abonnée à moi
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de badge "Abonné mutuellement"</p>
|
||
<hr />
|
||
<h2 id="16-performance-avec-200-abonnements">16. Performance avec 200 abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 200 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule ma recommandation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la requête SQL utilise un JOIN sur la table abonnements
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la table est indexée sur user_id et creator_id
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le temps de calcul reste inférieur à 50ms</p>
|
||
<hr />
|
||
<h2 id="17-impact-sur-la-recommandation-avec-beaucoup-dabonnements">17. Impact sur la recommandation avec beaucoup d'abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 150 créateurs très actifs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'ils publient collectivement 100 contenus par jour</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère ma file de 5 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> environ 60-70% des contenus proviennent de créateurs suivis (grâce au boost +30%)
|
||
<span style="color: #F44336"><strong>Mais</strong></span> 30-40% proviennent de nouveaux créateurs (découverte)</p>
|
||
<hr />
|
||
<h2 id="18-notification-de-desabonnement-au-createur-non-implemente">18. Notification de désabonnement au créateur (non implémenté)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me désabonne d'un créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le créateur ne reçoit aucune notification
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il ne peut pas savoir qui s'est désabonné</p>
|
||
<hr />
|
||
<h2 id="19-statistiques-dabonnements-pour-lutilisateur">19. Statistiques d'abonnements pour l'utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 87 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mes statistiques d'abonnements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="20-recherche-dans-la-liste-dabonnements">20. Recherche dans la liste d'abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 120 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à ma liste d'abonnements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux chercher par nom de créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les résultats sont filtrés en temps réel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je trouve rapidement un créateur spécifique</p>
|
||
<hr />
|
||
<h2 id="21-export-de-la-liste-dabonnements-rgpd">21. Export de la liste d'abonnements (RGPD)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je demande l'export de mes données</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la liste de mes abonnements est incluse:</p>
|
||
<hr />
|
||
<h2 id="22-suppression-compte-utilisateur-et-impact-sur-abonnements">22. Suppression compte utilisateur et impact sur abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 50 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je supprime définitivement mon compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous mes abonnements sont supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur d'abonnés de chaque créateur est décrémenté de -1
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les jauges n'existent plus (données supprimées)</p>
|
||
<hr />
|
||
<h2 id="23-suppression-compte-createur-et-impact-sur-abonnes">23. Suppression compte créateur et impact sur abonnés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "Bob"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "Bob" supprime son compte créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis automatiquement désabonné
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes jauges diminuent de -5% pour les tags de "Bob"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne vois plus "Bob" dans ma liste d'abonnements</p>
|
||
<hr />
|
||
<h2 id="24-limite-200-justifiee-par-usage-realiste">24. Limite 200 justifiée par usage réaliste</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la moyenne d'abonnements sur YouTube est de ~50-100 chaînes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que Twitter limite à 5000 follows (mais moyenne ~150)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe la limite à 200</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cela couvre largement 99% des utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> évite les abus (comptes spam suivant tout le monde)</p>
|
||
<hr />
|
||
<h2 id="25-table-postgresql-optimisee-pour-abonnements">25. Table PostgreSQL optimisée pour abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> la structure de table subscriptions:</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les requêtes d'abonnements sont O(1) avec index
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le count d'abonnés par créateur est rapide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la vérification "est abonné ?" est instantanée</p>
|
||
<hr />
|
||
<h2 id="26-detection-dabonnements-abusifs">26. Détection d'abonnements abusifs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur s'abonne à 200 créateurs en moins de 5 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette activité suspecte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un rate limiting est appliqué (max 10 abonnements/minute)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur voit "Trop d'actions rapides. Veuillez réessayer dans 1 minute"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela prévient les bots de spam</p>
|
||
<hr />
|
||
<h2 id="27-badge-createur-verifie-visible-dans-abonnements">27. Badge créateur vérifié visible dans abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 3 créateurs dont 1 vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte ma liste d'abonnements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le créateur vérifié a un badge ✓ bleu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les créateurs non vérifiés n'ont pas de badge</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="notifications-contextuelles-selon-le-mode-de-deplacement">Notifications contextuelles selon le mode de déplacement</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur</em>
|
||
<em>Je veux recevoir des notifications adaptées à mon contexte</em>
|
||
<em>Afin d'être informé sans être distrait en conduisant</em></p>
|
||
</blockquote>
|
||
<p><strong>28 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant qu'auditeur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai activé les notifications</p>
|
||
</blockquote>
|
||
<h2 id="1-detection-automatique-du-contexte-en-voiture">1. Détection automatique du contexte en voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma vitesse GPS est de 50 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte mon contexte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis identifié comme "En voiture"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les notifications push sont désactivées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les notifications in-app sont actives</p>
|
||
<hr />
|
||
<h2 id="2-detection-automatique-du-contexte-a-pied">2. Détection automatique du contexte à pied</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma vitesse GPS est de 3 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte mon contexte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis identifié comme "À pied"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les notifications push sont activées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'interface tactile et vocale sont disponibles</p>
|
||
<hr />
|
||
<h2 id="3-zone-de-transition-5-10-kmh">3. Zone de transition 5-10 km/h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma vitesse GPS varie entre 5 et 10 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte mon contexte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un algorithme de lissage est appliqué sur 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le mode est déterminé selon la vitesse moyenne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les changements de mode ne sont pas trop fréquents</p>
|
||
<hr />
|
||
<h2 id="4-nouveau-contenu-createur-suivi-mode-voiture">4. Nouveau contenu créateur suivi - Mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en voiture (vitesse >10 km/h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis abonné au créateur "JeanDupont"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "JeanDupont" publie un nouveau contenu dans ma zone</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de notification push
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je vois un badge compteur in-app
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu apparaît dans ma file avec boost +30%</p>
|
||
<hr />
|
||
<h2 id="5-nouveau-contenu-createur-suivi-mode-pieton">5. Nouveau contenu créateur suivi - Mode piéton</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à pied (vitesse <5 km/h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis abonné au créateur "JeanDupont"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis situé en Île-de-France</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "JeanDupont" publie un contenu géolocalisé en Île-de-France</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push:</p>
|
||
<hr />
|
||
<h2 id="6-live-createur-suivi-mode-voiture">6. Live créateur suivi - Mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en voiture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis abonné au créateur "RadioLive"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "RadioLive" démarre un live dans ma zone</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de notification push
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je vois un badge compteur in-app
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le live peut apparaître dans ma recommandation automatiquement</p>
|
||
<hr />
|
||
<h2 id="7-live-createur-suivi-mode-pieton">7. Live créateur suivi - Mode piéton</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à pied
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis abonné au créateur "RadioLive"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis situé dans la zone du live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "RadioLive" démarre un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push:</p>
|
||
<hr />
|
||
<h2 id="8-audio-guide-disponible-a-proximite-mode-pieton">8. Audio-guide disponible à proximité - Mode piéton</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à pied</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je passe à moins de 100m d'un lieu avec audio-guides</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push:</p>
|
||
<hr />
|
||
<h2 id="9-audio-guide-disponible-a-proximite-mode-voiture">9. Audio-guide disponible à proximité - Mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en voiture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je passe à moins de 100m d'un lieu avec audio-guides</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification audio (bip)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une annonce vocale: "Audio-guide disponible"
|
||
<span style="color: #F44336"><strong>Mais</strong></span> pas de notification push (sécurité)</p>
|
||
<hr />
|
||
<h2 id="10-filtrage-geographique-des-notifications">10. Filtrage géographique des notifications</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "CreateurMarseille"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis situé à Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "CreateurMarseille" publie un contenu ancré à Marseille</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de notification
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela évite la frustration de contenus non écoutables</p>
|
||
<hr />
|
||
<h2 id="11-contenu-national-notifie-tous-les-abonnes">11. Contenu national notifie tous les abonnés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "MediaNational"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis situé n'importe où en France</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "MediaNational" publie un contenu de type "National"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification (si mode piéton)</p>
|
||
<hr />
|
||
<h2 id="12-limite-de-10-notifications-push-par-jour">12. Limite de 10 notifications push par jour</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 50 créateurs actifs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai déjà reçu 10 notifications push aujourd'hui</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un 11ème contenu est publié</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de notification push individuelle
|
||
<span style="color: #F44336"><strong>Mais</strong></span> une notification groupée: "🎧 3 nouveaux contenus de créateurs suivis"</p>
|
||
<hr />
|
||
<h2 id="13-parametrage-de-la-limite-quotidienne">13. Paramétrage de la limite quotidienne</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la limite par défaut est de 10 notifications/jour</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède aux paramètres de notifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux modifier la limite entre 5 et 20
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je choisis 15, je recevrai jusqu'à 15 notifications/jour</p>
|
||
<hr />
|
||
<h2 id="14-mode-silencieux-nocturne-par-defaut">14. Mode silencieux nocturne par défaut</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode silencieux est activé de 22h à 8h par défaut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il est 23h30</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un créateur suivi publie un contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de notification push
|
||
<span style="color: #F44336"><strong>Mais</strong></span> les notifications sont empilées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je les vois le lendemain matin à 8h01</p>
|
||
<hr />
|
||
<h2 id="15-exception-du-mode-silencieux-pour-les-lives">15. Exception du mode silencieux pour les lives</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode silencieux est activé (22h-8h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il est 23h00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai activé "Notifications importantes uniquement" (lives uniquement)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un créateur suivi démarre un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois quand même la notification push du live</p>
|
||
<hr />
|
||
<h2 id="16-desactivation-complete-des-notifications">16. Désactivation complète des notifications</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède aux paramètres de notifications</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive toutes les notifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois plus aucune notification push
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les badges in-app sont également désactivés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule la recommandation algorithmique reste active</p>
|
||
<hr />
|
||
<h2 id="17-notification-nouveaux-contenus-activee-par-defaut">17. Notification "Nouveaux contenus" activée par défaut</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée un nouveau compte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je m'abonne à mon premier créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les préférences de notifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> "Nouveaux contenus" est activé par défaut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> "Lives" est activé par défaut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> "Audio-guides proximité" est activé par défaut</p>
|
||
<hr />
|
||
<h2 id="18-desactivation-selective-par-type-de-notification">18. Désactivation sélective par type de notification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai activé toutes les notifications</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive uniquement "Nouveaux contenus"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois plus de notifications pour nouveaux contenus
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je reçois toujours les notifications de lives
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les notifications d'audio-guides restent actives</p>
|
||
<hr />
|
||
<h2 id="19-notification-groupee-apres-limite-depassee">19. Notification groupée après limite dépassée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu 10 notifications push aujourd'hui
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 5 nouveaux contenus sont publiés dans l'heure suivante</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la 11ème notification devrait être envoyée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 5 contenus sont regroupés en une seule notification:</p>
|
||
<hr />
|
||
<h2 id="20-detail-de-la-notification-groupee">20. Détail de la notification groupée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu une notification groupée "3 nouveaux contenus"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape sur la notification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app s'ouvre sur une liste des 3 contenus:</p>
|
||
<pre><code>| créateur | titre |
|
||
|---|---|
|
||
| JeanDupont | "Actualité du jour" |
|
||
| MarieDurand | "Podcast économie" |
|
||
| PaulMartin | "Anecdote historique" |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir lequel écouter en premier</p>
|
||
<hr />
|
||
<h2 id="21-personnalisation-des-plages-horaires-du-mode-silencieux">21. Personnalisation des plages horaires du mode silencieux</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode silencieux est 22h-8h par défaut</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède aux paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux modifier les heures: par exemple 23h-7h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le mode silencieux s'applique dans la nouvelle plage horaire</p>
|
||
<hr />
|
||
<h2 id="22-format-notification-nouveau-contenu-complet">22. Format notification nouveau contenu complet</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à pied
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un créateur suivi publie un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois la notification push</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle contient:</p>
|
||
<pre><code>| élément | exemple |
|
||
|---|---|
|
||
| Emoji | 🎧 |
|
||
| Créateur | JeanDupont |
|
||
| Action | a publié |
|
||
| Titre | "Les secrets du Louvre" |
|
||
| CTA | Tap pour écouter |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="23-format-notification-live-complet">23. Format notification live complet</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à pied
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un créateur suivi démarre un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois la notification push</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle contient:</p>
|
||
<pre><code>| élément | exemple |
|
||
|---|---|
|
||
| Emoji | 🔴 |
|
||
| Créateur | RadioLive |
|
||
| Action | est en direct |
|
||
| Titre | "Débat politique ce soir" |
|
||
| CTA | Tap pour rejoindre |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="24-notification-disparait-si-contenu-supprime">24. Notification disparaît si contenu supprimé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu une notification pour un contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas encore tapé dessus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur supprime le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification est automatiquement retirée de mon centre de notifications
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je tape dessus par erreur, je vois "Contenu non disponible"</p>
|
||
<hr />
|
||
<h2 id="25-badge-compteur-in-app-en-mode-voiture">25. Badge compteur in-app en mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en voiture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 5 créateurs suivis publient des contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un badge "5" sur l'onglet "Nouveautés"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> en consultant l'onglet, je vois les 5 nouveaux contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le badge disparaît après consultation</p>
|
||
<hr />
|
||
<h2 id="26-cout-des-notifications-push-firebase">26. Coût des notifications push Firebase</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je reçois 10 notifications push par jour
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis actif 365 jours par an</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule le coût</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 3650 notifications/an sont envoyées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Firebase Cloud Messaging est gratuit jusqu'à plusieurs millions de notifications
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût reste 0€ pour le volume MVP/Growth</p>
|
||
<hr />
|
||
<h2 id="27-deep-link-depuis-notification-push">27. Deep link depuis notification push</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je reçois une notification push pour un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape sur la notification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app s'ouvre directement sur le contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture démarre automatiquement (si j'étais à pied)</p>
|
||
<hr />
|
||
<h2 id="28-notification-refusee-si-permissions-desactivees-au-niveau-os">28. Notification refusée si permissions désactivées au niveau OS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai désactivé les notifications dans les paramètres iOS/Android</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un créateur suivi publie un contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification push n'est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'app propose de réactiver les permissions dans les paramètres
|
||
<span style="color: #F44336"><strong>Mais</strong></span> les badges in-app continuent de fonctionner</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="creation-daudio-guide-multi-sequences">Création d'audio-guide multi-séquences</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur de contenu</em>
|
||
<em>Je veux créer des audio-guides avec plusieurs séquences géolocalisées</em>
|
||
<em>Afin d'offrir des expériences guidées adaptées aux différents modes de déplacement</em></p>
|
||
</blockquote>
|
||
<p><strong>35 scénarios</strong> (32 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le créateur "guide@example.com" est connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que son compte est vérifié</p>
|
||
</blockquote>
|
||
<h2 id="1-plan-detection-automatique-du-mode-selon-la-vitesse">1. 📋 Plan: Détection automatique du mode selon la vitesse</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur se déplace à <vitesse> km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la vitesse est calculée sur 30 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode <mode> est suggéré automatiquement</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>vitesse</th>
|
||
<th>mode</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>Piéton</td>
|
||
</tr>
|
||
<tr>
|
||
<td>15</td>
|
||
<td>Vélo</td>
|
||
</tr>
|
||
<tr>
|
||
<td>35</td>
|
||
<td>Voiture</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>Voiture</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="2-suggestion-de-mode-au-demarrage-avec-confirmation">2. Suggestion de mode au démarrage avec confirmation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide "Safari du Paugre" est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur se déplace à 35 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'audio-guide démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup s'affiche:</p>
|
||
<hr />
|
||
<h2 id="3-changement-manuel-du-mode-detecte">3. Changement manuel du mode détecté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode "Voiture" est suggéré automatiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "Changer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 4 modes sont proposés:</p>
|
||
<pre><code>| mode | emoji |
|
||
|---|---|
|
||
| Piéton | 🚶 |
|
||
| Voiture | 🚗 |
|
||
| Vélo | 🚴 |
|
||
| Transport | 🚌 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="4-plan-caracteristiques-par-mode-de-deplacement">4. 📋 Plan: Caractéristiques par mode de déplacement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide configuré en mode <mode></p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les paramètres suivants sont appliqués:</p>
|
||
<pre><code>| paramètre | valeur |
|
||
|---|---|
|
||
| Vitesse détection | <vitesse_detection> |
|
||
| Déclenchement | <declenchement> |
|
||
</code></pre>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>mode</th>
|
||
<th>vitesse_detection</th>
|
||
<th>declenchement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Piéton</td>
|
||
<td><5 km/h</td>
|
||
<td>Manuel (bouton Suivant)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Voiture</td>
|
||
<td>>10 km/h</td>
|
||
<td>Auto GPS + Manuel</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Vélo</td>
|
||
<td>5-25 km/h</td>
|
||
<td>Auto GPS + Manuel</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Transport</td>
|
||
<td>Variable</td>
|
||
<td>Auto GPS + Manuel</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="5-acces-au-formulaire-de-creation-daudio-guide">5. Accès au formulaire de création d'audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur est sur son dashboard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "Créer un audio-guide"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le formulaire de création s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le wizard guidé en 4 étapes est visible:</p>
|
||
<pre><code>| étape | description |
|
||
|---|---|
|
||
| 1 | Infos générales |
|
||
| 2 | Ajout séquences |
|
||
| 3 | Preview carte |
|
||
| 4 | Validation modération |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="6-etape-1-informations-generales-obligatoires">6. Étape 1 - Informations générales obligatoires</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur est sur l'étape 1 du wizard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il complète le formulaire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les champs suivants sont obligatoires:</p>
|
||
<pre><code>| 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+ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="7-selection-du-mode-de-deplacement">7. Sélection du mode de déplacement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur crée un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il sélectionne le mode "🚗 Voiture (GPS auto + manuel)"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le champ "Vitesse recommandée" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la plage suggérée est "30-50 km/h"</p>
|
||
<hr />
|
||
<h2 id="8-validation-du-titre">8. Validation du titre</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur entre un titre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le titre contient moins de 5 caractères</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur "Minimum 5 caractères" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton "Suivant" est désactivé</p>
|
||
<hr />
|
||
<h2 id="9-validation-de-la-description">9. Validation de la description</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur entre une description</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la description contient 520 caractères</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur "Maximum 500 caractères" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 20 caractères en trop sont surlignés en rouge</p>
|
||
<hr />
|
||
<h2 id="10-etape-2-ajout-de-la-premiere-sequence">10. Étape 2 - Ajout de la première séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur est sur l'étape 2 "Ajout séquences"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "Ajouter séquence"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le formulaire de séquence s'affiche avec:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-ajout-du-point-gps-pour-une-sequence">11. Ajout du point GPS pour une séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur ajoute une séquence en mode "Voiture"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "📍 Ajouter point GPS"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une carte s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut:</p>
|
||
<pre><code>| action |
|
||
|---|
|
||
| Cliquer sur la carte |
|
||
| Entrer coordonnées manuelles |
|
||
| Utiliser sa position actuelle |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="12-configuration-du-rayon-de-declenchement-avec-preview">12. Configuration du rayon de déclenchement avec preview</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un point GPS est défini à (43.1234, 2.5678)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur ajuste le curseur de rayon</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le rayon varie de 10m à 200m
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un cercle visuel est affiché sur la carte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la valeur actuelle s'affiche "30m"</p>
|
||
<hr />
|
||
<h2 id="13-plan-rayon-par-defaut-selon-le-mode">13. 📋 Plan: Rayon par défaut selon le mode</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en mode <mode></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur ajoute un point GPS</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le rayon par défaut est <rayon_defaut></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>mode</th>
|
||
<th>rayon_defaut</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Voiture</td>
|
||
<td>30m</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Vélo</td>
|
||
<td>50m</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Transport</td>
|
||
<td>100m</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="14-suggestion-intelligente-du-rayon">14. Suggestion intelligente du rayon</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en mode "Voiture" avec vitesse recommandée 30 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur ajoute un point GPS</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une suggestion s'affiche: "Recommandé : 30m pour voiture à 30 km/h"</p>
|
||
<hr />
|
||
<h2 id="15-upload-audio-pour-une-sequence">15. Upload audio pour une séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur crée une séquence "Introduction"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il upload un fichier audio de 5 MB</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est vérifié:</p>
|
||
<pre><code>| vérification | règle |
|
||
|---|---|
|
||
| Format | MP3, AAC, M4A |
|
||
| Taille max | 200 MB |
|
||
| Durée max | 15 minutes |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="16-ordre-des-sequences-modifiable">16. Ordre des séquences modifiable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide avec 5 séquences:</p>
|
||
<pre><code>| ordre | titre |
|
||
|---|---|
|
||
| 1 | Introduction |
|
||
| 2 | Les lions |
|
||
| 3 | Les girafes |
|
||
| 4 | Les éléphants |
|
||
| 5 | Conclusion |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur glisse "Les éléphants" en position 2</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'ordre devient:</p>
|
||
<pre><code>| ordre | titre |
|
||
|---|---|
|
||
| 1 | Introduction |
|
||
| 2 | Les éléphants |
|
||
| 3 | Les lions |
|
||
| 4 | Les girafes |
|
||
| 5 | Conclusion |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-nombre-minimum-de-sequences-requis">17. Nombre minimum de séquences requis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide avec seulement 1 séquence</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur tente de passer à l'étape suivante</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur s'affiche: "Minimum 2 séquences requis"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton "Suivant" est désactivé</p>
|
||
<hr />
|
||
<h2 id="18-nombre-maximum-de-sequences">18. Nombre maximum de séquences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide avec 50 séquences</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur tente d'ajouter une 51ème séquence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur s'affiche: "Maximum 50 séquences par audio-guide"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton "+ Ajouter séquence" est désactivé</p>
|
||
<hr />
|
||
<h2 id="19-etape-3-preview-carte-avec-trace-et-points">19. Étape 3 - Preview carte avec tracé et points</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide avec 5 séquences géolocalisées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur accède à l'étape 3 "Preview carte"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une carte Leaflet s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les éléments suivants sont visibles:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="20-statistiques-du-parcours">20. Statistiques du parcours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide avec les séquences suivantes:</p>
|
||
<pre><code>| séquence | durée | distance_au_suivant |
|
||
|---|---|---|
|
||
| 1 | 2:15 | 150m |
|
||
| 2 | 3:42 | 200m |
|
||
| 3 | 4:10 | 320m |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les statistiques sont calculées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le résumé suivant est affiché:</p>
|
||
<pre><code>| métrique | valeur |
|
||
|---|---|
|
||
| Séquences | 3 complètes |
|
||
| Durée totale | 10:07 |
|
||
| Distance totale | 670m |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="21-modification-dune-sequence-depuis-la-carte">21. Modification d'une séquence depuis la carte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la preview carte est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur clique sur le marker "2"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup s'affiche avec:</p>
|
||
<pre><code>| information |
|
||
|---|
|
||
| Titre: "Les lions" |
|
||
| Durée: 3:42 |
|
||
| Rayon: 30m |
|
||
| [✏️ Modifier] |
|
||
| [🗑️ Supprimer] |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="22-zone-de-diffusion-geographique">22. Zone de diffusion géographique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide avec des points dans Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur définit la zone de diffusion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut choisir:</p>
|
||
<pre><code>| type | exemple |
|
||
|---|---|
|
||
| Polygon | Tracé manuel sur carte |
|
||
| Ville | Paris (API Nominatim) |
|
||
| Département | 75 - Paris |
|
||
| Région | Île-de-France |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="23-etape-4-publication-et-validation-moderation">23. Étape 4 - Publication et validation modération</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur qui publie ses 3 premiers audio-guides</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "✅ Publier audio-guide"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message s'affiche:</p>
|
||
<hr />
|
||
<h2 id="24-publication-directe-pour-createurs-experimentes">24. Publication directe pour créateurs expérimentés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur ayant publié 5 audio-guides validés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun strike actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il publie un nouvel audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide est publié immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il devient visible pour les utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune validation manuelle n'est requise</p>
|
||
<hr />
|
||
<h2 id="25-mode-pieton-sans-points-gps-obligatoires">25. Mode piéton sans points GPS obligatoires</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en mode "🚶 Piéton"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur ajoute une séquence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le champ "Point GPS" est optionnel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le champ "Rayon déclenchement" est masqué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message info s'affiche: "Mode manuel : les séquences se déclenchent au clic utilisateur"</p>
|
||
<hr />
|
||
<h2 id="26-sauvegarde-brouillon-automatique">26. Sauvegarde brouillon automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur édite un audio-guide depuis 5 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il ajoute une nouvelle séquence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide est sauvegardé en brouillon automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast "Brouillon sauvegardé" s'affiche brièvement</p>
|
||
<hr />
|
||
<h2 id="27-reprise-dun-brouillon">27. Reprise d'un brouillon</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en brouillon "Safari du Paugre"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il contient 3 séquences complètes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur retourne sur son dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le brouillon est visible avec le statut "📝 Brouillon"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Continuer" est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la progression "3/5 séquences" est affichée</p>
|
||
<hr />
|
||
<h2 id="28-suppression-dun-brouillon">28. Suppression d'un brouillon</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en brouillon</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur clique sur "🗑️ Supprimer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une confirmation s'affiche:</p>
|
||
<hr />
|
||
<h2 id="29-modification-dun-audio-guide-publie">29. Modification d'un audio-guide publié</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide publié "Safari du Paugre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur clique sur "✏️ Modifier"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut modifier:</p>
|
||
<pre><code>| élément modifiable | élément non modifiable |
|
||
|---|---|
|
||
| Titre | Mode de déplacement |
|
||
| Description | Points GPS |
|
||
| Tags | Rayons déclenchement |
|
||
| Séquences (ordre) | |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> un avertissement s'affiche: "Les modifications structurelles nécessitent une nouvelle publication"</p>
|
||
<hr />
|
||
<h2 id="30-duplication-dun-audio-guide-existant">30. Duplication d'un audio-guide existant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide publié "Visite Paris"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur clique sur "📋 Dupliquer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une copie est créée avec le titre "Visite Paris (copie)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les séquences sont copiées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut est "📝 Brouillon"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur peut modifier avant publication</p>
|
||
<hr />
|
||
<h2 id="31-upload-audio-echoue-format-non-supporte">31. Upload audio échoue (format non supporté)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur upload un fichier "audio.wav"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le format est vérifié</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur s'affiche: "Format non supporté. Utilisez MP3, AAC ou M4A"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier est rejeté</p>
|
||
<hr />
|
||
<h2 id="32-upload-audio-echoue-taille-trop-grande">32. Upload audio échoue (taille trop grande)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur upload un fichier de 250 MB</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la taille est vérifiée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur s'affiche: "Fichier trop volumineux. Maximum 200 MB"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier est rejeté</p>
|
||
<hr />
|
||
<h2 id="33-points-gps-trop-eloignes-alerte-coherence">33. Points GPS trop éloignés (alerte cohérence)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en mode "Piéton"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une séquence au Louvre (Paris)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur ajoute une séquence à Lyon</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un avertissement s'affiche:</p>
|
||
<hr />
|
||
<h2 id="34-pas-de-connexion-lors-de-la-sauvegarde">34. Pas de connexion lors de la sauvegarde</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur édite un audio-guide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la connexion réseau est perdue</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il tente de sauvegarder</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le brouillon est sauvegardé localement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message s'affiche: "Sauvegarde locale. Sera synchronisée à la reconnexion"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une icône "☁️ Hors ligne" s'affiche</p>
|
||
<hr />
|
||
<h2 id="35-reprise-apres-perte-de-connexion">35. Reprise après perte de connexion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un brouillon sauvegardé localement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la connexion réseau est rétablie</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le brouillon est synchronisé automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast "✅ Audio-guide synchronisé" s'affiche</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="integration-audio-guides-avec-autres-fonctionnalites">Intégration audio-guides avec autres fonctionnalités</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux utiliser les audio-guides avec toutes les fonctionnalités de l'app</em>
|
||
<em>Afin d'avoir une expérience complète et cohérente</em></p>
|
||
</blockquote>
|
||
<p><strong>39 scénarios</strong> (38 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur "jean@example.com" est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-telechargement-complet-dun-audio-guide">1. Téléchargement complet d'un audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide "Visite du Louvre" avec 12 séquences</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "⬇️ Télécharger pour écouter hors ligne"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les 12 séquences sont téléchargées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les métadonnées (titres, descriptions, GPS) sont sauvegardées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les images (cover, miniatures) sont mises en cache</p>
|
||
<hr />
|
||
<h2 id="2-affichage-de-la-progression-du-telechargement">2. Affichage de la progression du téléchargement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un téléchargement d'audio-guide est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur consulte l'état</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progression s'affiche:</p>
|
||
<hr />
|
||
<h2 id="3-telechargement-uniquement-en-wifi-par-defaut">3. Téléchargement uniquement en WiFi (par défaut)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'option "Télécharger uniquement en WiFi" est activée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur lance un téléchargement sur réseau mobile</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un avertissement s'affiche:</p>
|
||
<hr />
|
||
<h2 id="4-gestion-de-lespace-de-stockage">4. Gestion de l'espace de stockage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'appareil a 500 MB d'espace libre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un audio-guide pèse 380 MB</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur lance le téléchargement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un avertissement s'affiche:</p>
|
||
<hr />
|
||
<h2 id="5-liste-des-audio-guides-telecharges">5. Liste des audio-guides téléchargés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a téléchargé 3 audio-guides</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède à "Bibliothèque > Téléchargés"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="6-lecture-hors-connexion-complete">6. Lecture hors connexion complète</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide est téléchargé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur active le mode avion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il lance l'audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les séquences sont lisibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les métadonnées sont accessibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les images s'affichent normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la progression est sauvegardée localement</p>
|
||
<hr />
|
||
<h2 id="7-gps-fonctionne-en-mode-avion-mode-voiture">7. GPS fonctionne en mode avion (mode voiture)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide voiture est téléchargé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le mode avion est activé (avec GPS actif)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur se déplace</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les déclenchements GPS fonctionnent normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la distance/ETA sont calculés</p>
|
||
<hr />
|
||
<h2 id="8-suppression-daudio-guide-telecharge">8. Suppression d'audio-guide téléchargé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide téléchargé pèse 380 MB</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "🗑️ Supprimer téléchargement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une confirmation s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si confirmé, les 380 MB sont libérés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'audio-guide reste accessible en streaming</p>
|
||
<hr />
|
||
<h2 id="9-mise-a-jour-automatique-si-nouvelle-version">9. Mise à jour automatique si nouvelle version</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide téléchargé a été mis à jour par le créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur se connecte en WiFi</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification s'affiche:</p>
|
||
<hr />
|
||
<h2 id="10-ajout-daudio-guide-a-une-playlist">10. Ajout d'audio-guide à une playlist</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur consulte un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "➕ Ajouter à une playlist"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ses playlists s'affichent:</p>
|
||
<pre><code>| playlist |
|
||
|---|
|
||
| 🗺️ Voyages en France |
|
||
| 🏛️ Musées parisiens |
|
||
| + Créer nouvelle playlist |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-comportement-audio-guide-dans-une-playlist">11. Comportement audio-guide dans une playlist</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une playlist contenant 2 audio-guides et 1 podcast</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la lecture atteint un audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide démarre à la séquence 1 (ou progression sauvegardée)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les séquences se jouent normalement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'audio-guide se termine (dernière séquence)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu suivant de la playlist démarre</p>
|
||
<hr />
|
||
<h2 id="12-audio-guide-marque-comme-favori">12. Audio-guide marqué comme "Favori"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur aime un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "⭐ Ajouter aux favoris"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide est ajouté à la section "Favoris"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est facilement accessible depuis le menu principal</p>
|
||
<hr />
|
||
<h2 id="13-collections-thematiques-daudio-guides">13. Collections thématiques d'audio-guides</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave propose des collections éditoriales</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur accède à "Collections"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit des collections comme:</p>
|
||
<pre><code>| collection | nombre_audio_guides |
|
||
|---|---|
|
||
| 🏛️ Musées de France | 12 |
|
||
| 🦁 Parcs animaliers | 8 |
|
||
| 🚴 Circuits vélo | 15 |
|
||
| 🚗 Routes touristiques | 10 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="14-bouton-partager-sur-page-audio-guide">14. Bouton partager sur page audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur consulte un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "⬆️ Partager"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le menu de partage natif s'ouvre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le lien généré est "https://roadwave.fr/share/ag/louvre_123"</p>
|
||
<hr />
|
||
<h2 id="15-page-web-de-partage-pour-audio-guide">15. Page web de partage pour audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un lien d'audio-guide partagé est ouvert sur le web</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page se charge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle affiche:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="16-deep-link-vers-audio-guide-specifique">16. Deep link vers audio-guide spécifique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'app est installée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un lien "https://roadwave.fr/share/ag/louvre_123" est cliqué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte l'app</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app s'ouvre directement sur l'audio-guide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut démarrer immédiatement</p>
|
||
<hr />
|
||
<h2 id="17-partage-avec-sequence-specifique">17. Partage avec séquence spécifique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur est sur la séquence 5 "La Joconde"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il partage l'audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le lien généré est "https://roadwave.fr/share/ag/louvre_123?seq=5"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le destinataire est dirigé vers la séquence 5 directement</p>
|
||
<hr />
|
||
<h2 id="18-note-globale-de-laudio-guide">18. Note globale de l'audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur termine un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la dernière séquence se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup de notation s'affiche:</p>
|
||
<hr />
|
||
<h2 id="19-note-moyenne-affichee-sur-la-page">19. Note moyenne affichée sur la page</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide a reçu 150 notes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la moyenne est 4.3/5</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la note "⭐ 4.3 (150 avis)" est visible</p>
|
||
<hr />
|
||
<h2 id="20-commentaires-tries-par-pertinence">20. Commentaires triés par pertinence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide a 50 commentaires</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur consulte les avis</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les commentaires sont triés par défaut selon:</p>
|
||
<pre><code>| critère | poids |
|
||
|---|---|
|
||
| Note élevée | 30% |
|
||
| Récent | 30% |
|
||
| Likes reçus | 40% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="21-reponse-du-createur-aux-commentaires">21. Réponse du créateur aux commentaires</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur laisse un commentaire négatif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur consulte son dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut répondre au commentaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sa réponse apparaît en dessous avec badge "Créateur"</p>
|
||
<hr />
|
||
<h2 id="22-audio-guides-similaires-recommandes">22. Audio-guides similaires recommandés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur termine "Visite du Louvre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'algorithme suggère des audio-guides basés sur:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="23-suggestion-geographique-contextuelle">23. Suggestion géographique contextuelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur est à Paris (GPS détecté)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il ouvre l'onglet "Audio-guides"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les audio-guides parisiens sont mis en avant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un filtre "🗺️ Autour de moi" est pré-appliqué</p>
|
||
<hr />
|
||
<h2 id="24-badge-populaire-dans-votre-region">24. Badge "Populaire dans votre région"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide a >100 écoutes dans la région Île-de-France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur est en Île-de-France</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'audio-guide est affiché</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un badge "🔥 Populaire près de chez vous" est visible</p>
|
||
<hr />
|
||
<h2 id="25-prechargement-de-la-sequence-suivante">25. Préchargement de la séquence suivante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 3 est en cours à 2:30/3:42</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il reste 60 secondes de lecture</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 est préchargée en arrière-plan
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transition est instantanée (0 latence)</p>
|
||
<hr />
|
||
<h2 id="26-buffer-adaptatif-selon-connexion">26. Buffer adaptatif selon connexion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur est sur réseau 4G</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la séquence démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 30 secondes d'audio sont bufferisées initialement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffering continue en arrière-plan</p>
|
||
<hr />
|
||
<h2 id="27-plan-buffer-selon-qualite-reseau">27. 📋 Plan: Buffer selon qualité réseau</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur est sur réseau <reseau></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une séquence démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> <buffer_secondes> secondes sont bufferisées</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>reseau</th>
|
||
<th>buffer_secondes</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>WiFi</td>
|
||
<td>60</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5G</td>
|
||
<td>45</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4G</td>
|
||
<td>30</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3G</td>
|
||
<td>20</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="28-compression-audio-adaptative">28. Compression audio adaptative</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur est sur connexion lente (3G)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une séquence est streamée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le CDN sert la version 64 kbps (au lieu de 128 kbps)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité reste acceptable pour la voix</p>
|
||
<hr />
|
||
<h2 id="29-cache-intelligent-des-sequences-jouees">29. Cache intelligent des séquences jouées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté les séquences 1-5</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "Précédent" pour réécouter la séquence 4</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 est chargée depuis le cache local
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le chargement est instantané (pas de stream)</p>
|
||
<hr />
|
||
<h2 id="30-nettoyage-automatique-du-cache">30. Nettoyage automatique du cache</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le cache audio occupe 500 MB
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la limite configurée est 300 MB</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le nettoyage automatique s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les séquences les plus anciennes (non téléchargées) sont supprimées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cache revient à 280 MB</p>
|
||
<hr />
|
||
<h2 id="31-tracking-des-evenements-cles">31. Tracking des événements clés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur écoute un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il interagit avec l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les événements suivants sont trackés:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="32-heatmap-des-abandons-par-sequence">32. Heatmap des abandons par séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide a été écouté 1000 fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur consulte la heatmap</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit pour chaque séquence:</p>
|
||
<pre><code>| sequence | starts | completions | abandon_rate |
|
||
|---|---|---|---|
|
||
| 1 | 1000 | 950 | 5% |
|
||
| 2 | 950 | 920 | 3% |
|
||
| 3 | 920 | 850 | 8% |
|
||
| ... | ... | ... | ... |
|
||
| 12 | 650 | 580 | 11% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="33-attribution-gps-auto-vs-manuel">33. Attribution GPS auto vs manuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide voiture avec 8 points GPS</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les statistiques sont calculées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le créateur voit:</p>
|
||
<pre><code>| mode_declenchement | nombre |
|
||
|---|---|
|
||
| GPS automatique | 542 |
|
||
| Manuel | 123 |
|
||
| Point manqué | 89 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="34-audio-guide-avec-une-seule-sequence-edge-case">34. Audio-guide avec une seule séquence (edge case)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide avec seulement 1 séquence</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il est publié</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un avertissement s'affiche:</p>
|
||
<hr />
|
||
<h2 id="35-sequence-manquante-ou-corrompue">35. Séquence manquante ou corrompue</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une séquence 5 a un fichier audio corrompu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur tente de la lire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "⏭️ Passer à la suivante" est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur reçoit une notification de l'erreur</p>
|
||
<hr />
|
||
<h2 id="36-gps-desactive-puis-reactive-en-cours-de-route">36. GPS désactivé puis réactivé en cours de route</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide voiture en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur désactive le GPS</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il le réactive 10 minutes plus tard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le déclenchement automatique reprend
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les points GPS manqués entre-temps ne déclenchent pas de popup</p>
|
||
<hr />
|
||
<h2 id="37-modification-daudio-guide-avec-utilisateurs-en-cours">37. Modification d'audio-guide avec utilisateurs en cours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide a 50 utilisateurs en cours d'écoute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur modifie une séquence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les utilisateurs actuels conservent l'ancienne version
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les nouveaux utilisateurs obtiennent la nouvelle version
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message informe les utilisateurs lors de la prochaine ouverture</p>
|
||
<hr />
|
||
<h2 id="38-suppression-daudio-guide-par-le-createur">38. Suppression d'audio-guide par le créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide a 20 utilisateurs avec progression</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur supprime l'audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une confirmation stricte est demandée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si confirmé, les progressions utilisateurs sont archivées (30 jours)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'audio-guide devient inaccessible</p>
|
||
<hr />
|
||
<h2 id="39-signalement-daudio-guide-pour-contenu-inapproprie">39. Signalement d'audio-guide pour contenu inapproprié</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur signale un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement est modéré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> jugé valide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide est dépublié temporairement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur reçoit une notification d'explication
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut corriger puis republier</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="audio-guide-mode-pieton-navigation-manuelle">Audio-guide mode piéton (navigation manuelle)</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur à pied</em>
|
||
<em>Je veux naviguer manuellement entre les séquences d'un audio-guide</em>
|
||
<em>Afin de contrôler mon rythme de visite</em></p>
|
||
</blockquote>
|
||
<p><strong>29 scénarios</strong> (28 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur "jean@example.com" est connecté (gratuit)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un audio-guide piéton "Visite du Louvre" est disponible avec 12 séquences</p>
|
||
</blockquote>
|
||
<h2 id="1-fin-de-sequence-normale-avec-pause-automatique">1. Fin de séquence normale avec pause automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 1 "Introduction" est en cours de lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la séquence se termine à 2:15</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le player se met en pause automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message suivant s'affiche: "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la barre de progression indique "1/12 complétée"</p>
|
||
<hr />
|
||
<h2 id="2-passage-manuel-a-la-sequence-suivante">2. Passage manuel à la séquence suivante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 1 est terminée et le player en pause</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur appuie sur le bouton [▶|] "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 2 "Pyramide du Louvre" démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune latence n'est observée</p>
|
||
<hr />
|
||
<h2 id="3-sequence-avec-publicite-15-sequences">3. Séquence avec publicité (1/5 séquences)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 5 se termine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que c'est la 5ème séquence (1 pub toutes les 5)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la séquence se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité s'enchaîne automatiquement (sans attente bouton)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publicité se lit normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est skippable après 5 secondes</p>
|
||
<hr />
|
||
<h2 id="4-fin-de-publicite-avec-pause-automatique">4. Fin de publicité avec pause automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité est en cours de lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la publicité se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le player se met en pause automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message suivant s'affiche: "Séquence 6 prête. Appuyez sur Suivant."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur doit cliquer sur [▶|] pour continuer</p>
|
||
<hr />
|
||
<h2 id="5-flux-complet-sequence-pub-sequence">5. Flux complet séquence → pub → séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 5 démarre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la séquence 5 se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité démarre automatiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la publicité se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le player se met en pause</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶|]</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 6 démarre</p>
|
||
<hr />
|
||
<h2 id="6-plan-frequence-de-publicite-configurable">6. 📋 Plan: Fréquence de publicité configurable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur gratuit écoute un audio-guide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la fréquence pub est configurée à <frequence></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il termine la séquence <numero_sequence></p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une publicité est insérée : <pub_inseree></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>frequence</th>
|
||
<th>numero_sequence</th>
|
||
<th>pub_inseree</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1/5</td>
|
||
<td>5</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/5</td>
|
||
<td>10</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/5</td>
|
||
<td>4</td>
|
||
<td>Non</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/3</td>
|
||
<td>3</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/3</td>
|
||
<td>6</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="7-utilisateur-premium-sans-publicites">7. Utilisateur Premium sans publicités</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur "premium@example.com" est abonné Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il écoute un audio-guide piéton</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il termine la séquence 5</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune publicité n'est insérée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le player se met en pause immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message "Séquence 6 prête. Appuyez sur Suivant." s'affiche</p>
|
||
<hr />
|
||
<h2 id="8-boutons-de-controle-disponibles-en-mode-pieton">8. Boutons de contrôle disponibles en mode piéton</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide piéton est en lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur consulte les contrôles</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les boutons suivants sont visibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-passage-a-la-sequence-suivante-pendant-la-lecture">9. Passage à la séquence suivante pendant la lecture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 3 "La Joconde" est en cours à 1:42/3:42</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶|] "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 "Vénus de Milo" démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la séquence 3 n'est pas marquée comme écoutée (car <80%)</p>
|
||
<hr />
|
||
<h2 id="10-retour-a-la-sequence-precedente-saut-direct">10. Retour à la séquence précédente (saut direct)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 5 est en cours de lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [|◀] "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 démarre depuis le début (0:00)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de logique "replay si >10s" (contrairement au contenu classique)</p>
|
||
<hr />
|
||
<h2 id="11-pause-et-reprise-pendant-une-sequence">11. Pause et reprise pendant une séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 2 est en cours à 1:15/1:48</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [⏸️] "Pause"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture se met en pause
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la position 1:15 est conservée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶️] "Play"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture reprend exactement à 1:15</p>
|
||
<hr />
|
||
<h2 id="12-interface-liste-des-sequences">12. Interface liste des séquences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide de 12 séquences est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [📋] "Liste séquences"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une liste complète s'affiche avec:</p>
|
||
<pre><code>| é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" |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="13-sequence-en-cours-dans-la-liste">13. Séquence en cours dans la liste</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 3 est en cours à 1:22/3:42</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la liste des séquences est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 3 affiche:</p>
|
||
<hr />
|
||
<h2 id="14-navigation-libre-vers-sequence-non-encore-ecoutee">14. Navigation libre vers séquence non encore écoutée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur est sur la séquence 3
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que les séquences 4 à 12 n'ont pas été écoutées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "8. Les Appartements de Napoléon"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 8 démarre immédiatement depuis 0:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les séquences 4 à 7 restent marquées ⭕ "À écouter"</p>
|
||
<hr />
|
||
<h2 id="15-retour-a-une-sequence-deja-ecoutee">15. Retour à une séquence déjà écoutée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 2 "Pyramide du Louvre" a été écoutée à 100%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'elle est marquée ✅ "Écouté"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique dessus dans la liste</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 2 démarre depuis 0:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut ✅ est conservé</p>
|
||
<hr />
|
||
<h2 id="16-checkmarks-sur-sequences-ecoutees-80">16. Checkmarks sur séquences écoutées >80%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur écoute la séquence 2 de durée 1:48</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il écoute jusqu'à 1:30 (83% de complétion)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il passe à la séquence suivante</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 2 est marquée ✅ "Écouté"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la date d'écoute est enregistrée</p>
|
||
<hr />
|
||
<h2 id="17-pas-de-checkmark-si-sequence-ecoutee-80">17. Pas de checkmark si séquence écoutée <80%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur écoute la séquence 3 de durée 3:42</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il écoute jusqu'à 1:30 (40% de complétion)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il passe à la séquence suivante</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 3 reste marquée ⭕ "À écouter"</p>
|
||
<hr />
|
||
<h2 id="18-bouton-tout-afficher-si-plus-de-6-sequences">18. Bouton "Tout afficher" si plus de 6 séquences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide avec 12 séquences</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la liste est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules les 6 premières séquences sont visibles initialement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Tout afficher ▼" est présent</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "Tout afficher ▼"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 6 séquences restantes sont affichées</p>
|
||
<hr />
|
||
<h2 id="19-saut-vers-sequence-specifique-depuis-la-barre-de-progression">19. Saut vers séquence spécifique depuis la barre de progression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "3/12" dans la barre de progression</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la liste des séquences s'ouvre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la séquence en cours (3) est mise en surbrillance</p>
|
||
<hr />
|
||
<h2 id="20-position-exacte-sauvegardee-automatiquement">20. Position exacte sauvegardée automatiquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 5 est en cours à 2:34/4:10</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur quitte l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la position 2:34 dans la séquence 5 est sauvegardée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la sauvegarde est effectuée localement (SQLite)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la sauvegarde est synchronisée sur le cloud (PostgreSQL)</p>
|
||
<hr />
|
||
<h2 id="21-reprise-apres-fermeture-de-lapplication">21. Reprise après fermeture de l'application</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a quitté l'app à la séquence 5 position 2:34</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il rouvre l'audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup de reprise s'affiche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "▶️ Reprendre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture reprend à la séquence 5 position 2:34 exacte</p>
|
||
<hr />
|
||
<h2 id="22-visiteur-qui-connait-deja-certaines-uvres">22. Visiteur qui connaît déjà certaines œuvres</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un visiteur du Louvre démarre l'audio-guide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il connaît déjà "La Joconde" (séquence 3)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il arrive à la séquence 3
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il clique sur [▶|] "Suivant" après 10 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la séquence 3 n'est pas marquée comme écoutée</p>
|
||
<hr />
|
||
<h2 id="23-visiteur-qui-veut-voir-une-uvre-eloignee">23. Visiteur qui veut voir une œuvre éloignée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un visiteur est à la séquence 2
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il aperçoit "La Victoire de Samothrace" (séquence 8) physiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il ouvre la liste et clique sur la séquence 8</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 8 démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut écouter la description même si les séquences 3-7 ne sont pas écoutées</p>
|
||
<hr />
|
||
<h2 id="24-visiteur-qui-prend-une-pause-cafe">24. Visiteur qui prend une pause café</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un visiteur écoute la séquence 6</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur [⏸️] "Pause"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il ferme l'application pendant 30 minutes
|
||
<span style="color: #FF9800"><strong>Quand</strong></span> il rouvre l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 6 reprend à la position exacte où il s'était arrêté</p>
|
||
<hr />
|
||
<h2 id="25-visiteur-qui-revient-le-lendemain">25. Visiteur qui revient le lendemain</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un visiteur a écouté les séquences 1-5 hier
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il revient au musée aujourd'hui</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il ouvre l'audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup propose "▶️ Reprendre" (séquence 6)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les séquences 1-5 sont marquées ✅ "Écouté"</p>
|
||
<hr />
|
||
<h2 id="26-sequence-audio-corrompue-ou-indisponible">26. Séquence audio corrompue ou indisponible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 7 a un fichier audio corrompu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur tente de la lire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur s'affiche:</p>
|
||
<hr />
|
||
<h2 id="27-perte-de-connexion-pendant-le-chargement">27. Perte de connexion pendant le chargement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur lance la séquence 4
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la connexion réseau est perdue</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le chargement échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message s'affiche: "Connexion perdue. Vérifiez votre réseau."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "🔄 Réessayer" est disponible</p>
|
||
<hr />
|
||
<h2 id="28-batterie-faible-en-cours-de-visite">28. Batterie faible en cours de visite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la batterie de l'appareil est à 5%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur écoute une séquence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification système s'affiche: "Batterie faible. Progression sauvegardée."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la position est sauvegardée localement toutes les 10 secondes</p>
|
||
<hr />
|
||
<h2 id="29-mode-pieton-sans-points-gps-pas-dalerte-localisation">29. Mode piéton sans points GPS (pas d'alerte localisation)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en mode piéton
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le GPS est désactivé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur démarre l'audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune alerte GPS ne s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'audio-guide fonctionne normalement (navigation 100% manuelle)</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="audio-guide-mode-voiture-gps-automatique">Audio-guide mode voiture (GPS automatique)</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur en voiture</em>
|
||
<em>Je veux que les séquences se déclenchent automatiquement selon ma position GPS</em>
|
||
<em>Afin de profiter d'une expérience guidée hands-free</em></p>
|
||
</blockquote>
|
||
<p><strong>45 scénarios</strong> (40 standards, 5 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur "jean@example.com" est connecté (gratuit)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un audio-guide voiture "Safari du Paugre" est disponible avec 8 séquences
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le GPS est activé</p>
|
||
</blockquote>
|
||
<h2 id="1-distinction-audio-guides-vs-contenus-geolocalises-simples">1. Distinction audio-guides vs contenus géolocalisés simples</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur est en mode voiture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il écoute un contenu géolocalisé simple (1 séquence unique)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification avec compteur 7→1 est affichée 7s avant le point
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il doit valider avec "Suivant" + décompte 5s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce contenu compte 1/6 dans le quota horaire</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il démarre un audio-guide multi-séquences</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les séquences se déclenchent au point GPS exact (rayon 30m)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun compteur 7s n'est affiché (juste notification "Ding" + toast 2s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'audio-guide entier compte 1/6 dans le quota</p>
|
||
<hr />
|
||
<h2 id="2-demarrage-automatique-au-premier-point-gps">2. Démarrage automatique au premier point GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur démarre l'audio-guide "Safari du Paugre"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le point de départ est à (43.1234, 2.5678) avec rayon 30m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur entre dans le rayon de 30m</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 1 "Introduction - Point d'accueil" démarre automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification sonore "Ding" est jouée (non intrusif)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast s'affiche brièvement pendant 2s: "Introduction - Point d'accueil"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun compteur 7→1 n'est affiché (contrairement aux contenus géolocalisés simples)</p>
|
||
<hr />
|
||
<h2 id="3-declenchement-automatique-sequence-suivante">3. Déclenchement automatique séquence suivante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 1 est terminée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur se déplace vers le point GPS 2 (43.1245, 2.5690)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur entre dans le rayon de 30m du point 2</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 2 "Enclos des lions" démarre automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification "Ding" + toast "Enclos des lions" s'affiche</p>
|
||
<hr />
|
||
<h2 id="4-navigation-manuelle-conservee-bouton-suivant-actif">4. Navigation manuelle conservée (bouton Suivant actif)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 1 est en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur est encore loin du point GPS 2 (distance 500m)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶|] "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 2 démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune vérification GPS n'est effectuée</p>
|
||
<hr />
|
||
<h2 id="5-navigation-manuelle-conservee-bouton-precedent-actif">5. Navigation manuelle conservée (bouton Précédent actif)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 3 est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [|◀] "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 2 démarre depuis le début
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune vérification GPS n'est effectuée</p>
|
||
<hr />
|
||
<h2 id="6-tous-les-boutons-de-controle-restent-actifs">6. Tous les boutons de contrôle restent actifs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide voiture est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur consulte les contrôles</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les boutons suivants sont actifs:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="7-use-case-embouteillage-sequence-finie-point-gps-loin">7. Use case - Embouteillage (séquence finie, point GPS loin)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 3 "Enclos des girafes" est terminée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le point GPS 4 est à 2 km de distance (embouteillage)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique manuellement sur [▶|] "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut continuer l'expérience sans attendre d'atteindre le point GPS</p>
|
||
<hr />
|
||
<h2 id="8-use-case-route-fermee-point-gps-inaccessible">8. Use case - Route fermée (point GPS inaccessible)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le point GPS 5 est sur une route fermée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur ne peut pas s'en approcher</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶|] "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 5 démarre quand même
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'audio-guide continue normalement</p>
|
||
<hr />
|
||
<h2 id="9-use-case-passager-manipule-lapplication">9. Use case - Passager manipule l'application</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur est passager (non conducteur)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la vitesse du véhicule est 45 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le passager clique sur [▶|] "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence suivante démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un avertissement s'affiche pendant 3 secondes</p>
|
||
<hr />
|
||
<h2 id="10-avertissement-securite-si-vitesse-10-kmh">10. Avertissement sécurité si vitesse >10 km/h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la vitesse actuelle est 35 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur un bouton (Suivant ou Précédent)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est exécutée immédiatement (pas de blocage)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast s'affiche pendant 3 secondes:</p>
|
||
<hr />
|
||
<h2 id="11-plan-avertissement-selon-la-vitesse">11. 📋 Plan: Avertissement selon la vitesse</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la vitesse actuelle est <vitesse> km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur un bouton de navigation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'avertissement est affiché : <avertissement></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>vitesse</th>
|
||
<th>avertissement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>Non</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>Non</td>
|
||
</tr>
|
||
<tr>
|
||
<td>11</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>35</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>90</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="12-affichage-entre-deux-sequences-avec-progress-bar">12. Affichage entre deux séquences avec progress bar</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 2 "Les lions" vient de se terminer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le prochain point GPS 3 "Enclos des girafes" est à 500m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface bascule en mode "attente prochain point"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écran affiche:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="13-progress-bar-dynamique-vers-le-prochain-point">13. Progress bar dynamique vers le prochain point</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la distance initiale vers le prochain point était 500m
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la séquence précédente est terminée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur se rapproche du prochain point
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la distance actuelle est 175m</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progress bar affiche "65%" remplie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le calcul est: 100 - (175 / 500 * 100) = 65%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la barre se met à jour chaque seconde</p>
|
||
<hr />
|
||
<h2 id="14-bouton-rejouer-seq-pour-reecouter">14. Bouton "Rejouer séq." pour réécouter</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 3 vient de se terminer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'interface "attente prochain point" est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶️ Rejouer séq.]</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 3 redémarre depuis 0:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut la réécouter (utile si distraction)</p>
|
||
<hr />
|
||
<h2 id="15-interface-en-conduite-avec-distance-et-eta">15. Interface en conduite avec distance et ETA</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 2 est en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le prochain point GPS 3 "Enclos des girafes" est à 320m
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la vitesse actuelle est 28 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les informations suivantes sont visibles:</p>
|
||
<pre><code>| 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" |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="16-mise-a-jour-de-la-distance-en-temps-reel">16. Mise à jour de la distance en temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la distance au prochain point est 500m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 10 secondes s'écoulent et que l'utilisateur se rapproche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la distance est mise à jour chaque seconde
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la nouvelle distance "450m" s'affiche</p>
|
||
<hr />
|
||
<h2 id="17-mise-a-jour-de-leta-en-temps-reel">17. Mise à jour de l'ETA en temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'ETA est "≈ 2 minutes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la vitesse est constante à 30 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur se rapproche du point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'ETA est recalculé chaque seconde
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il diminue progressivement: "≈ 1 minute 50", "≈ 1 minute 40", etc.</p>
|
||
<hr />
|
||
<h2 id="18-plan-format-daffichage-de-la-distance">18. 📋 Plan: Format d'affichage de la distance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la distance au prochain point est <distance_metres></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface est mise à jour</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la distance affichée est "<affichage>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>distance_metres</th>
|
||
<th>affichage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>50 m</td>
|
||
</tr>
|
||
<tr>
|
||
<td>320</td>
|
||
<td>320 m</td>
|
||
</tr>
|
||
<tr>
|
||
<td>980</td>
|
||
<td>980 m</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1200</td>
|
||
<td>1.2 km</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5400</td>
|
||
<td>5.4 km</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="19-plan-format-daffichage-de-leta">19. 📋 Plan: Format d'affichage de l'ETA</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'ETA calculé est <secondes> secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface est mise à jour</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'ETA affiché est "<affichage>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>secondes</th>
|
||
<th>affichage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>30</td>
|
||
<td>≈ 30 secondes</td>
|
||
</tr>
|
||
<tr>
|
||
<td>75</td>
|
||
<td>≈ 1 minute</td>
|
||
</tr>
|
||
<tr>
|
||
<td>150</td>
|
||
<td>≈ 2 minutes</td>
|
||
</tr>
|
||
<tr>
|
||
<td>400</td>
|
||
<td>≈ 6 minutes</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="20-calcul-de-la-direction-fleche-8-directions">20. Calcul de la direction (flèche 8 directions)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la position actuelle est (43.1234, 2.5678)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le prochain point est au nord-est (angle 45°)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la direction est calculée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la flèche "↗" est affichée</p>
|
||
<hr />
|
||
<h2 id="21-plan-fleches-de-direction-selon-langle">21. 📋 Plan: Flèches de direction selon l'angle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'angle vers le prochain point est <angle>°</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la direction est calculée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la flèche "<fleche>" est affichée</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>angle</th>
|
||
<th>fleche</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>↑</td>
|
||
</tr>
|
||
<tr>
|
||
<td>45</td>
|
||
<td>↗</td>
|
||
</tr>
|
||
<tr>
|
||
<td>90</td>
|
||
<td>→</td>
|
||
</tr>
|
||
<tr>
|
||
<td>135</td>
|
||
<td>↘</td>
|
||
</tr>
|
||
<tr>
|
||
<td>180</td>
|
||
<td>↓</td>
|
||
</tr>
|
||
<tr>
|
||
<td>225</td>
|
||
<td>↙</td>
|
||
</tr>
|
||
<tr>
|
||
<td>270</td>
|
||
<td>←</td>
|
||
</tr>
|
||
<tr>
|
||
<td>315</td>
|
||
<td>↖</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="22-mise-a-jour-de-la-direction-toutes-les-5-secondes">22. Mise à jour de la direction toutes les 5 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la direction actuelle est ↑ (nord)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur tourne vers l'est</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 5 secondes s'écoulent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la direction est recalculée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la nouvelle flèche ↗ (nord-est) s'affiche</p>
|
||
<hr />
|
||
<h2 id="23-message-en-attente-de-deplacement-si-vitesse-5-kmh">23. Message "En attente de déplacement" si vitesse <5 km/h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la vitesse actuelle est 2 km/h (arrêté)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'ETA est calculé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le message "En attente de déplacement" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'ETA n'est pas calculé (car vitesse insuffisante)</p>
|
||
<hr />
|
||
<h2 id="24-simplicite-de-linterface-pas-de-carte-miniature">24. Simplicité de l'interface (pas de carte miniature)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide voiture est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune carte miniature n'est présente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les éléments essentiels sont affichés:</p>
|
||
<pre><code>| élément |
|
||
|---|
|
||
| Distance |
|
||
| ETA |
|
||
| Direction (flèche) |
|
||
| Vitesse |
|
||
| Contrôles audio |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="25-rayon-de-declenchement-par-defaut-en-mode-voiture">25. Rayon de déclenchement par défaut en mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide voiture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un point GPS est défini</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le rayon de déclenchement est 30 mètres par défaut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le rayon de tolérance "point manqué" est 100 mètres</p>
|
||
<hr />
|
||
<h2 id="26-declenchement-dans-le-rayon-30m">26. Déclenchement dans le rayon (30m)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le point GPS 3 est défini avec rayon 30m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur entre à 25m du point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 3 se déclenche automatiquement</p>
|
||
<hr />
|
||
<h2 id="27-pas-de-declenchement-hors-rayon">27. Pas de déclenchement hors rayon</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le point GPS 3 a un rayon de 30m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur passe à 45m du point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 3 ne se déclenche pas automatiquement</p>
|
||
<hr />
|
||
<h2 id="28-point-manque-dans-rayon-de-tolerance-100m">28. Point manqué dans rayon de tolérance (100m)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur passe à 60m du point GPS 4 (hors rayon 30m)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 60m < 100m (rayon tolérance)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le point est détecté comme manqué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un toast s'affiche: "⚠️ Point manqué : Enclos des éléphants"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une popup s'affiche pendant 5 secondes avec 3 options</p>
|
||
<hr />
|
||
<h2 id="29-popup-point-manque-avec-3-actions">29. Popup "Point manqué" avec 3 actions</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un point GPS a été manqué (distance 60m)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la popup s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les options suivantes sont disponibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="30-action-ecouter-quand-meme">30. Action "Écouter quand même"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un point GPS est manqué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "🔊 Écouter quand même"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence correspondante démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut continuer sa route</p>
|
||
<hr />
|
||
<h2 id="31-action-passer-au-suivant">31. Action "Passer au suivant"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un point GPS 5 est manqué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "⏭️ Passer au suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 5 est ignorée (non écoutée)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application attend le point GPS 6
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la distance vers le point 6 s'affiche</p>
|
||
<hr />
|
||
<h2 id="32-action-faire-demi-tour">32. Action "Faire demi-tour"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un point GPS est manqué à (43.1250, 2.5700)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "🔙 Faire demi-tour"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'application détecte l'app GPS installée (Google Maps ou Waze)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ouvre la navigation GPS externe vers (43.1250, 2.5700)</p>
|
||
<hr />
|
||
<h2 id="33-point-manque-au-dela-du-rayon-de-tolerance-100m">33. Point manqué au-delà du rayon de tolérance (>100m)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur passe à 150m du point GPS 6</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la distance est détectée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune popup ne s'affiche (point trop loin)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut naviguer manuellement avec [▶|]</p>
|
||
<hr />
|
||
<h2 id="34-plan-gestion-selon-la-distance-au-point">34. 📋 Plan: Gestion selon la distance au point</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un point GPS avec rayon 30m et tolérance 100m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur passe à <distance> du point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le comportement est <comportement></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>distance</th>
|
||
<th>comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>20m</td>
|
||
<td>Déclenchement automatique séquence</td>
|
||
</tr>
|
||
<tr>
|
||
<td>40m</td>
|
||
<td>Rien (hors rayon, pas encore tolérance)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>60m</td>
|
||
<td>Popup "Point manqué" avec 3 options</td>
|
||
</tr>
|
||
<tr>
|
||
<td>110m</td>
|
||
<td>Rien (trop loin, hors tolérance)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="35-configuration-rayon-personnalise-par-le-createur">35. Configuration rayon personnalisé par le créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur définit un rayon de 50m (au lieu de 30m)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur entre à 45m du point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence se déclenche automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le rayon personnalisé est respecté</p>
|
||
<hr />
|
||
<h2 id="36-rayon-minimum-et-maximum-configurables">36. Rayon minimum et maximum configurables</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur configure un rayon</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il ajuste le curseur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les valeurs disponibles sont de 10m à 200m
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le rayon par défaut suggéré est 30m pour la voiture</p>
|
||
<hr />
|
||
<h2 id="37-safari-parc-avec-declenchement-automatique-fluide">37. Safari-parc avec déclenchement automatique fluide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur roule dans un safari à 20 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il passe devant "Enclos des lions" (point GPS 2)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 2 démarre automatiquement sans intervention
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut se concentrer sur la conduite et l'observation</p>
|
||
<hr />
|
||
<h2 id="38-detour-imprevu-travaux-sur-la-route">38. Détour imprévu (travaux sur la route)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur prend un détour à cause de travaux
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le point GPS 4 devient inaccessible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il est loin du point (>100m)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il clique manuellement sur [▶|]</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 démarre quand même
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience continue sans blocage</p>
|
||
<hr />
|
||
<h2 id="39-passager-qui-navigue-librement">39. Passager qui navigue librement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un passager utilise l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le conducteur roule à 50 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le passager clique sur "Précédent" pour réécouter</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est exécutée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un warning apparaît brièvement (sensibilisation)</p>
|
||
<hr />
|
||
<h2 id="40-embouteillage-prolonge">40. Embouteillage prolongé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la séquence 3 est terminée depuis 10 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur est bloqué dans un embouteillage
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le point GPS 4 est encore à 1.5 km</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶|]</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 4 démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut passer le temps en écoutant</p>
|
||
<hr />
|
||
<h2 id="41-gps-desactive-en-mode-voiture">41. GPS désactivé en mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide voiture est démarré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le GPS est désactivé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application détecte l'absence de GPS</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte s'affiche:</p>
|
||
<hr />
|
||
<h2 id="42-action-passer-en-mode-manuel">42. Action "Passer en mode Manuel"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le GPS est désactivé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "Passer en mode Manuel"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide bascule en navigation 100% manuelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les boutons [▶|] et [|◀] permettent de naviguer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun déclenchement GPS n'est tenté</p>
|
||
<hr />
|
||
<h2 id="43-precision-gps-insuffisante">43. Précision GPS insuffisante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le signal GPS a une précision de ±150m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur approche d'un point GPS avec rayon 30m</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un avertissement s'affiche:</p>
|
||
<hr />
|
||
<h2 id="44-perte-signal-gps-en-cours-de-route">44. Perte signal GPS en cours de route</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide voiture est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signal GPS est perdu (tunnel, parking souterrain)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un toast s'affiche: "Signal GPS perdu. Navigation manuelle active."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les boutons de navigation restent actifs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signal GPS revient</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un toast s'affiche: "Signal GPS rétabli"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le déclenchement automatique est réactivé</p>
|
||
<hr />
|
||
<h2 id="45-depassement-de-la-vitesse-recommandee">45. Dépassement de la vitesse recommandée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide recommande 20-30 km/h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur roule à 65 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la vitesse est détectée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'affichage vitesse est en orange: "⚠️ 65 km/h"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message info s'affiche: "Vitesse élevée. Risque de manquer des points."</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="audio-guides-modes-velo-et-transport">Audio-guides modes vélo et transport</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur à vélo ou en transport en commun</em>
|
||
<em>Je veux profiter d'un guidage GPS adapté à mon mode de déplacement</em>
|
||
<em>Afin d'avoir une expérience optimisée avec tolérances appropriées</em></p>
|
||
</blockquote>
|
||
<p><strong>27 scénarios</strong> (24 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur "jean@example.com" est connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le GPS est activé</p>
|
||
</blockquote>
|
||
<h2 id="1-plan-parametres-par-mode-de-deplacement">1. 📋 Plan: Paramètres par mode de déplacement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide configuré en mode <mode></p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les paramètres suivants sont appliqués:</p>
|
||
<pre><code>| 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> |
|
||
</code></pre>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>mode</th>
|
||
<th>rayon_declenchement</th>
|
||
<th>rayon_tolerance</th>
|
||
<th>vitesse_recommandee</th>
|
||
<th>seuil_warning</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Voiture</td>
|
||
<td>30m</td>
|
||
<td>100m</td>
|
||
<td>20-50 km/h</td>
|
||
<td>>10 km/h</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Vélo</td>
|
||
<td>50m</td>
|
||
<td>75m</td>
|
||
<td>10-25 km/h</td>
|
||
<td>>5 km/h</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Transport</td>
|
||
<td>100m</td>
|
||
<td>150m</td>
|
||
<td>Variable</td>
|
||
<td>Désactivé</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="2-declenchement-automatique-avec-rayon-50m-mode-velo">2. Déclenchement automatique avec rayon 50m (mode vélo)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide vélo "Circuit des châteaux de la Loire"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le point GPS 3 a un rayon de 50m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur à vélo entre à 45m du point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 3 "Château de Chambord" se déclenche automatiquement</p>
|
||
<hr />
|
||
<h2 id="3-rayon-plus-large-justifie-pour-le-velo">3. Rayon plus large justifié pour le vélo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un cycliste roule sur piste cyclable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que sa vitesse varie entre 8 et 22 km/h (arrêts fréquents)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le tracé est moins prévisible qu'en voiture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un point GPS avec rayon 50m est défini</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le rayon plus large compense la variabilité de trajectoire</p>
|
||
<hr />
|
||
<h2 id="4-warning-securite-des-5-kmh-en-velo">4. Warning sécurité dès 5 km/h en vélo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide vélo en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la vitesse actuelle est 12 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶|] "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est exécutée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un warning s'affiche: "⚠️ Manipulation en déplacement détecté. Pour votre sécurité, arrêtez-vous."</p>
|
||
<hr />
|
||
<h2 id="5-plan-warning-velo-selon-la-vitesse">5. 📋 Plan: Warning vélo selon la vitesse</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la vitesse actuelle à vélo est <vitesse> km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur un bouton de navigation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le warning est affiché : <warning></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>vitesse</th>
|
||
<th>warning</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>Non</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4</td>
|
||
<td>Non</td>
|
||
</tr>
|
||
<tr>
|
||
<td>6</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>15</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>25</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="6-tolerance-gps-moins-stricte-en-velo">6. Tolérance GPS moins stricte en vélo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un cycliste passe à 65m du point GPS 4
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le rayon de déclenchement est 50m
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le rayon de tolérance est 75m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la distance est détectée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la popup "Point manqué" s'affiche avec 3 options
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système tolère l'écart (trajectoire vélo moins prévisible)</p>
|
||
<hr />
|
||
<h2 id="7-affichage-adapte-au-velo">7. Affichage adapté au vélo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide vélo en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les informations suivantes sont visibles:</p>
|
||
<pre><code>| 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" |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="8-cas-dusage-piste-cyclable-avec-arrets-frequents">8. Cas d'usage - Piste cyclable avec arrêts fréquents</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un cycliste suit un circuit nature
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il s'arrête régulièrement (feux, photos, fatigue)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il s'arrête à 40m d'un point GPS (rayon 50m)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence se déclenche automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le rayon large permet le déclenchement malgré l'arrêt</p>
|
||
<hr />
|
||
<h2 id="9-cas-dusage-circulation-mixte-pietonsvelos">9. Cas d'usage - Circulation mixte piétons/vélos</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un cycliste roule sur voie partagée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il doit ralentir fréquemment pour éviter les piétons</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> sa vitesse varie entre 5 et 20 km/h</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système s'adapte avec le rayon 50m
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le déclenchement reste fiable</p>
|
||
<hr />
|
||
<h2 id="10-declenchement-automatique-avec-rayon-100m-mode-transport">10. Déclenchement automatique avec rayon 100m (mode transport)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide transport "Ligne touristique Paris"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le point GPS "Tour Eiffel" a un rayon de 100m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le bus touristique entre à 85m du point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence "Tour Eiffel" se déclenche automatiquement</p>
|
||
<hr />
|
||
<h2 id="11-rayon-tres-large-justifie-pour-le-transport">11. Rayon très large justifié pour le transport</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un bus touristique suit une ligne fixe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il effectue des arrêts fréquents (stations)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur n'a aucun contrôle sur la trajectoire</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un point GPS avec rayon 100m est défini</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le rayon large compense les arrêts et la ligne fixe</p>
|
||
<hr />
|
||
<h2 id="12-pas-de-warning-securite-en-mode-transport">12. Pas de warning sécurité en mode transport</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide transport en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le bus roule à 50 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶|] "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est exécutée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun warning n'est affiché</p>
|
||
<hr />
|
||
<h2 id="13-vitesse-recommandee-selon-ligne">13. Vitesse recommandée "Selon ligne"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide transport</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la vitesse recommandée indique "Selon ligne"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune valeur fixe n'est affichée (car ligne de transport varie)</p>
|
||
<hr />
|
||
<h2 id="14-tolerance-horaire-pour-retards">14. Tolérance horaire pour retards</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un bus touristique est en retard de 3 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il arrive au point GPS "Musée du Louvre" avec retard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il entre dans le rayon de 100m</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence se déclenche normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système tolère le retard (pas de pénalité temporelle)</p>
|
||
<hr />
|
||
<h2 id="15-tolerance-spatiale-tres-large-150m">15. Tolérance spatiale très large (150m)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un bus passe à 120m du point GPS "Arc de Triomphe"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le rayon de déclenchement est 100m
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le rayon de tolérance est 150m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la distance est détectée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la popup "Point manqué" s'affiche avec 3 options</p>
|
||
<hr />
|
||
<h2 id="16-affichage-adapte-au-transport">16. Affichage adapté au transport</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide transport en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les informations suivantes sont visibles:</p>
|
||
<pre><code>| 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" |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-cas-dusage-bus-touristique-hop-on-hop-off">17. Cas d'usage - Bus touristique hop-on hop-off</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un bus touristique "Paris Open Tour"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il suit un circuit fixe avec 15 arrêts</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il approche de chaque arrêt</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence correspondante se déclenche automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur n'a rien à faire (expérience passive)</p>
|
||
<hr />
|
||
<h2 id="18-cas-dusage-train-panoramique">18. Cas d'usage - Train panoramique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un train touristique "Ligne des Alpes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il roule à vitesse variable (20-80 km/h)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il passe près de points d'intérêt</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les séquences se déclenchent avec rayon 100m
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système compense la vitesse élevée</p>
|
||
<hr />
|
||
<h2 id="19-navigation-manuelle-conservee-velo-et-transport">19. Navigation manuelle conservée (vélo et transport)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en mode <mode></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur [▶|] ou [|◀]</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les boutons manuels fonctionnent normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune vérification GPS n'est effectuée</p>
|
||
<hr />
|
||
<h2 id="20-affichage-distance-eta-direction-tous-modes">20. Affichage distance + ETA + direction (tous modes)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en mode <mode></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'interface est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les informations distance, ETA et direction sont affichées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le format est identique au mode voiture</p>
|
||
<hr />
|
||
<h2 id="21-gestion-point-manque-identique">21. Gestion "Point manqué" identique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide en mode <mode></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un point GPS est manqué (dans rayon tolérance)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la popup avec 3 options s'affiche:</p>
|
||
<pre><code>| option |
|
||
|---|
|
||
| 🔊 Écouter quand même |
|
||
| ⏭️ Passer au suivant |
|
||
| 🔙 Faire demi-tour |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="22-plan-insertion-publicite-dans-tous-les-modes">22. 📋 Plan: Insertion publicité dans tous les modes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un utilisateur gratuit écoute un audio-guide en mode <mode></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la séquence 5 se termine (1 pub / 5 séquences)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité s'enchaîne automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est skippable après 5 secondes</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>mode</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Voiture</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Vélo</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Transport</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Piéton</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="23-gps-imprecis-en-foret-velo">23. GPS imprécis en forêt (vélo)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un cycliste dans une forêt dense
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la précision GPS est ±80m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il approche d'un point GPS avec rayon 50m</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un avertissement s'affiche:</p>
|
||
<hr />
|
||
<h2 id="24-bus-devie-de-son-itineraire-transport">24. Bus dévié de son itinéraire (transport)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un bus touristique avec déviation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que plusieurs points GPS deviennent inaccessibles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur est informé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message s'affiche:</p>
|
||
<hr />
|
||
<h2 id="25-changement-de-mode-en-cours-de-route">25. Changement de mode en cours de route</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide démarré en mode "Vélo"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur décide de continuer à pied
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il ouvre les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut changer le mode vers "Piéton"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les rayons sont reconfigurés automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une confirmation s'affiche:</p>
|
||
<hr />
|
||
<h2 id="26-detection-automatique-incoherente">26. Détection automatique incohérente</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur marche rapidement (7 km/h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le système détecte "Vélo" par erreur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suggestion s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'utilisateur peut cliquer sur "Changer"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sélectionner manuellement "Piéton"</p>
|
||
<hr />
|
||
<h2 id="27-batterie-en-mode-velo-longue-distance">27. Batterie en mode vélo longue distance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un circuit vélo de 50 km avec 20 séquences
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur roule pendant 3 heures</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la batterie atteint 15%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification suggère:</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="audio-guides-premium-et-monetisation">Audio-guides Premium et monétisation</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur</em>
|
||
<em>Je veux pouvoir proposer des audio-guides Premium</em>
|
||
<em>Afin de monétiser mon contenu de qualité</em></p>
|
||
</blockquote>
|
||
<p><strong>31 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le créateur "guide@example.com" est connecté et vérifié</p>
|
||
</blockquote>
|
||
<h2 id="1-creation-dun-audio-guide-premium">1. Création d'un audio-guide Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur crée un audio-guide "Visite VIP Versailles"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède aux paramètres de monétisation (étape 4)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut choisir:</p>
|
||
<pre><code>| option | description |
|
||
|---|---|
|
||
| Gratuit | Accessible à tous (avec pubs) |
|
||
| Premium | Réservé abonnés Premium |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="2-badge-premium-visible-sur-laudio-guide">2. Badge Premium visible sur l'audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide configuré en Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il est affiché dans les résultats de recherche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un badge "👑 Premium" est visible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la cover image a un cadre doré subtil</p>
|
||
<hr />
|
||
<h2 id="3-preview-3-premieres-sequences-pour-utilisateurs-gratuits">3. Preview 3 premières séquences pour utilisateurs gratuits</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide Premium "Visite VIP Versailles" avec 15 séquences
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur gratuit ouvre l'audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte la liste des séquences</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les séquences affichent:</p>
|
||
<pre><code>| séquence | état |
|
||
|---|---|
|
||
| 1 | ✅ Accessible (preview) |
|
||
| 2 | ✅ Accessible (preview) |
|
||
| 3 | ✅ Accessible (preview) |
|
||
| 4 | 🔒 Réservé Premium |
|
||
| 5-15 | 🔒 Réservé Premium |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="4-ecoute-des-3-premieres-sequences-sans-blocage">4. Écoute des 3 premières séquences sans blocage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un utilisateur gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un audio-guide Premium avec preview</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il écoute les séquences 1, 2 et 3</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune publicité n'est insérée (preview = teasing)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'écoute est fluide</p>
|
||
<hr />
|
||
<h2 id="5-paywall-apres-la-3eme-sequence">5. Paywall après la 3ème séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur gratuit termine la séquence 3</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la séquence se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un overlay paywall s'affiche immédiatement:</p>
|
||
<hr />
|
||
<h2 id="6-bouton-passer-premium-vers-tunnel-dabonnement">6. Bouton "Passer Premium" vers tunnel d'abonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'overlay paywall Premium est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "Passer Premium"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est redirigé vers la page d'abonnement Mangopay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'audio-guide actuel est marqué en "pending" (reprise après souscription)</p>
|
||
<hr />
|
||
<h2 id="7-reprise-automatique-apres-souscription-premium">7. Reprise automatique après souscription Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur s'est abonné Premium depuis un paywall audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'abonnement est activé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est redirigé vers l'audio-guide automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la séquence 4 démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast de bienvenue s'affiche: "✨ Bienvenue Premium ! Profitez de votre audio-guide"</p>
|
||
<hr />
|
||
<h2 id="8-utilisateur-premium-acces-complet-immediat">8. Utilisateur Premium - Accès complet immédiat</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium ouvre un audio-guide Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte la liste des séquences</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les 15 séquences sont accessibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun paywall ne s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune publicité n'est insérée</p>
|
||
<hr />
|
||
<h2 id="9-pas-de-preview-si-laudio-guide-a-3-sequences">9. Pas de preview si l'audio-guide a <3 séquences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide Premium avec seulement 2 séquences</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur gratuit tente de l'ouvrir</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un paywall s'affiche immédiatement (avant lecture)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune preview n'est disponible</p>
|
||
<hr />
|
||
<h2 id="10-remuneration-createur-pour-audio-guide-premium">10. Rémunération créateur pour audio-guide Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec un audio-guide Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 50 utilisateurs Premium ont écouté l'audio-guide ce mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la répartition des revenus est calculée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le créateur reçoit 70% des revenus proportionnels
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la formule est: (Écoutes créateur / Total écoutes Premium) × 70% pool Premium</p>
|
||
<hr />
|
||
<h2 id="11-dashboard-revenus-par-audio-guide">11. Dashboard revenus par audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a 3 audio-guides Premium publiés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte son dashboard revenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit pour chaque audio-guide:</p>
|
||
<pre><code>| 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 € |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="12-comparaison-gratuit-vs-premium">12. Comparaison gratuit vs Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a publié 2 audio-guides:</p>
|
||
<pre><code>| titre | type | ecoutes_mois | revenus |
|
||
|---|---|---|---|
|
||
| Tour de Paris | Gratuit | 1200 | 12.50 € |
|
||
| Visite VIP Versailles | Premium | 142 | 45.20 € |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte son dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut comparer les performances
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> constater que Premium génère plus de revenus par écoute</p>
|
||
<hr />
|
||
<h2 id="13-seuil-minimum-de-paiement-20">13. Seuil minimum de paiement (20€)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a généré 18€ de revenus ce mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le paiement mensuel est traité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le montant est reporté au mois suivant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message s'affiche: "Seuil minimum non atteint (20€). Montant reporté."</p>
|
||
<hr />
|
||
<h2 id="14-paiement-automatique-mensuel">14. Paiement automatique mensuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a généré 138.50€ de revenus en janvier</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 5 février arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le paiement est initié automatiquement via Mangopay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur reçoit une notification: "Paiement de 138.50€ en cours"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fonds arrivent sous 2-3 jours ouvrés</p>
|
||
<hr />
|
||
<h2 id="15-insertion-publicite-toutes-les-5-sequences-gratuit">15. Insertion publicité toutes les 5 séquences (gratuit)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide gratuit avec 12 séquences
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il termine la séquence 5</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une publicité démarre automatiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il termine la séquence 10</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une deuxième publicité démarre</p>
|
||
<hr />
|
||
<h2 id="16-publicite-apres-sequence-en-mode-pieton-avec-pause">16. Publicité après séquence en mode piéton (avec pause)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide piéton gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la séquence 5 se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité démarre automatiquement (pas d'attente bouton)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la pub est skippable après 5 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la publicité se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le player se met en pause
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur doit cliquer sur [▶|] pour continuer</p>
|
||
<hr />
|
||
<h2 id="17-publicite-en-mode-voiturevelotransport-automatique">17. Publicité en mode voiture/vélo/transport (automatique)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide voiture gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la séquence 5 se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité démarre automatiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la publicité se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 6 démarre automatiquement (pas de pause)</p>
|
||
<hr />
|
||
<h2 id="18-publicites-geolocalisees-dans-audio-guides">18. Publicités géolocalisées dans audio-guides</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide dans la région "Île-de-France"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une publicité doit être insérée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'API publicitaire filtre par:</p>
|
||
<pre><code>| critère | valeur |
|
||
|---|---|
|
||
| Géolocalisation | Île-de-France |
|
||
| Catégorie | Tourisme, Culture |
|
||
| Langue | Français |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="19-comptabilisation-des-impressions-pub-pour-createur">19. Comptabilisation des impressions pub pour créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide gratuit génère 200 écoutes complètes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que chaque écoute complète = 2 publicités (séq. 5 et 10)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les revenus pub sont calculés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 400 impressions sont comptabilisées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur reçoit 0.80€ (400 × 0.002€)</p>
|
||
<hr />
|
||
<h2 id="20-cta-premium-apres-audio-guide-gratuit-complete">20. CTA Premium après audio-guide gratuit complété</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur gratuit complète un audio-guide gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il termine la dernière séquence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un overlay s'affiche:</p>
|
||
<hr />
|
||
<h2 id="21-recommandations-daudio-guides-premium-apres-gratuit">21. Recommandations d'audio-guides Premium après gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur termine un audio-guide gratuit "Tour de Paris"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'overlay de fin s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 3 audio-guides Premium similaires sont suggérés:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="22-badge-premium-recommande-sur-audio-guides-populaires">22. Badge "Premium recommandé" sur audio-guides populaires</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide Premium avec >500 écoutes et note >4.5/5</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il est affiché dans les résultats de recherche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un badge "⭐ Premium recommandé" est visible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est mis en avant dans les résultats</p>
|
||
<hr />
|
||
<h2 id="23-conversion-tracking-pour-attribution-createur">23. Conversion tracking pour attribution créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur découvre Premium via un audio-guide créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il s'abonne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la conversion est trackée:</p>
|
||
<pre><code>| donnée | valeur |
|
||
|---|---|
|
||
| source_conversion | audio_guide_paywall |
|
||
| audio_guide_id | visite_vip_versailles_123 |
|
||
| creator_id | guide_versailles_456 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le créateur bénéficie d'un bonus de conversion</p>
|
||
<hr />
|
||
<h2 id="24-essai-gratuit-7-jours-premium-via-audio-guide">24. Essai gratuit 7 jours Premium via audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur gratuit atteint le paywall d'un audio-guide Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il n'a jamais essayé Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'overlay s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une offre d'essai est proposée:</p>
|
||
<hr />
|
||
<h2 id="25-activation-immediate-apres-essai-gratuit">25. Activation immédiate après essai gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur démarre un essai gratuit 7 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'essai est activé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide Premium démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les séquences sont débloquées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune publicité n'est insérée</p>
|
||
<hr />
|
||
<h2 id="26-rappel-2-jours-avant-fin-dessai">26. Rappel 2 jours avant fin d'essai</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a démarré un essai gratuit le 15/01</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 20/01 arrive (J-2)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification est envoyée:</p>
|
||
<hr />
|
||
<h2 id="27-createur-mix-gratuit-premium">27. Créateur mix gratuit + Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a publié 5 audio-guides:</p>
|
||
<pre><code>| titre | type |
|
||
|---|---|
|
||
| Découverte de Paris | Gratuit |
|
||
| Visite VIP Louvre | Premium |
|
||
| Balade Montmartre | Gratuit |
|
||
| Secrets Versailles | Premium |
|
||
| Visite express Orsay | Gratuit |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur découvre son profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les audio-guides gratuits servent de teasing
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les audio-guides Premium sont mis en avant avec badge</p>
|
||
<hr />
|
||
<h2 id="28-utilisateur-hesite-a-sabonner">28. Utilisateur hésite à s'abonner</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur atteint le paywall d'un audio-guide Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il clique sur "Découvrir d'autres audio-guides gratuits"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il revient 2 jours plus tard sur le même audio-guide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le paywall s'affiche à nouveau
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une réduction temporaire est proposée: "Offre spéciale : -20% premier mois"</p>
|
||
<hr />
|
||
<h2 id="29-echec-du-paiement-premium-via-paywall">29. Échec du paiement Premium via paywall</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur tente de s'abonner Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le paiement Mangopay échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur s'affiche:</p>
|
||
<hr />
|
||
<h2 id="30-abonnement-premium-expire-pendant-ecoute">30. Abonnement Premium expiré pendant écoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium écoute un audio-guide Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que son abonnement expire pendant l'écoute (séquence 8/15)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'expiration est détectée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute continue jusqu'à la fin de la séquence en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un overlay s'affiche ensuite:</p>
|
||
<hr />
|
||
<h2 id="31-createur-change-audio-guide-de-gratuit-a-premium">31. Créateur change audio-guide de gratuit à Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide gratuit a 50 utilisateurs avec progression</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur le passe en Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les utilisateurs ayant déjà commencé gardent l'accès complet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les nouveaux utilisateurs sont soumis au paywall
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message de transparence s'affiche:</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="sauvegarde-et-reprise-de-progression-audio-guide">Sauvegarde et reprise de progression audio-guide</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux que ma progression soit sauvegardée automatiquement</em>
|
||
<em>Afin de pouvoir reprendre mon audio-guide là où je me suis arrêté</em></p>
|
||
</blockquote>
|
||
<p><strong>32 scénarios</strong> (31 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur "jean@example.com" est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-sauvegarde-automatique-toutes-les-10-secondes">1. Sauvegarde automatique toutes les 10 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide "Visite du Louvre" est en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la séquence 3 est à la position 1:24</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 10 secondes s'écoulent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progression est sauvegardée automatiquement:</p>
|
||
<pre><code>| 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] |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="2-sauvegarde-locale-sqlite-pour-rapidite">2. Sauvegarde locale (SQLite) pour rapidité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une sauvegarde est déclenchée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la progression est enregistrée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les données sont écrites en SQLite local
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'écriture prend moins de 50ms
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application reste fluide</p>
|
||
<hr />
|
||
<h2 id="3-synchronisation-cloud-en-arriere-plan">3. Synchronisation cloud en arrière-plan</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une sauvegarde locale est effectuée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 30 secondes s'écoulent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progression est synchronisée vers PostgreSQL cloud
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la synchronisation s'effectue en arrière-plan
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle n'impacte pas les performances</p>
|
||
<hr />
|
||
<h2 id="4-sauvegarde-immediate-lors-de-la-fermeture">4. Sauvegarde immédiate lors de la fermeture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide est en cours à la séquence 4 position 2:15</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur ferme l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progression est sauvegardée immédiatement (local + cloud)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les données sont écrites avant la fermeture complète</p>
|
||
<hr />
|
||
<h2 id="5-sauvegarde-des-sequences-completees">5. Sauvegarde des séquences complétées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide de 12 séquences est en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que les séquences 1, 2, 4, 5 ont été écoutées à >80%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la progression est sauvegardée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les séquences complétées sont enregistrées:</p>
|
||
<hr />
|
||
<h2 id="6-historique-des-ecoutes-pour-statistiques">6. Historique des écoutes pour statistiques</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté 3 séquences d'un audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les données sont sauvegardées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique d'écoute inclut:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="7-popup-de-reprise-au-redemarrage">7. Popup de reprise au redémarrage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a quitté l'app à la séquence 6 position 2:34</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il rouvre l'audio-guide "Visite du Louvre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup s'affiche:</p>
|
||
<hr />
|
||
<h2 id="8-action-reprendre-position-exacte-restauree">8. Action "Reprendre" - Position exacte restaurée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une popup de reprise est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "▶️ Reprendre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la séquence 6 "Vénus de Milo" se charge
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la position exacte 2:34 est restaurée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture démarre automatiquement après 1 seconde</p>
|
||
<hr />
|
||
<h2 id="9-action-recommencer-reinitialisation-complete">9. Action "Recommencer" - Réinitialisation complète</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une popup de reprise est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "🔄 Recommencer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide redémarre depuis la séquence 1 position 0:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les séquences sont marquées ⭕ "À écouter"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique d'écoute est réinitialisé pour cette session</p>
|
||
<hr />
|
||
<h2 id="10-reprise-apres-7-jours-dinactivite">10. Reprise après 7 jours d'inactivité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a arrêté un audio-guide le 15/01/2026
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il le rouvre le 22/01/2026 (7 jours plus tard)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'audio-guide se charge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la popup de reprise s'affiche normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les données de progression sont conservées</p>
|
||
<hr />
|
||
<h2 id="11-reprise-sur-un-autre-appareil-synchronisation-cloud">11. Reprise sur un autre appareil (synchronisation cloud)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur écoute un audio-guide sur iPhone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il quitte à la séquence 4 position 1:20</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il ouvre le même audio-guide sur iPad</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la popup de reprise s'affiche avec la progression iPhone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut reprendre exactement où il s'était arrêté</p>
|
||
<hr />
|
||
<h2 id="12-conflit-de-synchronisation-dernier-appareil-gagne">12. Conflit de synchronisation (dernier appareil gagne)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur écoute sur iPhone à la séquence 3
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> simultanément sur iPad à la séquence 7</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les deux appareils synchronisent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progression la plus récente (timestamp) est conservée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'appareil avec ancienne progression affiche une notification:</p>
|
||
<hr />
|
||
<h2 id="13-mode-hors-ligne-sauvegarde-locale-uniquement">13. Mode hors-ligne - Sauvegarde locale uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur écoute un audio-guide hors connexion
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il atteint la séquence 5</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la progression est sauvegardée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les données sont écrites localement (SQLite)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une icône "☁️ Non synchronisé" s'affiche discrètement</p>
|
||
<hr />
|
||
<h2 id="14-synchronisation-automatique-a-la-reconnexion">14. Synchronisation automatique à la reconnexion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a écouté hors ligne jusqu'à la séquence 8
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 5 progressions locales ne sont pas synchronisées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la connexion réseau est rétablie</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 5 progressions sont synchronisées automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast s'affiche brièvement: "✅ Progression synchronisée"</p>
|
||
<hr />
|
||
<h2 id="15-suppression-de-la-progression-recommencer-proprement">15. Suppression de la progression (recommencer proprement)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur est à la séquence 10/12</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il ouvre les paramètres de l'audio-guide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il clique sur "🔄 Réinitialiser progression"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une confirmation s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si confirmé, la progression est effacée</p>
|
||
<hr />
|
||
<h2 id="16-taux-de-completion-global-de-laudio-guide">16. Taux de complétion global de l'audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide de 12 séquences
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur a écouté complètement 8 séquences
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> partiellement 1 séquence (45%)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les statistiques sont calculées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le taux de complétion affiché est "67%" (8/12)</p>
|
||
<hr />
|
||
<h2 id="17-badge-audio-guide-complete-a-100">17. Badge "Audio-guide complété" à 100%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide de 12 séquences</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur écoute la 12ème séquence à 100%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un badge "✅ Audio-guide complété" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification de félicitations est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut "Complété le 22/01/2026" est visible dans l'historique</p>
|
||
<hr />
|
||
<h2 id="18-temps-total-passe-sur-laudio-guide">18. Temps total passé sur l'audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté un audio-guide sur 2 sessions:</p>
|
||
<pre><code>| session | durée |
|
||
|---|---|
|
||
| 1 | 25 min |
|
||
| 2 | 18 min |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les statistiques sont calculées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le temps total est "43 minutes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est affiché dans l'historique personnel</p>
|
||
<hr />
|
||
<h2 id="19-liste-des-audio-guides-en-cours-dans-le-profil">19. Liste des audio-guides "En cours" dans le profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a 3 audio-guides en cours:</p>
|
||
<pre><code>| audio_guide | progression |
|
||
|---|---|
|
||
| Visite du Louvre | 6/12 |
|
||
| Safari du Paugre | 3/8 |
|
||
| Circuit Loire à Vélo | 12/15 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte son profil "Audio-guides"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la section "📍 En cours" affiche les 3 audio-guides
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque élément montre la progression sous forme de barre</p>
|
||
<hr />
|
||
<h2 id="20-liste-des-audio-guides-completes-dans-le-profil">20. Liste des audio-guides "Complétés" dans le profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a complété 2 audio-guides:</p>
|
||
<pre><code>| audio_guide | date_completion |
|
||
|---|---|
|
||
| Tour de Paris | 2026-01-15 |
|
||
| Découverte de Lyon | 2026-01-20 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte son profil "Audio-guides"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la section "✅ Complétés" affiche les 2 audio-guides
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la date de complétion est visible</p>
|
||
<hr />
|
||
<h2 id="21-badge-completiste-pour-10-audio-guides-completes">21. Badge "Complétiste" pour 10 audio-guides complétés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur complète son 10ème audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la complétion est enregistrée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un badge "🏆 Complétiste" est débloqué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il apparaît sur son profil
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification est envoyée:</p>
|
||
<hr />
|
||
<h2 id="22-plan-niveaux-de-badges-selon-nombre-daudio-guides-completes">22. 📋 Plan: Niveaux de badges selon nombre d'audio-guides complétés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur complète <nombre> audio-guides</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le badge est attribué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il reçoit le badge "<badge>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>nombre</th>
|
||
<th>badge</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>🎧 Premier audio-guide</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>🗺️ Explorateur</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>🏆 Complétiste</td>
|
||
</tr>
|
||
<tr>
|
||
<td>25</td>
|
||
<td>🌟 Expert</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>💎 Maître audio-guideur</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="23-dashboard-createur-statistiques-par-audio-guide">23. Dashboard créateur - Statistiques par audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a publié l'audio-guide "Visite du Louvre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte son dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métriques suivantes sont affichées:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="24-graphique-de-completion-par-sequence">24. Graphique de complétion par séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide de 12 séquences</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur consulte les statistiques détaillées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un graphique en barres affiche:</p>
|
||
<pre><code>| séquence | taux_completion |
|
||
|---|---|
|
||
| 1 | 100% |
|
||
| 2 | 95% |
|
||
| 3 | 89% |
|
||
| ... | ... |
|
||
| 12 | 58% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="25-detection-des-points-dabandon">25. Détection des points d'abandon</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide a un taux de complétion de 58%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 35% des utilisateurs abandonnent à la séquence 7</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur consulte les insights</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un avertissement s'affiche:</p>
|
||
<hr />
|
||
<h2 id="26-heatmap-geographique-des-ecoutes">26. Heatmap géographique des écoutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un audio-guide géolocalisé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur consulte la heatmap</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une carte affiche:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="27-temps-moyen-par-sequence">27. Temps moyen par séquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur analyse son audio-guide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte les statistiques temporelles</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit pour chaque séquence:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="28-notification-createur-pour-milestone">28. Notification créateur pour milestone</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide atteint 1000 écoutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le seuil est franchi</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification est envoyée au créateur:</p>
|
||
<hr />
|
||
<h2 id="29-corruption-de-donnees-de-sauvegarde">29. Corruption de données de sauvegarde</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une sauvegarde locale (SQLite) est corrompue</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application tente de charger la progression</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une récupération depuis le cloud est tentée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si réussie, les données cloud sont restaurées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la base locale est reconstruite</p>
|
||
<hr />
|
||
<h2 id="30-echec-de-synchronisation-cloud">30. Échec de synchronisation cloud</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API cloud est indisponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une tentative de synchronisation est effectuée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'application continue avec sauvegarde locale uniquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un retry automatique est programmé dans 5 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'icône "☁️ Non synchronisé" reste affichée</p>
|
||
<hr />
|
||
<h2 id="31-suppression-accidentelle-de-progression-recuperation">31. Suppression accidentelle de progression (récupération)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur réinitialise un audio-guide par erreur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il contacte le support dans les 7 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'équipe peut restaurer la progression depuis les backups
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les données sont récupérables (backup quotidien conservé 30 jours)</p>
|
||
<hr />
|
||
<h2 id="32-nettoyage-automatique-des-vieilles-progressions">32. Nettoyage automatique des vieilles progressions</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une progression n'a pas été mise à jour depuis 6 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le nettoyage automatique s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progression est archivée (mais pas supprimée)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut la restaurer via l'historique</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="classification-des-contenus-par-age">Classification des contenus par âge</h1>
|
||
<blockquote>
|
||
<p><em>En tant que plateforme responsable</em>
|
||
<em>Je veux classifier les contenus par tranche d'âge</em>
|
||
<em>Afin de protéger les mineurs et respecter les obligations légales</em></p>
|
||
</blockquote>
|
||
<p><strong>13 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-createur-doit-classifier-son-contenu-a-la-publication">1. Créateur doit classifier son contenu à la publication</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur connecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée un nouveau contenu audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois obligatoirement choisir une classification d'âge parmi:</p>
|
||
<pre><code>| classification | description |
|
||
|---|---|
|
||
| Tout public | Contenu adapté à tous les âges |
|
||
| 13+ | Contenu mature léger |
|
||
| 16+ | Contenu mature |
|
||
| 18+ | Contenu adulte |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="2-publication-impossible-sans-classification">2. Publication impossible sans classification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée un contenu audio</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de publier sans sélectionner de classification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publication échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vous devez sélectionner une classification d'âge"</p>
|
||
<hr />
|
||
<h2 id="3-utilisateur-13-15-ans-voit-uniquement-du-contenu-tout-public">3. Utilisateur 13-15 ans voit uniquement du contenu "Tout public"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de 14 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il existe des contenus avec les classifications suivantes:</p>
|
||
<pre><code>| classification | nombre |
|
||
|---|---|
|
||
| Tout public | 20 |
|
||
| 13+ | 15 |
|
||
| 16+ | 10 |
|
||
| 18+ | 5 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois uniquement les 20 contenus "Tout public"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les autres contenus ne sont jamais proposés</p>
|
||
<hr />
|
||
<h2 id="4-utilisateur-16-17-ans-voit-tout-public-et-13">4. Utilisateur 16-17 ans voit "Tout public" et "13+"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de 17 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il existe des contenus avec les classifications suivantes:</p>
|
||
<pre><code>| classification | nombre |
|
||
|---|---|
|
||
| Tout public | 20 |
|
||
| 13+ | 15 |
|
||
| 16+ | 10 |
|
||
| 18+ | 5 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois 35 contenus (Tout public + 13+)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus 16+ et 18+ ne sont pas proposés</p>
|
||
<hr />
|
||
<h2 id="5-utilisateur-18-voit-tous-les-contenus">5. Utilisateur 18+ voit tous les contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de 25 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il existe des contenus avec toutes les classifications</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois tous les contenus sans restriction
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun filtre d'âge n'est appliqué</p>
|
||
<hr />
|
||
<h2 id="6-mode-kids-active-automatiquement-pour-les-moins-de-13-ans">6. Mode Kids activé automatiquement pour les moins de 13 ans</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je m'inscris avec une date de naissance "2013-01-21"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode Kids est activé automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois uniquement du contenu "Tout public"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> des protections supplémentaires sont appliquées</p>
|
||
<hr />
|
||
<h2 id="7-moderateur-reclassifie-un-contenu-mal-categorise">7. Modérateur reclassifie un contenu mal catégorisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est publié avec la classification "Tout public"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ce contenu contient du langage inapproprié détecté en modération</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur reclassifie ce contenu en "16+"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la nouvelle classification est appliquée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu n'est plus visible pour les utilisateurs de moins de 16 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur reçoit une notification de reclassification</p>
|
||
<hr />
|
||
<h2 id="8-strike-si-classification-volontairement-incorrecte">8. Strike si classification volontairement incorrecte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a publié un contenu "18+" classifié comme "Tout public"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ce contenu a été signalé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur confirme la mauvaise classification volontaire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le créateur reçoit 1 strike
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est reclassifié en "18+"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur reçoit une notification explicative</p>
|
||
<hr />
|
||
<h2 id="9-createur-peut-voir-la-distribution-dage-de-son-audience">9. Créateur peut voir la distribution d'âge de son audience</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai publié des contenus avec différentes classifications</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la répartition des âges de mes auditeurs:</p>
|
||
<pre><code>| tranche_age | pourcentage |
|
||
|---|---|
|
||
| 13-15 ans | 15% |
|
||
| 16-17 ans | 20% |
|
||
| 18+ ans | 65% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="10-recherche-filtree-par-classification-dage">10. Recherche filtrée par classification d'âge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de 16 ans</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je recherche des contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les résultats incluent uniquement:</p>
|
||
<pre><code>| classification |
|
||
|---|
|
||
| Tout public |
|
||
| 13+ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je ne vois pas les contenus 16+ et 18+ dans les résultats</p>
|
||
<hr />
|
||
<h2 id="11-notification-si-tentative-dacces-a-contenu-non-autorise">11. Notification si tentative d'accès à contenu non autorisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de 14 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu "16+" est partagé avec moi via un lien direct</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder au contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'accès est refusé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Ce contenu est réservé aux utilisateurs de 16 ans et plus"</p>
|
||
<hr />
|
||
<h2 id="12-validation-obligatoire-des-3-premiers-contenus-inclut-la-classification">12. Validation obligatoire des 3 premiers contenus inclut la classification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouveau créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je publie mon premier contenu classifié "18+"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur valide mon contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il vérifie que la classification "18+" est appropriée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> peut la modifier si nécessaire avant validation</p>
|
||
<hr />
|
||
<h2 id="13-statistiques-de-classification-dans-linterface-createur">13. Statistiques de classification dans l'interface créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes contenus publiés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois pour chaque contenu:</p>
|
||
<pre><code>| information | exemple |
|
||
|---|---|
|
||
| Classification actuelle | 13+ |
|
||
| Nombre de signalements | 2 |
|
||
| Reclassifications | Aucune / 1× par modérateur |
|
||
</code></pre>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="connexion-utilisateur">Connexion utilisateur</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur existant</em>
|
||
<em>Je veux me connecter à mon compte</em>
|
||
<em>Afin d'accéder à mes contenus et paramètres</em></p>
|
||
</blockquote>
|
||
<p><strong>11 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur existe avec:
|
||
| email | mot_de_passe |
|
||
|---|---|
|
||
| user@test.fr | Password123 |</p>
|
||
</blockquote>
|
||
<h2 id="1-connexion-reussie-avec-identifiants-valides">1. Connexion réussie avec identifiants valides</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec:</p>
|
||
<pre><code>| email | mot_de_passe |
|
||
|---|---|
|
||
| user@test.fr | Password123 |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis connecté avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un access token valide pour 15 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un refresh token valide pour 30 jours</p>
|
||
<hr />
|
||
<h2 id="2-connexion-echouee-avec-email-inexistant">2. Connexion échouée avec email inexistant</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec l'email "inexistant@test.fr"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la connexion échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Email ou mot de passe incorrect"</p>
|
||
<hr />
|
||
<h2 id="3-connexion-echouee-avec-mot-de-passe-incorrect">3. Connexion échouée avec mot de passe incorrect</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec:</p>
|
||
<pre><code>| email | mot_de_passe |
|
||
|---|---|
|
||
| user@test.fr | MauvaisPass1 |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la connexion échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Email ou mot de passe incorrect"</p>
|
||
<hr />
|
||
<h2 id="4-blocage-apres-5-tentatives-echouees">4. Blocage après 5 tentatives échouées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai échoué 4 tentatives de connexion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'échoue une 5ème tentative de connexion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est temporairement bloqué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Compte bloqué pour 15 minutes après 5 tentatives échouées"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de notification de blocage</p>
|
||
<hr />
|
||
<h2 id="5-tentative-de-connexion-pendant-le-blocage">5. Tentative de connexion pendant le blocage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte est bloqué suite à 5 tentatives échouées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que seulement 5 minutes se sont écoulées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de me connecter avec les bons identifiants</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la connexion échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Compte bloqué. Réessayez dans 10 minutes"</p>
|
||
<hr />
|
||
<h2 id="6-deblocage-automatique-apres-15-minutes">6. Déblocage automatique après 15 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte est bloqué suite à 5 tentatives échouées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 15 minutes se sont écoulées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec les bons identifiants</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis connecté avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur de tentatives est réinitialisé</p>
|
||
<hr />
|
||
<h2 id="7-reset-du-compteur-apres-connexion-reussie">7. Reset du compteur après connexion réussie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai échoué 3 tentatives de connexion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec les bons identifiants</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis connecté avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur de tentatives est remis à 0</p>
|
||
<hr />
|
||
<h2 id="8-reset-automatique-du-compteur-apres-15-minutes-sans-blocage">8. Reset automatique du compteur après 15 minutes sans blocage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai échoué 3 tentatives de connexion
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 15 minutes se sont écoulées sans nouvelle tentative</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mon compteur de tentatives</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur est réinitialisé à 0</p>
|
||
<hr />
|
||
<h2 id="9-deblocage-via-lien-mot-de-passe-oublie">9. Déblocage via lien "Mot de passe oublié"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte est bloqué suite à 5 tentatives échouées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise la fonction "Mot de passe oublié"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je réinitialise mon mot de passe</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le blocage est levé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux me connecter avec le nouveau mot de passe</p>
|
||
<hr />
|
||
<h2 id="10-email-de-notification-lors-dun-blocage">10. Email de notification lors d'un blocage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai échoué 5 tentatives de connexion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email avec:</p>
|
||
<pre><code>| sujet | Tentatives de connexion suspectes détectées |
|
||
|---|---|
|
||
| contenu_contient | Votre compte a été temporairement bloqué |
|
||
| lien_mot_de_passe | présent |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-connexion-multi-device-simultanee-autorisee">11. Connexion multi-device simultanée autorisée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté sur un appareil iOS</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte également sur un appareil Android</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les deux sessions sont actives simultanément
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux utiliser l'application sur les deux appareils</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="inscription-utilisateur">Inscription utilisateur</h1>
|
||
<blockquote>
|
||
<p><em>En tant que nouvel utilisateur</em>
|
||
<em>Je veux créer un compte avec email et mot de passe</em>
|
||
<em>Afin d'accéder à l'application RoadWave</em></p>
|
||
</blockquote>
|
||
<p><strong>15 scénarios</strong> (14 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que Zitadel est configuré</p>
|
||
</blockquote>
|
||
<h2 id="1-inscription-reussie-avec-donnees-valides">1. Inscription réussie avec données valides</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'email "nouveau@example.com" n'existe pas</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec les données suivantes:</p>
|
||
<pre><code>| champ | valeur |
|
||
|---|---|
|
||
| email | nouveau@example.com |
|
||
| mot_de_passe | Password123 |
|
||
| pseudo | nouveau_user |
|
||
| date_naissance | 1995-06-15 |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est créé avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de vérification
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le lien de vérification expire dans 7 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis redirigé vers l'application</p>
|
||
<hr />
|
||
<h2 id="2-inscription-avec-email-deja-existant">2. Inscription avec email déjà existant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur existe avec l'email "existant@example.com"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec l'email "existant@example.com"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'inscription échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Cet email est déjà utilisé"</p>
|
||
<hr />
|
||
<h2 id="3-inscription-avec-mot-de-passe-invalide-trop-court">3. Inscription avec mot de passe invalide - trop court</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec un mot de passe de moins de 8 caractères "Pass1"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'inscription échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le mot de passe doit contenir au moins 8 caractères"</p>
|
||
<hr />
|
||
<h2 id="4-inscription-avec-mot-de-passe-invalide-sans-majuscule">4. Inscription avec mot de passe invalide - sans majuscule</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec un mot de passe sans majuscule "password123"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'inscription échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le mot de passe doit contenir au moins une majuscule"</p>
|
||
<hr />
|
||
<h2 id="5-inscription-avec-mot-de-passe-invalide-sans-chiffre">5. Inscription avec mot de passe invalide - sans chiffre</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec un mot de passe sans chiffre "Password"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'inscription échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le mot de passe doit contenir au moins un chiffre"</p>
|
||
<hr />
|
||
<h2 id="6-inscription-avec-pseudo-invalide-trop-court">6. Inscription avec pseudo invalide - trop court</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec un pseudo de 2 caractères "ab"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'inscription échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le pseudo doit contenir entre 3 et 30 caractères"</p>
|
||
<hr />
|
||
<h2 id="7-inscription-avec-pseudo-invalide-caracteres-speciaux">7. Inscription avec pseudo invalide - caractères spéciaux</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec un pseudo contenant des caractères spéciaux "user@123"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'inscription échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le pseudo ne peut contenir que des lettres, chiffres et underscores"</p>
|
||
<hr />
|
||
<h2 id="8-inscription-avec-email-invalide">8. Inscription avec email invalide</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec un email invalide "email.invalide"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'inscription échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Format d'email invalide"</p>
|
||
<hr />
|
||
<h2 id="9-plan-inscription-avec-age-minimum-non-respecte">9. 📋 Plan: Inscription avec âge minimum non respecté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> la date du jour est "2026-01-21"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec une date de naissance "<date_naissance>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'inscription échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vous devez avoir au moins 13 ans pour créer un compte"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>date_naissance</th>
|
||
<th>age</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>2013-01-22</td>
|
||
<td>12</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2015-06-15</td>
|
||
<td>10</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2020-01-01</td>
|
||
<td>6</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="10-inscription-avec-age-limite-acceptable-13-ans">10. Inscription avec âge limite acceptable (13 ans)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> la date du jour est "2026-01-21"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec une date de naissance "2013-01-21"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est créé avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le mode Kids est activé automatiquement</p>
|
||
<hr />
|
||
<h2 id="11-inscription-avec-age-superieur-a-18-ans">11. Inscription avec âge supérieur à 18 ans</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> la date du jour est "2026-01-21"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris avec une date de naissance "1990-06-15"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est créé avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> j'ai accès à tous les contenus sans restriction d'âge</p>
|
||
<hr />
|
||
<h2 id="12-donnees-minimales-requises-a-linscription">12. Données minimales requises à l'inscription</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris sans fournir de nom complet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sans fournir de photo de profil
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sans fournir de bio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est créé avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un avatar par défaut est généré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les champs optionnels sont vides</p>
|
||
<hr />
|
||
<h2 id="13-renvoyer-lemail-de-verification">13. Renvoyer l'email de vérification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis inscrit avec l'email "nouveau@example.com"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas vérifié mon email</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande à renvoyer l'email de vérification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un nouvel email de vérification est envoyé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le précédent lien est invalidé</p>
|
||
<hr />
|
||
<h2 id="14-limite-de-renvoi-demail-de-verification">14. Limite de renvoi d'email de vérification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis inscrit avec l'email "nouveau@example.com"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai déjà renvoyé l'email de vérification 3 fois aujourd'hui</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande à renvoyer l'email de vérification une 4ème fois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la demande échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vous avez atteint la limite de 3 renvois par jour"</p>
|
||
<hr />
|
||
<h2 id="15-expiration-du-lien-de-verification">15. Expiration du lien de vérification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis inscrit il y a 8 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas vérifié mon email</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'utiliser le lien de vérification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la vérification échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Ce lien a expiré"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux demander un nouveau lien</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="recuperation-de-compte">Récupération de compte</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur ayant oublié son mot de passe</em>
|
||
<em>Je veux pouvoir réinitialiser mon mot de passe via email</em>
|
||
<em>Afin de récupérer l'accès à mon compte</em></p>
|
||
</blockquote>
|
||
<p><strong>14 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur existe avec l'email "user@test.fr"</p>
|
||
</blockquote>
|
||
<h2 id="1-demander-la-reinitialisation-du-mot-de-passe">1. Demander la réinitialisation du mot de passe</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Mot de passe oublié"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je saisis mon email "user@test.fr"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email avec un lien de réinitialisation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le lien expire dans 1 heure
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Email de réinitialisation envoyé"</p>
|
||
<hr />
|
||
<h2 id="2-email-inexistant-lors-de-la-demande-de-reinitialisation">2. Email inexistant lors de la demande de réinitialisation</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande une réinitialisation pour l'email "inexistant@test.fr"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le même message "Email de réinitialisation envoyé"
|
||
<span style="color: #F44336"><strong>Mais</strong></span> aucun email n'est envoyé (sécurité - pas d'énumération d'emails)</p>
|
||
<hr />
|
||
<h2 id="3-reinitialiser-le-mot-de-passe-avec-un-lien-valide">3. Réinitialiser le mot de passe avec un lien valide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé une réinitialisation de mot de passe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu le lien de réinitialisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le lien
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je saisis un nouveau mot de passe "NouveauPass123"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je confirme le nouveau mot de passe "NouveauPass123"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon mot de passe est modifié avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis déconnecté de tous mes appareils sauf celui en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de confirmation de changement</p>
|
||
<hr />
|
||
<h2 id="4-lien-de-reinitialisation-expire">4. Lien de réinitialisation expiré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé une réinitialisation il y a 2 heures</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'utiliser le lien</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Ce lien a expiré"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux demander un nouveau lien</p>
|
||
<hr />
|
||
<h2 id="5-nouveau-mot-de-passe-ne-respecte-pas-les-regles">5. Nouveau mot de passe ne respecte pas les règles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un lien de réinitialisation valide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un nouveau mot de passe "faible"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la réinitialisation échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le mot de passe doit contenir au moins 8 caractères, 1 majuscule et 1 chiffre"</p>
|
||
<hr />
|
||
<h2 id="6-confirmation-du-mot-de-passe-ne-correspond-pas">6. Confirmation du mot de passe ne correspond pas</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un lien de réinitialisation valide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un nouveau mot de passe "NouveauPass123"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je confirme avec un mot de passe différent "AutrePass123"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la réinitialisation échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Les mots de passe ne correspondent pas"</p>
|
||
<hr />
|
||
<h2 id="7-limite-de-demandes-de-reinitialisation">7. Limite de demandes de réinitialisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai déjà demandé 3 réinitialisations dans la dernière heure</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande une 4ème réinitialisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la demande échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Maximum 3 demandes par heure. Réessayez plus tard."</p>
|
||
<hr />
|
||
<h2 id="8-compteur-de-demandes-se-reinitialise-apres-1-heure">8. Compteur de demandes se réinitialise après 1 heure</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé 3 réinitialisations
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 1 heure s'est écoulée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande une nouvelle réinitialisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la demande réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email avec un nouveau lien</p>
|
||
<hr />
|
||
<h2 id="9-email-de-notification-de-changement-de-mot-de-passe">9. Email de notification de changement de mot de passe</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de réinitialiser mon mot de passe</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email de confirmation avec:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="10-notification-push-si-changement-depuis-appareil-non-reconnu">10. Notification push si changement depuis appareil non reconnu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis toujours connecté depuis mon iPhone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je réinitialise mon mot de passe depuis un PC Windows</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push sur mon iPhone avec:</p>
|
||
<pre><code>| titre | Mot de passe modifié |
|
||
|---|---|
|
||
| message | Depuis Windows - Paris, France |
|
||
| action | Sécuriser le compte si ce n'est pas vous |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-deconnexion-de-tous-les-appareils-apres-reinitialisation">11. Déconnexion de tous les appareils après réinitialisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté sur 4 appareils différents
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je réinitialise mon mot de passe depuis un navigateur web</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 3 autres appareils sont déconnectés immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule la session du navigateur web reste active
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vous avez été déconnecté des autres appareils par sécurité"</p>
|
||
<hr />
|
||
<h2 id="12-lien-de-reinitialisation-invalide-si-deja-utilise">12. Lien de réinitialisation invalide si déjà utilisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai réinitialisé mon mot de passe avec un lien</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de réutiliser le même lien</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Ce lien a déjà été utilisé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux demander un nouveau lien si nécessaire</p>
|
||
<hr />
|
||
<h2 id="13-nouveau-lien-invalide-lancien">13. Nouveau lien invalide l'ancien</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé une réinitialisation et reçu un lien</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande une nouvelle réinitialisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'ancien lien est invalidé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul le nouveau lien fonctionne</p>
|
||
<hr />
|
||
<h2 id="14-reinitialisation-debloque-un-compte-bloque">14. Réinitialisation débloque un compte bloqué</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte est bloqué après 5 tentatives de connexion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je réinitialise mon mot de passe via email</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le blocage est levé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux me connecter avec le nouveau mot de passe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur de tentatives est remis à 0</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="gestion-des-sessions-et-tokens">Gestion des sessions et tokens</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur connecté</em>
|
||
<em>Je veux que mes sessions soient sécurisées et gérées automatiquement</em>
|
||
<em>Afin de maintenir l'accès à l'application sans friction</em></p>
|
||
</blockquote>
|
||
<p><strong>13 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté avec succès</p>
|
||
</blockquote>
|
||
<h2 id="1-access-token-expire-apres-15-minutes">1. Access token expire après 15 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu un access token
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 15 minutes se sont écoulées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je fais une requête API avec cet access token</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la requête échoue avec le code 401
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Token expiré"</p>
|
||
<hr />
|
||
<h2 id="2-refresh-automatique-du-token-avec-refresh-token">2. Refresh automatique du token avec refresh token</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon access token a expiré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon refresh token est valide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application demande un nouveau access token</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un nouvel access token valide pour 15 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un nouveau refresh token (rotation)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'ancien refresh token est invalidé</p>
|
||
<hr />
|
||
<h2 id="3-refresh-token-expire-apres-30-jours-dinactivite">3. Refresh token expire après 30 jours d'inactivité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis connecté il y a 30 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas utilisé l'application depuis</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'utiliser mon refresh token</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la requête échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois me reconnecter avec email/password</p>
|
||
<hr />
|
||
<h2 id="4-prolongation-automatique-de-la-session-si-lapp-est-utilisee">4. Prolongation automatique de la session si l'app est utilisée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis connecté il y a 25 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'utilise l'application régulièrement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je fais une requête API</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma session est automatiquement prolongée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon refresh token reste valide</p>
|
||
<hr />
|
||
<h2 id="5-detection-de-token-replay-attack">5. Détection de token replay attack</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai rafraîchi mon token
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu un nouveau refresh token</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de réutiliser l'ancien refresh token</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la requête échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Token invalide ou révoqué"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes mes sessions sont révoquées par sécurité</p>
|
||
<hr />
|
||
<h2 id="6-voir-la-liste-des-appareils-connectes">6. Voir la liste des appareils connectés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté sur 3 appareils différents</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la liste de mes appareils connectés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois 3 appareils avec les informations suivantes:</p>
|
||
<pre><code>| information | exemple |
|
||
|---|---|
|
||
| OS | iOS 17.1 |
|
||
| Navigateur | Safari |
|
||
| Dernière connexion | Il y a 2 heures |
|
||
| Localisation | Paris, France (IP visible) |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="7-revoquer-un-appareil-specifique">7. Révoquer un appareil spécifique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté sur mon iPhone et mon iPad</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je révoque la session de mon iPad depuis les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la session iPad est immédiatement déconnectée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma session iPhone reste active</p>
|
||
<hr />
|
||
<h2 id="8-deconnecter-tous-les-appareils-sauf-celui-en-cours">8. Déconnecter tous les appareils sauf celui en cours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté sur 4 appareils</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Déconnecter tous les appareils"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 3 autres appareils sont déconnectés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul l'appareil actuel reste connecté</p>
|
||
<hr />
|
||
<h2 id="9-alerte-de-connexion-depuis-nouveau-device">9. Alerte de connexion depuis nouveau device</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis toujours connecté depuis Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte depuis un nouvel appareil à Lyon</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push sur mes autres appareils
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email avec:</p>
|
||
<pre><code>| sujet | Nouvelle connexion détectée |
|
||
|---|---|
|
||
| localisation | Lyon, France |
|
||
| appareil | Android 14 - Chrome |
|
||
| action | Lien pour révoquer la session |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="10-alerte-de-connexion-suspecte-depuis-pays-different">10. Alerte de connexion suspecte depuis pays différent</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis toujours connecté depuis la France</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte depuis un appareil aux États-Unis</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push immédiate
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email d'alerte de sécurité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la nouvelle session nécessite une validation 2FA même si désactivée</p>
|
||
<hr />
|
||
<h2 id="11-deconnexion-apres-30-jours-dinactivite-totale">11. Déconnexion après 30 jours d'inactivité totale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je ne me suis pas connecté depuis 30 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis automatiquement déconnecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois me reconnecter avec email/password
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Session expirée après 30 jours d'inactivité"</p>
|
||
<hr />
|
||
<h2 id="12-sessions-multiples-simultanees-autorisees">12. Sessions multiples simultanées autorisées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté sur:</p>
|
||
<pre><code>| appareil |
|
||
|---|
|
||
| iPhone |
|
||
| iPad |
|
||
| PC Windows (Web) |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je fais des actions sur les 3 appareils simultanément</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les sessions fonctionnent sans conflit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque appareil maintient sa propre session</p>
|
||
<hr />
|
||
<h2 id="13-validation-de-jwt-via-zitadel">13. Validation de JWT via Zitadel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu un access token JWT</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'API RoadWave valide le token</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validation est faite localement avec la clé publique Zitadel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune requête externe n'est effectuée (performance)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le token contient les claims suivants:</p>
|
||
<pre><code>| claim | valeur_exemple |
|
||
|---|---|
|
||
| sub | user-id-123 |
|
||
| email | user@test.fr |
|
||
| exp | timestamp + 15 minutes |
|
||
| iss | zitadel.roadwave.com |
|
||
</code></pre>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="authentification-a-deux-facteurs-2fa">Authentification à deux facteurs (2FA)</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur soucieux de sécurité</em>
|
||
<em>Je veux activer la 2FA sur mon compte</em>
|
||
<em>Afin de protéger mon accès même si mon mot de passe est compromis</em></p>
|
||
</blockquote>
|
||
<p><strong>16 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté à mon compte</p>
|
||
</blockquote>
|
||
<h2 id="1-activer-la-2fa-totp-time-based-one-time-password">1. Activer la 2FA TOTP (Time-based One-Time Password)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA n'est pas activée sur mon compte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je choisis d'activer la 2FA TOTP</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un QR code à scanner
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le secret partagé en texte clair (backup)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois entrer un code de vérification depuis mon app authenticator</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un code TOTP valide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la 2FA TOTP est activée avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois des codes de backup (10 codes)</p>
|
||
<hr />
|
||
<h2 id="2-connexion-avec-2fa-totp-activee">2. Connexion avec 2FA TOTP activée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA TOTP est activée sur mon compte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec email/password</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers la page de saisie du code 2FA</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un code TOTP valide de mon authenticator</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis connecté avec succès</p>
|
||
<hr />
|
||
<h2 id="3-connexion-echouee-avec-code-totp-invalide">3. Connexion échouée avec code TOTP invalide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA TOTP est activée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec email/password
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je saisis un code TOTP invalide "000000"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la connexion échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Code d'authentification invalide"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux réessayer</p>
|
||
<hr />
|
||
<h2 id="4-utiliser-un-code-de-backup-pour-2fa">4. Utiliser un code de backup pour 2FA</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA TOTP est activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai perdu l'accès à mon authenticator</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec email/password
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Utiliser un code de backup"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je saisis un code de backup valide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis connecté avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le code de backup utilisé est invalidé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il me reste 9 codes de backup</p>
|
||
<hr />
|
||
<h2 id="5-activer-la-2fa-par-email">5. Activer la 2FA par email</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA n'est pas activée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je choisis d'activer la 2FA par email</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la 2FA email est activée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "2FA email activée. Vous recevrez un code à chaque connexion"</p>
|
||
<hr />
|
||
<h2 id="6-connexion-avec-2fa-email">6. Connexion avec 2FA email</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA email est activée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec email/password</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email avec un code à 6 chiffres
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le code expire dans 10 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois saisir ce code pour terminer la connexion</p>
|
||
<hr />
|
||
<h2 id="7-code-2fa-email-expire">7. Code 2FA email expiré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA email est activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je me suis connecté avec email/password
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu un code 2FA par email il y a 11 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis ce code</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la connexion échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Code expiré. Demandez un nouveau code."</p>
|
||
<hr />
|
||
<h2 id="8-renvoyer-le-code-2fa-email">8. Renvoyer le code 2FA email</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA email est activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis sur la page de saisie du code 2FA</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Renvoyer le code"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un nouveau code par email
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'ancien code est invalidé</p>
|
||
<hr />
|
||
<h2 id="9-ajouter-un-appareil-de-confiance-skip-2fa-pendant-30-jours">9. Ajouter un appareil de confiance (skip 2FA pendant 30 jours)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA TOTP est activée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte avec email/password et code TOTP
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je coche "Ne plus demander sur cet appareil"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis connecté avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cet appareil est enregistré comme "appareil de confiance"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me reconnecte dans les 30 jours suivants sur ce même appareil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne dois pas saisir de code 2FA</p>
|
||
<hr />
|
||
<h2 id="10-appareil-de-confiance-expire-apres-30-jours">10. Appareil de confiance expire après 30 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai enregistré un appareil de confiance il y a 31 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte depuis cet appareil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois saisir un code 2FA
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Appareil de confiance expiré. Veuillez vous authentifier"</p>
|
||
<hr />
|
||
<h2 id="11-voir-la-liste-des-appareils-de-confiance">11. Voir la liste des appareils de confiance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai enregistré 3 appareils de confiance</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes paramètres de sécurité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste de mes 3 appareils de confiance avec:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="12-revoquer-un-appareil-de-confiance">12. Révoquer un appareil de confiance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un iPhone enregistré comme appareil de confiance</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je révoque cet appareil depuis les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'appareil est supprimé de la liste</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me reconnecte depuis cet iPhone</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois saisir un code 2FA</p>
|
||
<hr />
|
||
<h2 id="13-revoquer-tous-les-appareils-de-confiance">13. Révoquer tous les appareils de confiance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 5 appareils de confiance enregistrés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Révoquer tous les appareils de confiance"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les appareils sont révoqués
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Tous les appareils de confiance ont été révoqués"</p>
|
||
<hr />
|
||
<h2 id="14-2fa-forcee-pour-connexion-suspecte-malgre-appareil-de-confiance">14. 2FA forcée pour connexion suspecte malgré appareil de confiance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un appareil de confiance enregistré en France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je me connecte depuis ce même appareil mais avec une IP américaine</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tente de me connecter</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la 2FA est requise malgré l'appareil de confiance
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Connexion suspecte détectée. Authentification requise."</p>
|
||
<hr />
|
||
<h2 id="15-desactiver-la-2fa">15. Désactiver la 2FA</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA TOTP est activée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive la 2FA depuis mes paramètres
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je confirme avec mon mot de passe</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la 2FA est désactivée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les codes de backup sont invalidés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les appareils de confiance sont révoqués</p>
|
||
<hr />
|
||
<h2 id="16-regenerer-les-codes-de-backup">16. Régénérer les codes de backup</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la 2FA est activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai utilisé 8 codes de backup sur 10</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande à régénérer les codes de backup</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois 10 nouveaux codes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les anciens codes (utilisés ou non) sont invalidés</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="verification-demail">Vérification d'email</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur inscrit</em>
|
||
<em>Je veux vérifier mon adresse email</em>
|
||
<em>Afin d'accéder à toutes les fonctionnalités selon mon rôle</em></p>
|
||
</blockquote>
|
||
<p><strong>10 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-auditeur-avec-email-non-verifie-lecture-illimitee">1. Auditeur avec email non vérifié - lecture illimitée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un auditeur avec email non vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'écouter du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux écouter tous les contenus sans limite</p>
|
||
<hr />
|
||
<h2 id="2-auditeur-avec-email-non-verifie-creation-limitee-a-5-contenus">2. Auditeur avec email non vérifié - création limitée à 5 contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un auditeur avec email non vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai créé 4 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée un 5ème contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est créé avec succès
|
||
<span style="color: #F44336"><strong>Mais</strong></span> quand j'essaie de créer un 6ème contenu
|
||
<span style="color: #4CAF50"><strong>Alors</strong></span> la création échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vérifiez votre email pour créer plus de contenus"</p>
|
||
<hr />
|
||
<h2 id="3-rappel-de-verification-apres-le-3eme-contenu-cree">3. Rappel de vérification après le 3ème contenu créé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un auditeur avec email non vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai créé 2 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée mon 3ème contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est créé avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois une notification in-app "Vérifiez votre email pour débloquer la création illimitée"</p>
|
||
<hr />
|
||
<h2 id="4-auditeur-verifie-son-email">4. Auditeur vérifie son email</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un auditeur avec email non vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu un lien de vérification</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le lien de vérification dans l'email</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon email est marqué comme vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Email vérifié avec succès"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les fonctionnalités sont débloquées</p>
|
||
<hr />
|
||
<h2 id="5-createur-doit-verifier-son-email-sous-7-jours-pour-monetisation">5. Créateur doit vérifier son email sous 7 jours pour monétisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis inscrit comme créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon email n'est pas vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je remplis les conditions de monétisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder au programme de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'accès est refusé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vérifiez votre email pour accéder à la monétisation"</p>
|
||
<hr />
|
||
<h2 id="6-createur-ne-peut-pas-publier-de-contenus-illimites-sans-verification">6. Créateur ne peut pas publier de contenus illimités sans vérification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec email non vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai créé 5 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de créer un 6ème contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la création échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vérifiez votre email pour publier des contenus illimités"</p>
|
||
<hr />
|
||
<h2 id="7-createur-verifie-son-email-et-deboque-tout">7. Créateur vérifie son email et déboque tout</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec email non vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu un lien de vérification</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le lien de vérification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon email est marqué comme vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux publier des contenus illimités
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accéder au programme de monétisation si j'en remplis les conditions</p>
|
||
<hr />
|
||
<h2 id="8-kyc-impossible-sans-email-verifie">8. KYC impossible sans email vérifié</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec email non vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de compléter le KYC via Mangopay</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'accès au KYC est refusé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vérifiez votre email avant de procéder au KYC"</p>
|
||
<hr />
|
||
<h2 id="9-tentative-de-verification-avec-un-lien-deja-utilise">9. Tentative de vérification avec un lien déjà utilisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai déjà vérifié mon email avec un lien</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de réutiliser le même lien de vérification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la vérification échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Ce lien a déjà été utilisé"</p>
|
||
<hr />
|
||
<h2 id="10-auditeur-verifie-peut-creer-plus-de-5-contenus">10. Auditeur vérifié peut créer plus de 5 contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un auditeur avec email vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai créé 10 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée un 11ème contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est créé avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de limite de création</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="metadonnees-et-publication-de-contenu">Métadonnées et publication de contenu</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur</em>
|
||
<em>Je veux remplir les métadonnées de mon contenu</em>
|
||
<em>Afin de le publier sur RoadWave</em></p>
|
||
</blockquote>
|
||
<p><strong>34 scénarios</strong> (32 standards, 2 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis un créateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon fichier audio est encodé et prêt</p>
|
||
</blockquote>
|
||
<h2 id="1-publication-avec-toutes-les-metadonnees-obligatoires">1. Publication avec toutes les métadonnées obligatoires</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je remplis les métadonnées suivantes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publication réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon contenu est soumis pour validation</p>
|
||
<hr />
|
||
<h2 id="2-titre-valide-entre-5-et-100-caracteres">2. Titre valide entre 5 et 100 caractères</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un titre de 50 caractères</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le titre est accepté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la validation passe</p>
|
||
<hr />
|
||
<h2 id="3-titre-trop-court-5-caracteres">3. Titre trop court (<5 caractères)</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un titre de 4 caractères "Test"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publication échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le titre doit contenir entre 5 et 100 caractères"</p>
|
||
<hr />
|
||
<h2 id="4-titre-trop-long-100-caracteres">4. Titre trop long (>100 caractères)</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un titre de 101 caractères</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publication échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le titre doit contenir entre 5 et 100 caractères"</p>
|
||
<hr />
|
||
<h2 id="5-titre-a-exactement-5-caracteres-accepte">5. Titre à exactement 5 caractères accepté</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un titre de exactement 5 caractères "Titre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le titre est accepté</p>
|
||
<hr />
|
||
<h2 id="6-titre-a-exactement-100-caracteres-accepte">6. Titre à exactement 100 caractères accepté</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un titre de exactement 100 caractères</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le titre est accepté</p>
|
||
<hr />
|
||
<h2 id="7-selectionner-type-geo-ancre">7. Sélectionner type géo "Ancré"</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne le type géo "Ancré"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système applique une pondération géo de 0.7
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois définir une zone de diffusion précise</p>
|
||
<hr />
|
||
<h2 id="8-selectionner-type-geo-contextuel">8. Sélectionner type géo "Contextuel"</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne le type géo "Contextuel"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système applique une pondération géo de 0.5
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux définir une zone ville/département/région</p>
|
||
<hr />
|
||
<h2 id="9-selectionner-type-geo-neutre">9. Sélectionner type géo "Neutre"</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne le type géo "Neutre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système applique une pondération géo de 0.2
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux définir une zone nationale</p>
|
||
<hr />
|
||
<h2 id="10-zone-diffusion-point-gps-avec-rayon">10. Zone diffusion - Point GPS avec rayon</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je choisis "Point GPS"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je définis les coordonnées (48.8584, 2.2945)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je définis un rayon de 500 mètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la zone est validée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu sera diffusé dans un rayon de 500m autour du point</p>
|
||
<hr />
|
||
<h2 id="11-zone-diffusion-rayon-minimum-100m">11. Zone diffusion - Rayon minimum 100m</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis un rayon de 50 mètres (< 100m)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validation échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le rayon doit être entre 100m et 10km"</p>
|
||
<hr />
|
||
<h2 id="12-zone-diffusion-rayon-maximum-10km">12. Zone diffusion - Rayon maximum 10km</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis un rayon de 15 km (> 10km)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validation échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le rayon doit être entre 100m et 10km"</p>
|
||
<hr />
|
||
<h2 id="13-zone-diffusion-ville-depuis-referentiel-insee">13. Zone diffusion - Ville depuis référentiel INSEE</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je choisis "Ville"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une liste de villes du référentiel INSEE</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Paris (75000)"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la zone est définie sur toute la ville de Paris</p>
|
||
<hr />
|
||
<h2 id="14-zone-diffusion-departement">14. Zone diffusion - Département</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je choisis "Département"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je sélectionne "Ille-et-Vilaine (35)"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la zone couvre tout le département 35</p>
|
||
<hr />
|
||
<h2 id="15-zone-diffusion-region">15. Zone diffusion - Région</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je choisis "Région"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je sélectionne "Bretagne"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la zone couvre toute la région Bretagne</p>
|
||
<hr />
|
||
<h2 id="16-zone-diffusion-national">16. Zone diffusion - National</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je choisis "National"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la zone couvre toute la France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune restriction géographique n'est appliquée</p>
|
||
<hr />
|
||
<h2 id="17-zones-mutuellement-exclusives">17. Zones mutuellement exclusives</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai sélectionné "Point GPS"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de sélectionner également "Ville"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la première sélection est remplacée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule "Ville" reste active</p>
|
||
<hr />
|
||
<h2 id="18-selectionner-1-tag-minimum">18. Sélectionner 1 tag minimum</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne 1 tag "Voyage"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validation passe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est tagué "Voyage"</p>
|
||
<hr />
|
||
<h2 id="19-selectionner-3-tags-maximum">19. Sélectionner 3 tags maximum</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne 3 tags "Automobile", "Technologie", "Sport"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validation passe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est tagué avec les 3 tags</p>
|
||
<hr />
|
||
<h2 id="20-impossible-de-selectionner-0-tag">20. Impossible de sélectionner 0 tag</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de publier sans sélectionner de tag</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publication échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vous devez sélectionner entre 1 et 3 tags"</p>
|
||
<hr />
|
||
<h2 id="21-impossible-de-selectionner-4-tags">21. Impossible de sélectionner 4 tags</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de sélectionner 4 tags</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le 4ème tag ne peut pas être ajouté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Maximum 3 tags"</p>
|
||
<hr />
|
||
<h2 id="22-tags-disponibles-dans-la-liste">22. Tags disponibles dans la liste</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la liste des tags</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les tags suivants:</p>
|
||
<pre><code>| tag |
|
||
|---|
|
||
| Automobile |
|
||
| Voyage |
|
||
| Famille |
|
||
| Amour |
|
||
| Musique |
|
||
| Économie |
|
||
| Cryptomonnaie |
|
||
| Politique |
|
||
| Culture générale |
|
||
| Sport |
|
||
| Technologie |
|
||
| Santé |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="23-classification-age-obligatoire">23. Classification âge obligatoire</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de publier sans classification âge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publication échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vous devez sélectionner une classification d'âge"</p>
|
||
<hr />
|
||
<h2 id="24-plan-selectionner-classification-age">24. 📋 Plan: Sélectionner classification âge</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne la classification "<classification>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu sera visible pour "<public_cible>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>classification</th>
|
||
<th>public_cible</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Tout public</td>
|
||
<td>Tous les utilisateurs</td>
|
||
</tr>
|
||
<tr>
|
||
<td>13+</td>
|
||
<td>Utilisateurs 13 ans et plus</td>
|
||
</tr>
|
||
<tr>
|
||
<td>16+</td>
|
||
<td>Utilisateurs 16 ans et plus</td>
|
||
</tr>
|
||
<tr>
|
||
<td>18+</td>
|
||
<td>Utilisateurs 18 ans et plus</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="25-image-de-couverture-auto-generee-selon-type-geo">25. Image de couverture auto-générée selon type géo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je choisis le type géo "Ancré"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon tag principal est "Voyage"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la publication est soumise</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une image de couverture est générée automatiquement:</p>
|
||
<pre><code>| paramètre | valeur |
|
||
|---|---|
|
||
| Icône | 📍 (Ancré) |
|
||
| Couleur | Bleu-vert (Voyage) |
|
||
| Format | 800×800px PNG |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="26-image-de-couverture-type-contextuel">26. Image de couverture type Contextuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je choisis "Contextuel"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'image est générée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'icône est 🌍 (Contextuel)</p>
|
||
<hr />
|
||
<h2 id="27-image-de-couverture-type-neutre">27. Image de couverture type Neutre</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je choisis "Neutre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'image est générée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'icône est 🎧 (Neutre)</p>
|
||
<hr />
|
||
<h2 id="28-plan-couleur-selon-tag-principal">28. 📋 Plan: Couleur selon tag principal</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon tag principal est "<tag>"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'image est générée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la couleur de fond est "<couleur>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>tag</th>
|
||
<th>couleur</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Automobile</td>
|
||
<td>Bleu</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Voyage</td>
|
||
<td>Vert</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Musique</td>
|
||
<td>Rouge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Économie</td>
|
||
<td>Gris</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Sport</td>
|
||
<td>Orange</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="29-champs-optionnels-non-obligatoires">29. Champs optionnels non obligatoires</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie sans description
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sans image de couverture personnalisée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publication réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les champs optionnels restent vides
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une image par défaut est générée</p>
|
||
<hr />
|
||
<h2 id="30-temps-de-publication-estime-2-minutes">30. Temps de publication estimé 2 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon fichier audio est prêt</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je commence à remplir les métadonnées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux publier en environ 2 minutes</p>
|
||
<hr />
|
||
<h2 id="31-publication-rapide-sans-friction">31. Publication rapide sans friction</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie mon premier contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun champ complexe n'est demandé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne suis pas bloqué sur description ou image
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publication est fluide</p>
|
||
<hr />
|
||
<h2 id="32-previsualisation-avant-publication">32. Prévisualisation avant publication</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai rempli toutes les métadonnées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Prévisualiser"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un aperçu de mon contenu:</p>
|
||
<pre><code>| élément | affiché |
|
||
|---|---|
|
||
| Titre | ✅ |
|
||
| Image couverture | ✅ |
|
||
| Tags | ✅ |
|
||
| Zone diffusion | ✅ |
|
||
| Durée audio | ✅ |
|
||
| Classification | ✅ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="33-enregistrer-brouillon">33. Enregistrer brouillon</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai commencé à remplir les métadonnées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Enregistrer brouillon"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes métadonnées sont sauvegardées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux reprendre la publication plus tard</p>
|
||
<hr />
|
||
<h2 id="34-reprendre-brouillon">34. Reprendre brouillon</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un brouillon sauvegardé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mes contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le brouillon avec statut "📝 Brouillon"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux reprendre la publication</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="modification-et-suppression-de-contenu">Modification et suppression de contenu</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur</em>
|
||
<em>Je veux pouvoir modifier ou supprimer mes contenus</em>
|
||
<em>Afin de garder le contrôle sur mes publications</em></p>
|
||
</blockquote>
|
||
<p><strong>30 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis un créateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai publié un contenu</p>
|
||
</blockquote>
|
||
<h2 id="1-modifier-le-titre-dun-contenu">1. Modifier le titre d'un contenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu a le titre "Histoire de Paris"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie le titre en "Histoire fascinante de Paris"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est enregistrée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Titre modifié avec succès"</p>
|
||
<hr />
|
||
<h2 id="2-correction-de-coquilles-dans-le-titre">2. Correction de coquilles dans le titre</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon titre contient une faute "Histoore de Paris"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je corrige en "Histoire de Paris"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est acceptée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le titre corrigé est affiché</p>
|
||
<hr />
|
||
<h2 id="3-ajouter-une-description-ulterieurement">3. Ajouter une description ultérieurement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié sans description</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ajoute une description "Découvrez l'histoire de la capitale"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la description est enregistrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est visible sur la page du contenu</p>
|
||
<hr />
|
||
<h2 id="4-modifier-la-description-existante">4. Modifier la description existante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu a déjà une description</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie la description</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la nouvelle description remplace l'ancienne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la modification est immédiate</p>
|
||
<hr />
|
||
<h2 id="5-modifier-les-tags-pour-ajuster-pertinence">5. Modifier les tags pour ajuster pertinence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est tagué "Sport", "Musique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je change les tags en "Sport", "Santé"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les nouveaux tags sont appliqués
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme utilise les nouveaux tags pour recommandations</p>
|
||
<hr />
|
||
<h2 id="6-personnaliser-limage-de-couverture">6. Personnaliser l'image de couverture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu a une image auto-générée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'uploade une image personnalisée 800×800px</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'image personnalisée remplace l'image par défaut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est visible sur le contenu</p>
|
||
<hr />
|
||
<h2 id="7-impossible-de-modifier-laudio">7. Impossible de modifier l'audio</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu audio est publié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de remplacer le fichier audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est refusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "L'audio ne peut pas être modifié après publication"</p>
|
||
<hr />
|
||
<h2 id="8-raison-eviter-fraude-audio">8. Raison - Éviter fraude audio</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux changer l'audio après validation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système refuse pour éviter:</p>
|
||
<pre><code>| risque |
|
||
|---|
|
||
| Uploader contenu validé puis remplacer spam |
|
||
| Fraude sur l'intégrité du contenu |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-impossible-de-modifier-la-zone-de-diffusion">9. Impossible de modifier la zone de diffusion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est diffusé à Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de changer la zone en "National"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est refusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "La zone de diffusion ne peut pas être modifiée"</p>
|
||
<hr />
|
||
<h2 id="10-raison-eviter-manipulation-algorithme">10. Raison - Éviter manipulation algorithme</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux changer ma zone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système refuse pour éviter:</p>
|
||
<pre><code>| manipulation |
|
||
|---|
|
||
| Créer "Local Paris" puis changer en "National" |
|
||
| Boost artificiel de visibilité |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-impossible-de-modifier-le-type-geo">11. Impossible de modifier le type géo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est type "Neutre" (pondération 0.2)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de changer en "Ancré" (pondération 0.7)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est refusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le type géographique ne peut pas être modifié"</p>
|
||
<hr />
|
||
<h2 id="12-raison-eviter-abus-de-ponderation">12. Raison - Éviter abus de pondération</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux changer le type géo</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système refuse pour éviter:</p>
|
||
<pre><code>| abus |
|
||
|---|
|
||
| Créer "Neutre" puis passer en "Ancré" |
|
||
| Manipulation de la pondération algorithme |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="13-impossible-de-modifier-la-classification-age">13. Impossible de modifier la classification âge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est classé "Tout public"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de changer en "18+"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est refusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "La classification d'âge ne peut pas être modifiée"</p>
|
||
<hr />
|
||
<h2 id="14-raison-securite-mineurs">14. Raison - Sécurité mineurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux changer la classification</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système refuse pour garantir:</p>
|
||
<pre><code>| protection |
|
||
|---|
|
||
| Classification vérifiée en modération |
|
||
| Pas de contournement validation |
|
||
| Sécurité des mineurs |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="15-solution-si-besoin-de-changer-audiozoneclassification">15. Solution si besoin de changer audio/zone/classification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux absolument changer l'audio</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les options</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "Supprimer et republier le contenu"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> c'est la seule solution disponible</p>
|
||
<hr />
|
||
<h2 id="16-republication-apres-suppression-createur-3-validations">16. Republication après suppression - créateur <3 validations</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouveau créateur (2 contenus validés)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je supprime puis republie un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je republie avec les modifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu repasse en file de validation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une nouvelle validation est effectuée</p>
|
||
<hr />
|
||
<h2 id="17-republication-apres-suppression-createur-verifie">17. Republication après suppression - créateur vérifié</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur vérifié (≥3 contenus validés)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je supprime puis republie un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je republie avec les modifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est publié immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune validation préalable n'est requise</p>
|
||
<hr />
|
||
<h2 id="18-suppression-de-contenu-immediate">18. Suppression de contenu immédiate</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Supprimer le contenu"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je confirme la suppression</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est supprimé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> disparaît de la liste publique</p>
|
||
<hr />
|
||
<h2 id="19-confirmation-avant-suppression">19. Confirmation avant suppression</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Supprimer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un message de confirmation:</p>
|
||
<pre><code>| titre | Êtes-vous sûr ? |
|
||
|---|---|
|
||
| message | Cette action est définitive |
|
||
| warning | Le contenu sera supprimé définitivement |
|
||
| actions | Confirmer / Annuler |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="20-suppression-definitive-et-non-reversible">20. Suppression définitive et non réversible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai supprimé un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de le récupérer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la récupération est impossible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est définitivement perdu</p>
|
||
<hr />
|
||
<h2 id="21-suppression-bdd-cdn-sous-5-minutes">21. Suppression BDD + CDN sous 5 minutes</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je supprime un contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'entrée en base de données est marquée "deleted"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fichiers CDN sont marqués pour suppression
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la suppression effective a lieu sous 5 minutes</p>
|
||
<hr />
|
||
<h2 id="22-historique-auditeurs-conserve-anonymise">22. Historique auditeurs conservé anonymisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 1000 personnes ont écouté mon contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je supprime le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> leur historique est conservé
|
||
<span style="color: #F44336"><strong>Mais</strong></span> marqué "Contenu supprimé par créateur"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la durée d'écoute est conservée pour leurs stats</p>
|
||
<hr />
|
||
<h2 id="23-analytics-plateforme-anonymisees-conservees">23. Analytics plateforme anonymisées conservées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu a généré 10K écoutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je supprime le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métriques globales sont conservées anonymement:</p>
|
||
<pre><code>| métrique | conservée |
|
||
|---|---|
|
||
| Total écoutes | ✅ (anonyme) |
|
||
| Durée totale | ✅ (anonyme) |
|
||
| Catégorie | ✅ (anonyme) |
|
||
| Auteur | ❌ (anonymisé) |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> c'est conforme RGPD</p>
|
||
<hr />
|
||
<h2 id="24-fichiers-cdn-supprimes-sous-24h">24. Fichiers CDN supprimés sous 24h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est supprimé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 24 heures s'écoulent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les fichiers audio sont purgés du CDN Bunny
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace de stockage est libéré</p>
|
||
<hr />
|
||
<h2 id="25-pas-de-notification-aux-auditeurs">25. Pas de notification aux auditeurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 500 utilisateurs ont écouté mon contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je supprime le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification n'est envoyée aux auditeurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas d'effet Streisand</p>
|
||
<hr />
|
||
<h2 id="26-auditeur-tente-de-reecouter-contenu-supprime">26. Auditeur tente de réécouter contenu supprimé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un auditeur a écouté mon contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai supprimé ce contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'auditeur tente de le réécouter depuis son historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit le message "Ce contenu n'est plus disponible"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture est impossible</p>
|
||
<hr />
|
||
<h2 id="27-historique-auditeur-conserve-trace">27. Historique auditeur conserve trace</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un auditeur a écouté mon contenu le 15 janvier
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je supprime le contenu le 20 janvier</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'auditeur consulte son historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit "Vous avez écouté ce contenu le 15 janvier 2026"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le titre est remplacé par "Contenu supprimé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la date d'écoute est conservée</p>
|
||
<hr />
|
||
<h2 id="28-statistiques-createur-apres-suppression">28. Statistiques créateur après suppression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié 10 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je supprime 2 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques globales</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| métrique | valeur |
|
||
|---|---|
|
||
| Contenus publiés | 8 (actifs) |
|
||
| Total historique | 10 |
|
||
| Suppressions | 2 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> l'historique des suppressions est visible</p>
|
||
<hr />
|
||
<h2 id="29-limite-de-modifications-par-contenu">29. Limite de modifications par contenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai modifié un titre 10 fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier une 11ème fois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est acceptée</p>
|
||
<hr />
|
||
<h2 id="30-historique-des-modifications-visible">30. Historique des modifications visible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai modifié un contenu plusieurs fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| date | modification |
|
||
|---|---|
|
||
| 21/01/2026 | Titre changé |
|
||
| 20/01/2026 | Tags modifiés |
|
||
| 19/01/2026 | Description ajoutée |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux tracer toutes les modifications</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="upload-et-encodage-de-contenu-audio">Upload et encodage de contenu audio</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur</em>
|
||
<em>Je veux uploader mon contenu audio</em>
|
||
<em>Afin qu'il soit encodé et disponible pour les auditeurs</em></p>
|
||
</blockquote>
|
||
<p><strong>29 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis un créateur connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-upload-fichier-mp3-valide">1. Upload fichier MP3 valide</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'uploade un fichier MP3 de 50 MB et 30 minutes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier est envoyé vers Bunny Storage temporaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un job d'encodage asynchrone est lancé</p>
|
||
<hr />
|
||
<h2 id="2-upload-fichier-aac-valide-aac">2. Upload fichier AAC valide (.aac)</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'uploade un fichier AAC de 80 MB et 1 heure</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier est accepté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'encodage démarre</p>
|
||
<hr />
|
||
<h2 id="3-upload-fichier-m4a-valide">3. Upload fichier M4A valide</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'uploade un fichier M4A de 100 MB et 2 heures</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier est traité comme AAC
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'encodage démarre</p>
|
||
<hr />
|
||
<h2 id="4-rejet-fichier-wav-non-supporte">4. Rejet fichier WAV (non supporté)</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'uploader un fichier WAV</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)"</p>
|
||
<hr />
|
||
<h2 id="5-rejet-fichier-flac-non-supporte">5. Rejet fichier FLAC (non supporté)</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'uploader un fichier FLAC</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Format non supporté. Utilisez MP3 ou AAC (.mp3, .aac, .m4a)"</p>
|
||
<hr />
|
||
<h2 id="6-validation-taille-maximale-200-mb">6. Validation taille maximale 200 MB</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'uploader un fichier MP3 de 201 MB</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Fichier trop volumineux (max 200 MB)"</p>
|
||
<hr />
|
||
<h2 id="7-upload-a-la-limite-de-200-mb-accepte">7. Upload à la limite de 200 MB accepté</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'uploade un fichier MP3 de exactement 200 MB</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier est accepté</p>
|
||
<hr />
|
||
<h2 id="8-validation-duree-maximale-4-heures">8. Validation durée maximale 4 heures</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'uploader un fichier de 4h 10min</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Durée trop longue (max 4 heures)"</p>
|
||
<hr />
|
||
<h2 id="9-upload-a-la-limite-de-4h-accepte">9. Upload à la limite de 4h accepté</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'uploade un fichier de exactement 4 heures</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'upload réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier est accepté</p>
|
||
<hr />
|
||
<h2 id="10-validation-format-cote-client">10. Validation format côté client</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne un fichier dans l'interface</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validation du format est faite immédiatement côté client
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis informé avant même de lancer l'upload si le format est invalide</p>
|
||
<hr />
|
||
<h2 id="11-double-validation-cote-backend">11. Double validation côté backend</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un fichier a passé la validation client</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le backend reçoit le fichier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une validation supplémentaire est effectuée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le format et l'intégrité sont vérifiés</p>
|
||
<hr />
|
||
<h2 id="12-pipeline-dencodage-etape-1-upload">12. Pipeline d'encodage - étape 1 upload</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'uploade un fichier MP3 valide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est stocké temporairement dans Bunny Storage
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un job d'encodage est mis en file d'attente</p>
|
||
<hr />
|
||
<h2 id="13-pipeline-dencodage-validation-format">13. Pipeline d'encodage - validation format</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un job d'encodage est lancé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le worker Go traite le fichier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le format est validé avec FFmpeg
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'intégrité du fichier est vérifiée</p>
|
||
<hr />
|
||
<h2 id="14-pipeline-dencodage-generation-3-profils-opus">14. Pipeline d'encodage - génération 3 profils Opus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un fichier audio est validé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'encodage démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 3 profils Opus sont générés:</p>
|
||
<pre><code>| qualité | bitrate | usage |
|
||
|---|---|---|
|
||
| Basse | 24 kbps | 2G/Edge |
|
||
| Standard | 48 kbps | 3G |
|
||
| Haute | 64 kbps | 4G/5G |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="15-pipeline-dencodage-generation-segments-hls">15. Pipeline d'encodage - génération segments HLS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les profils Opus sont générés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'encodage continue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un fichier manifest .m3u8 est créé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> des segments .ts sont générés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est prêt pour streaming HLS</p>
|
||
<hr />
|
||
<h2 id="16-pipeline-dencodage-generation-image-par-defaut">16. Pipeline d'encodage - génération image par défaut</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'encodage est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les métadonnées sont traitées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une image de couverture par défaut est générée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'image fait 800×800px au format PNG</p>
|
||
<hr />
|
||
<h2 id="17-pipeline-dencodage-suppression-fichier-original">17. Pipeline d'encodage - suppression fichier original</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'encodage est terminé avec succès</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> tous les fichiers de sortie sont générés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier original MP3/AAC est supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les profils Opus et HLS sont conservés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace de stockage est économisé</p>
|
||
<hr />
|
||
<h2 id="18-temps-dencodage-contenu-5-minutes">18. Temps d'encodage contenu 5 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un fichier de 5 minutes est uploadé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'encodage démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'encodage prend environ 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification "Contenu prêt à publier"</p>
|
||
<hr />
|
||
<h2 id="19-temps-dencodage-podcast-1-heure">19. Temps d'encodage podcast 1 heure</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un fichier de 1 heure est uploadé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'encodage démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'encodage prend environ 5 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une barre de progression est affichée</p>
|
||
<hr />
|
||
<h2 id="20-temps-dencodage-podcast-4-heures">20. Temps d'encodage podcast 4 heures</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un fichier de 4 heures est uploadé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'encodage démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'encodage prend environ 20 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux fermer l'app (traitement asynchrone)</p>
|
||
<hr />
|
||
<h2 id="21-notification-contenu-pret-a-publier">21. Notification "Contenu prêt à publier"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est en cours d'encodage</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'encodage se termine avec succès</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push "✅ Votre contenu est prêt à publier"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accéder à l'interface de publication</p>
|
||
<hr />
|
||
<h2 id="22-echec-dencodage-fichier-corrompu">22. Échec d'encodage - fichier corrompu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un fichier MP3 corrompu est uploadé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'encodage démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'encodage échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification "❌ Erreur d'encodage: fichier corrompu"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier temporaire est supprimé</p>
|
||
<hr />
|
||
<h2 id="23-ecoute-acceleree-vitesses-disponibles">23. Écoute accélérée - vitesses disponibles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est publié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur écoute le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut choisir parmi les vitesses:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="24-ecoute-acceleree-pour-moderateurs">24. Écoute accélérée pour modérateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un modérateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu de 30 secondes est à valider</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je l'écoute à 2.0x</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je termine l'écoute en 15 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma productivité est doublée</p>
|
||
<hr />
|
||
<h2 id="25-ecoute-acceleree-pour-auditeurs">25. Écoute accélérée pour auditeurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un auditeur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un podcast de 1 heure est disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je configure la vitesse à 1.5x</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> j'écoute le podcast en 40 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je gagne 20 minutes</p>
|
||
<hr />
|
||
<h2 id="26-sauvegarde-preference-vitesse-decoute">26. Sauvegarde préférence vitesse d'écoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je configure la vitesse à 1.5x</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute plusieurs contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les contenus sont lus à 1.5x par défaut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma préférence est sauvegardée</p>
|
||
<hr />
|
||
<h2 id="27-scalabilite-horizontale-des-workers">27. Scalabilité horizontale des workers</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 contenus sont uploadés simultanément</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les jobs d'encodage sont distribués</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> plusieurs workers Go traitent les jobs en parallèle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Kubernetes scale automatiquement les pods
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les contenus sont encodés sans délai excessif</p>
|
||
<hr />
|
||
<h2 id="28-statut-dencodage-visible">28. Statut d'encodage visible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est en cours d'encodage</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le statut:</p>
|
||
<pre><code>| état | affichage |
|
||
|---|---|
|
||
| En attente | ⏳ File d'attente |
|
||
| En cours | ⚙️ Encodage en cours (45%) |
|
||
| Terminé | ✅ Prêt à publier |
|
||
| Échec | ❌ Erreur - Réessayer |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="29-reessayer-apres-echec-dencodage">29. Réessayer après échec d'encodage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'encodage de mon contenu a échoué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Réessayer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un nouveau job d'encodage est lancé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux tenter à nouveau</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="validation-des-3-premiers-contenus">Validation des 3 premiers contenus</h1>
|
||
<blockquote>
|
||
<p><em>En tant que nouveau créateur</em>
|
||
<em>Je veux que mes 3 premiers contenus soient validés</em>
|
||
<em>Afin de devenir créateur vérifié</em></p>
|
||
</blockquote>
|
||
<p><strong>30 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis un nouveau créateur</p>
|
||
</blockquote>
|
||
<h2 id="1-premier-contenu-passe-en-file-de-validation">1. Premier contenu passe en file de validation</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie mon premier contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu passe en file d'attente modération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Votre contenu est en cours de validation (24-48h)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu n'est pas encore visible publiquement</p>
|
||
<hr />
|
||
<h2 id="2-deuxieme-contenu-passe-egalement-en-validation">2. Deuxième contenu passe également en validation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon premier contenu a été validé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie mon deuxième contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu passe en file d'attente modération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le délai estimé est 24-48h</p>
|
||
<hr />
|
||
<h2 id="3-troisieme-contenu-derniere-validation">3. Troisième contenu - dernière validation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes 2 premiers contenus ont été validés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie mon troisième contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu passe en file d'attente modération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Dernière validation avant statut vérifié ✓"</p>
|
||
<hr />
|
||
<h2 id="4-moderateur-ecoute-30-secondes-du-contenu">4. Modérateur écoute 30 secondes du contenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est en file de validation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur junior l'examine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il écoute les 30 premières secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il vérifie les métadonnées</p>
|
||
<hr />
|
||
<h2 id="5-validation-qualite-audio-acceptable">5. Validation - Qualité audio acceptable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a une qualité audio claire</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur l'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il vérifie que l'audio est compréhensible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il n'y a pas de grésillement excessif</p>
|
||
<hr />
|
||
<h2 id="6-rejet-qualite-audio-insuffisante">6. Rejet - Qualité audio insuffisante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a un audio très grésillant</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur l'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la raison est "Qualité audio insuffisante"</p>
|
||
<hr />
|
||
<h2 id="7-validation-respect-des-regles">7. Validation - Respect des règles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu respecte les règles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur l'examine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il vérifie qu'il n'y a pas de contenu prohibé:</p>
|
||
<pre><code>| type prohibé |
|
||
|---|
|
||
| Haine |
|
||
| Violence |
|
||
| Spam |
|
||
| Illégalité |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="8-rejet-contenu-haineux-detecte">8. Rejet - Contenu haineux détecté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu contient des propos haineux</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur l'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est rejeté immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la raison est "Contenu haineux (violation des règles)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur peut recevoir un strike</p>
|
||
<hr />
|
||
<h2 id="9-validation-classification-age-coherente">9. Validation - Classification âge cohérente</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu familial est classé "Tout public"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur l'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il vérifie que la classification correspond au contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est accepté</p>
|
||
<hr />
|
||
<h2 id="10-rejet-classification-incorrecte">10. Rejet - Classification incorrecte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu adulte est classé "Tout public"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur détecte l'incohérence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la raison est "Classification d'âge incorrecte"</p>
|
||
<hr />
|
||
<h2 id="11-validation-tags-pertinents">11. Validation - Tags pertinents</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu sur l'automobile est tagué "Automobile", "Technologie"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur vérifie les tags</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il confirme que les tags correspondent au contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est accepté</p>
|
||
<hr />
|
||
<h2 id="12-rejet-tags-non-pertinents">12. Rejet - Tags non pertinents</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu musical est tagué "Automobile", "Sport"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur détecte l'incohérence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la raison est "Tags non pertinents avec le contenu"</p>
|
||
<hr />
|
||
<h2 id="13-validation-zone-diffusion-coherente">13. Validation - Zone diffusion cohérente</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide de la Tour Eiffel est en "Point GPS" Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur vérifie la cohérence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la zone est appropriée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est accepté</p>
|
||
<hr />
|
||
<h2 id="14-rejet-zone-incoherente">14. Rejet - Zone incohérente</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide de la Tour Eiffel est en zone "National"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur détecte l'incohérence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la raison est "Zone de diffusion incohérente (devrait être Point GPS)"</p>
|
||
<hr />
|
||
<h2 id="15-delai-de-validation-24-48h-jours-ouvres">15. Délai de validation 24-48h jours ouvrés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je publie un contenu un lundi</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu entre en file de validation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le délai estimé est 24-48h (mercredi maximum)</p>
|
||
<hr />
|
||
<h2 id="16-delai-etendu-le-weekend">16. Délai étendu le weekend</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je publie un contenu un vendredi soir</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu entre en file de validation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le délai peut atteindre 72h (lundi)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Validation en cours, délai 24-72h (weekend)"</p>
|
||
<hr />
|
||
<h2 id="17-priorite-fifo-first-in-first-out">17. Priorité FIFO (First In First Out)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 10 contenus sont en file de validation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les modérateurs traitent la file</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus sont traités dans l'ordre d'arrivée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> pas de traitement prioritaire</p>
|
||
<hr />
|
||
<h2 id="18-notification-acceptation">18. Notification acceptation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est validé et accepté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email "✅ Votre contenu '[Titre]' est en ligne !"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification push
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois un lien direct vers le contenu</p>
|
||
<hr />
|
||
<h2 id="19-compteur-de-validation">19. Compteur de validation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon premier contenu est accepté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "1/3 contenus validés pour devenir créateur vérifié"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon deuxième contenu est accepté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "2/3 contenus validés pour devenir créateur vérifié"</p>
|
||
<hr />
|
||
<h2 id="20-notification-refus-avec-raison-detaillee">20. Notification refus avec raison détaillée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu est rejeté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email "❌ Contenu '[Titre]' refusé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification push
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois la raison exacte: "Qualité audio insuffisante"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois un lien vers les règles de publication</p>
|
||
<hr />
|
||
<h2 id="21-possibilite-de-correction-et-resoumission">21. Possibilité de correction et resoumission</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu a été rejeté pour "Tags non pertinents"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je corrige les tags
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je resoumets le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu repasse en file de validation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une nouvelle validation est effectuée</p>
|
||
<hr />
|
||
<h2 id="22-apres-3-validations-statut-verifie-obtenu">22. Après 3 validations - Statut vérifié obtenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes 3 premiers contenus ont été validés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> j'obtiens le statut "Créateur Vérifié"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification "🎉 Vous êtes maintenant créateur vérifié !"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge ✓ apparaît sur mon profil</p>
|
||
<hr />
|
||
<h2 id="23-badge-verifie-visible-publiquement">23. Badge vérifié visible publiquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai le statut vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur consulte mon profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit le badge ✓ à côté de mon pseudo
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une mention "Créateur vérifié"</p>
|
||
<hr />
|
||
<h2 id="24-contenus-futurs-publies-immediatement">24. Contenus futurs publiés immédiatement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un 4ème contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est publié immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de validation préalable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "✅ Contenu publié"</p>
|
||
<hr />
|
||
<h2 id="25-moderation-a-posteriori-uniquement">25. Modération a posteriori uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je publie un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu est en ligne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut être signalé par les utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sera modéré uniquement si signalé</p>
|
||
<hr />
|
||
<h2 id="26-interface-moderateur-queue-de-contenus">26. Interface modérateur - Queue de contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un modérateur junior</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à l'interface de modération</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la file des contenus à valider
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le nombre total en attente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus sont triés par ordre FIFO</p>
|
||
<hr />
|
||
<h2 id="27-interface-moderateur-ecoute-acceleree">27. Interface modérateur - Écoute accélérée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un modérateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute un contenu de 30 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux choisir la vitesse 1.5x ou 2.0x
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je termine l'écoute en 15 secondes à 2x
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma productivité est doublée</p>
|
||
<hr />
|
||
<h2 id="28-interface-moderateur-raccourcis-clavier">28. Interface modérateur - Raccourcis clavier</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je modère un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise les raccourcis clavier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux:</p>
|
||
<pre><code>| touche | action |
|
||
|---|---|
|
||
| A | Accepter |
|
||
| R | Rejeter |
|
||
| Espace | Play/Pause |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> la modération est accélérée</p>
|
||
<hr />
|
||
<h2 id="29-historique-createur-visible">29. Historique créateur visible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur soumet son 2ème contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur examine le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit l'historique:</p>
|
||
<pre><code>| contenu | statut |
|
||
|---|---|
|
||
| Contenu 1 | Validé |
|
||
| Contenu 2 | En cours |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> il peut juger la cohérence du créateur</p>
|
||
<hr />
|
||
<h2 id="30-temps-de-moderation-estime-15-mincreateur">30. Temps de modération estimé 1.5 min/créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur soumet 3 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les modérateurs traitent ces contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le temps total est environ:</p>
|
||
<pre><code>| action | temps |
|
||
|---|---|
|
||
| Écoute 30s × 3 | 90s |
|
||
| Vérification metadata | 15s |
|
||
| Décision | 5s |
|
||
| Total | 110s |
|
||
</code></pre>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="elargissement-automatique-de-zone-quand-aucun-contenu-nest-disponible">Élargissement automatique de zone quand aucun contenu n'est disponible</h1>
|
||
<p><strong>9 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la géolocalisation est activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis en mode écoute</p>
|
||
</blockquote>
|
||
<h2 id="1-aucun-contenu-dans-rayon-50km-elargissement-a-100km">1. Aucun contenu dans rayon 50km - élargissement à 100km</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis situé à la position GPS 48.8566, 2.3522
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu n'existe dans un rayon de 50 km autour de ma position
|
||
<span style="color: #F44336"><strong>Mais</strong></span> qu'au moins 1 contenu existe dans un rayon de 100 km</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu à me proposer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système élargit automatiquement la zone de recherche à 100 km
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un message "Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un contenu dans le rayon de 100 km m'est proposé</p>
|
||
<hr />
|
||
<h2 id="2-aucun-contenu-dans-rayon-100km-elargissement-au-departement">2. Aucun contenu dans rayon 100km - élargissement au département</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis situé dans le département "75" (Paris)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position
|
||
<span style="color: #F44336"><strong>Mais</strong></span> qu'au moins 1 contenu existe avec la zone "département" pour "75"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu à me proposer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système élargit automatiquement la zone de recherche au département
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre département"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un contenu départemental m'est proposé</p>
|
||
<hr />
|
||
<h2 id="3-aucun-contenu-departemental-elargissement-a-la-region">3. Aucun contenu départemental - élargissement à la région</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis situé dans la région "Île-de-France"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu départemental n'existe pour mon département
|
||
<span style="color: #F44336"><strong>Mais</strong></span> qu'au moins 1 contenu existe avec la zone "région" pour "Île-de-France"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu à me proposer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système élargit automatiquement la zone de recherche à la région
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un message "Aucun contenu local disponible. Voici du contenu dans votre région"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un contenu régional m'est proposé</p>
|
||
<hr />
|
||
<h2 id="4-aucun-contenu-regional-basculement-sur-contenu-national">4. Aucun contenu régional - basculement sur contenu national</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis situé en France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu n'existe dans un rayon de 100 km autour de ma position
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu départemental n'existe pour mon département
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu régional n'existe pour ma région</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu à me proposer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système bascule automatiquement sur du contenu national
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un message "Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un contenu national m'est proposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne reste jamais sans contenu disponible</p>
|
||
<hr />
|
||
<h2 id="5-elargissement-progressif-avec-plusieurs-etapes">5. Élargissement progressif avec plusieurs étapes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis situé dans une zone rurale isolée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu n'existe dans un rayon de 50 km
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu n'existe dans un rayon de 100 km
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu départemental n'existe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu régional n'existe</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu à me proposer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système essaie d'abord 50 km
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tout ce processus se fait de manière transparente et automatique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois le message correspondant au dernier niveau trouvé</p>
|
||
<hr />
|
||
<h2 id="6-message-personnalise-selon-la-distance-trouvee">6. Message personnalisé selon la distance trouvée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis situé à la position GPS 43.6047, 1.4442
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que <niveau_geo> contenu(s) est/sont trouvé(s)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système me propose du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois le message "<message_attendu>"</p>
|
||
<hr />
|
||
<h2 id="7-le-contenu-national-sert-de-filet-de-securite">7. Le contenu national sert de filet de sécurité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système a épuisé toutes les zones géographiques locales</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système bascule sur du contenu national</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois toujours avoir au moins 1 contenu disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce contenu peut être:</p>
|
||
<pre><code>| type_contenu |
|
||
|---|
|
||
| Actualités Le Monde |
|
||
| Podcasts génériques |
|
||
| Contenu éducatif national |
|
||
| Contenu culturel national |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="8-pas-decran-derreur-aucun-contenu">8. Pas d'écran d'erreur "Aucun contenu"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je lance l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu local n'est disponible dans ma zone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne dois jamais voir un message d'erreur "Aucun contenu disponible"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne dois jamais voir un écran vide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un contenu doit toujours m'être proposé, même si c'est du contenu national</p>
|
||
<hr />
|
||
<h2 id="9-elargissement-avec-prise-en-compte-des-centres-dinteret">9. Élargissement avec prise en compte des centres d'intérêt</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis situé dans une zone rurale
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu n'existe dans un rayon de 50 km
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes centres d'intérêt incluent "Automobile" à 80% et "Voyage" à 70%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu national existe avec le tag "Automobile"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu national existe avec le tag "Politique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système bascule sur du contenu national</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu national proposé prend en compte mes centres d'intérêt
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu "Automobile" a un score supérieur au contenu "Politique"</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="gestion-dun-contenu-supprime-pendant-lecoute">Gestion d'un contenu supprimé pendant l'écoute</h1>
|
||
<p><strong>11 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis en mode écoute
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu "C123" est en cours de lecture</p>
|
||
</blockquote>
|
||
<h2 id="1-contenu-supprime-pendant-lecture-fin-de-lecture-sans-interruption">1. Contenu supprimé pendant lecture - fin de lecture sans interruption</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute le contenu "C123" depuis 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la durée totale du contenu est de 120 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu est supprimé par la modération côté backend</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture du contenu continue sans interruption
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux écouter le contenu jusqu'à la fin
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune interruption brutale ne se produit</p>
|
||
<hr />
|
||
<h2 id="2-passage-automatique-apres-fin-du-contenu-supprime">2. Passage automatique après fin du contenu supprimé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu "C123" a été supprimé pendant ma lecture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai écouté le contenu jusqu'à la fin</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système attend 2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> passe automatiquement au contenu suivant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification toast discrète "Contenu précédent retiré (violation règles)"</p>
|
||
<hr />
|
||
<h2 id="3-bouton-precedent-desactive-apres-suppression">3. Bouton Précédent désactivé après suppression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu "C123" a été supprimé pendant ma lecture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis passé au contenu suivant "C456"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'appuyer sur le bouton "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Précédent" ne me ramène pas au contenu supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un message "Ce contenu n'est plus disponible"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture du contenu actuel "C456" continue</p>
|
||
<hr />
|
||
<h2 id="4-tentative-de-retour-manuel-au-contenu-supprime">4. Tentative de retour manuel au contenu supprimé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis sur le contenu "C456"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu précédent "C123" a été supprimé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur le bouton "Précédent" pour revenir au contenu supprimé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un message "Ce contenu n'est plus disponible"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture reste sur le contenu actuel "C456"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune action n'est effectuée</p>
|
||
<hr />
|
||
<h2 id="5-notification-discrete-pendant-la-conduite">5. Notification discrète pendant la conduite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à une vitesse de 60 km/h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu "C123" est supprimé pendant ma lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification "Contenu précédent retiré (violation règles)" s'affiche en toast discret
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la notification disparaît automatiquement après 5 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune popup modale n'interrompt ma conduite
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu suivant démarre automatiquement après 2 secondes</p>
|
||
<hr />
|
||
<h2 id="6-message-informatif-mais-non-alarmiste">6. Message informatif mais non alarmiste</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu "C123" a été supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je passe au contenu suivant</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la notification s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le message doit être informatif: "Contenu précédent retiré (violation règles)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le ton ne doit pas être alarmiste
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message doit être bref et compréhensible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun détail technique n'est affiché pendant la conduite</p>
|
||
<hr />
|
||
<h2 id="7-contenu-supprime-retire-de-lhistorique">7. Contenu supprimé retiré de l'historique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu "C123" a été supprimé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mon historique d'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "C123" n'apparaît plus dans mon historique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas relancer la lecture de ce contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique affiche "[Contenu retiré]" à la place du titre</p>
|
||
<hr />
|
||
<h2 id="8-contenu-supprime-non-accessible-via-lien-direct">8. Contenu supprimé non accessible via lien direct</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu "C123" a été supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai un lien de partage "roadwave.fr/share/c/C123"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le lien de partage</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un message "Ce contenu a été retiré pour violation des règles de la communauté"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis redirigé vers l'accueil de l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune lecture n'est possible</p>
|
||
<hr />
|
||
<h2 id="9-plusieurs-contenus-supprimes-dans-lhistorique-recent">9. Plusieurs contenus supprimés dans l'historique récent</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté les contenus suivants:</p>
|
||
<pre><code>| id | statut |
|
||
|---|---|
|
||
| C123 | supprimé |
|
||
| C456 | actif |
|
||
| C789 | supprimé |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> que je suis actuellement sur le contenu "C456"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie plusieurs fois sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne peux pas revenir aux contenus "C123" ou "C789"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système saute automatiquement les contenus supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reviens au dernier contenu actif disponible avant "C456"</p>
|
||
<hr />
|
||
<h2 id="10-consultation-detaillee-du-contenu-supprime-a-larret">10. Consultation détaillée du contenu supprimé à l'arrêt</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à l'arrêt
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu "C123" a été supprimé pendant ma session</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre les détails de la notification de suppression</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux voir les informations suivantes:</p>
|
||
<pre><code>| information |
|
||
|---|
|
||
| Titre du contenu |
|
||
| Créateur |
|
||
| Raison de suppression |
|
||
| Date de suppression |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux signaler une erreur de modération si je pense qu'elle est injustifiée</p>
|
||
<hr />
|
||
<h2 id="11-pas-dimpact-sur-les-jauges-dinteret-lors-de-la-suppression">11. Pas d'impact sur les jauges d'intérêt lors de la suppression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté le contenu "C123" pendant 80 secondes (66%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges d'intérêt ont été mises à jour pendant l'écoute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu est supprimé après mon écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les modifications de mes jauges d'intérêt sont conservées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'écoute déjà effectuée reste comptabilisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les futures écoutes de ce contenu sont bloquées</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="mode-degrade-sans-geolocalisation">Mode dégradé sans géolocalisation</h1>
|
||
<p><strong>19 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai refusé ou désactivé l'accès à la géolocalisation</p>
|
||
</blockquote>
|
||
<h2 id="1-types-de-contenu-disponibles-sans-geolocalisation">1. Types de contenu disponibles sans géolocalisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les types de contenu suivants sont disponibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="2-popup-dinformation-au-premier-lancement-sans-gps">2. Popup d'information au premier lancement sans GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que c'est mon premier lancement de l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai refusé l'accès à la géolocalisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application détecte que le GPS est désactivé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup s'affiche avec le message:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la popup contient les boutons suivants:</p>
|
||
<pre><code>| bouton | action |
|
||
|---|---|
|
||
| Activer | Redirection vers paramètres OS |
|
||
| Continuer sans | Ferme popup et lance en mode dégradé |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> une checkbox "Ne plus me demander" est disponible</p>
|
||
<hr />
|
||
<h2 id="3-popup-non-affichee-si-case-ne-plus-me-demander-cochee">3. Popup non affichée si case "Ne plus me demander" cochée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai déjà vu la popup de géolocalisation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai coché "Ne plus me demander"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance l'application avec le GPS désactivé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la popup de géolocalisation ne s'affiche pas
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application démarre directement en mode dégradé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le banner permanent de rappel s'affiche</p>
|
||
<hr />
|
||
<h2 id="4-redirection-vers-parametres-os-lors-du-clic-sur-activer">4. Redirection vers paramètres OS lors du clic sur "Activer"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la popup de géolocalisation est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Activer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers les paramètres de géolocalisation de mon OS
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sur iOS, j'arrive dans "Réglages > RoadWave > Localisation"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sur Android, j'arrive dans "Paramètres > Applications > RoadWave > Autorisations > Position"</p>
|
||
<hr />
|
||
<h2 id="5-banner-de-rappel-permanent-sans-gps">5. Banner de rappel permanent sans GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai cliqué sur "Continuer sans" géolocalisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un bandeau s'affiche en haut de l'écran
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bandeau contient le texte: "Mode limité : géolocalisation désactivée. [Activer]"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bandeau a un fond de couleur avertissement (jaune/orange)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bandeau n'est pas intrusif mais reste visible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bandeau reste affiché sur toutes les pages de l'application</p>
|
||
<hr />
|
||
<h2 id="6-clic-sur-le-bouton-activer-du-banner">6. Clic sur le bouton "Activer" du banner</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le banner "Mode limité" est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le lien "[Activer]" dans le banner</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers les paramètres de géolocalisation de mon OS</p>
|
||
<hr />
|
||
<h2 id="7-disparition-du-banner-apres-activation-gps">7. Disparition du banner après activation GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le banner "Mode limité" est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je reviens dans l'application après avoir activé le GPS dans les paramètres</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application détecte que la géolocalisation est maintenant active</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le banner disparaît automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application bascule en mode normal avec contenu géolocalisé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast de confirmation s'affiche: "Géolocalisation activée"</p>
|
||
<hr />
|
||
<h2 id="8-lecture-de-contenu-national-sans-gps">8. Lecture de contenu national sans GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que du contenu national existe (actualités Le Monde, podcasts génériques)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance la lecture</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux écouter le contenu national sans restriction
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme de recommandation se base uniquement sur:</p>
|
||
<pre><code>| critère |
|
||
|---|
|
||
| Mes centres d'intérêt |
|
||
| Mon historique d'écoute |
|
||
| Popularité générale |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> la proximité géographique n'est pas prise en compte</p>
|
||
<hr />
|
||
<h2 id="9-lecture-de-contenu-telecharge-sans-gps">9. Lecture de contenu téléchargé sans GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai téléchargé 30 contenus quand j'avais le GPS activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mes contenus téléchargés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux lire tous mes contenus téléchargés normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus géolocalisés téléchargés restent accessibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le filtre géographique n'est pas appliqué pour les contenus offline</p>
|
||
<hr />
|
||
<h2 id="10-contenu-neutre-geographiquement-disponible">10. Contenu "Neutre" géographiquement disponible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un créateur a publié du contenu avec la classification géographique "Neutre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je recherche du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus "Neutre" sont inclus dans les résultats
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ils sont mélangés avec le contenu national
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme les priorise selon mes centres d'intérêt</p>
|
||
<hr />
|
||
<h2 id="11-audio-guides-inaccessibles-sans-gps">11. Audio-guides inaccessibles sans GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je recherche un audio-guide spécifique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les audio-guides apparaissent dans les résultats de recherche
|
||
<span style="color: #F44336"><strong>Mais</strong></span> un badge "GPS requis" est affiché sur chaque audio-guide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> quand je clique sur un audio-guide, un message s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir "Activer" ou "Annuler"</p>
|
||
<hr />
|
||
<h2 id="12-notifications-push-geo-declenchees-desactivees">12. Notifications push géo-déclenchées désactivées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis abonné à un créateur qui diffuse du contenu géolocalisé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur publie un nouveau contenu géolocalisé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de notification push géo-déclenchée
|
||
<span style="color: #F44336"><strong>Mais</strong></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"><strong>Et</strong></span> la notification précise: "Nouveau contenu national de [Créateur]"</p>
|
||
<hr />
|
||
<h2 id="13-contenu-geolocalise-non-propose-dans-le-feed">13. Contenu géolocalisé non proposé dans le feed</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système génère mon feed de contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun contenu "Ancré" ou "Contextuel" n'est inclus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les contenus "Neutre" et "National" sont proposés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon feed contient au minimum 20 contenus disponibles</p>
|
||
<hr />
|
||
<h2 id="14-application-fonctionnelle-sans-gps-pas-de-blocage">14. Application fonctionnelle sans GPS (pas de blocage)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne suis jamais bloqué par un écran "GPS requis"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les fonctionnalités non-géolocalisées restent accessibles:</p>
|
||
<pre><code>| fonctionnalité |
|
||
|---|
|
||
| Écoute contenu national |
|
||
| Gestion profil |
|
||
| Abonnements créateurs |
|
||
| Recherche textuelle |
|
||
| Historique d'écoute |
|
||
| Paramètres |
|
||
| Mode offline |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux créer et publier du contenu national</p>
|
||
<hr />
|
||
<h2 id="15-respect-du-choix-utilisateur-de-ne-pas-activer-gps">15. Respect du choix utilisateur de ne pas activer GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai coché "Ne plus me demander" pour la géolocalisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise l'application pendant plusieurs semaines</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la popup de demande GPS ne s'affiche plus jamais automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul le banner permanent reste affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application ne force jamais l'activation du GPS</p>
|
||
<hr />
|
||
<h2 id="16-bascule-automatique-en-mode-normal-apres-activation-gps">16. Bascule automatique en mode normal après activation GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise l'application en mode dégradé depuis 1 semaine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je décide d'activer la géolocalisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application détecte que le GPS est maintenant actif</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode dégradé est désactivé automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le banner "Mode limité" disparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu géolocalisé devient disponible immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon feed se rafraîchit avec du contenu local pertinent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast de confirmation s'affiche: "Géolocalisation activée - Contenu local disponible"</p>
|
||
<hr />
|
||
<h2 id="17-demande-de-permission-gps-lors-de-lutilisation-dune-fonctionnalite-geo">17. Demande de permission GPS lors de l'utilisation d'une fonctionnalité géo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder à une fonctionnalité nécessitant le GPS (ex: audio-guide)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup contextuelle s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accepter ou refuser
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si j'accepte, je suis redirigé vers les paramètres OS
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je refuse, je reste en mode dégradé sans message d'erreur répétitif</p>
|
||
<hr />
|
||
<h2 id="18-statistiques-de-contenu-local-disponible-non-affiche">18. Statistiques de contenu local disponible non affiché</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la géolocalisation est désactivée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je navigue dans l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le banner peut afficher occasionnellement:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce message incitatif change tous les 3 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il reste non intrusif (pas de popup, juste le banner)</p>
|
||
<hr />
|
||
<h2 id="19-onboarding-different-pour-utilisateurs-sans-gps">19. Onboarding différent pour utilisateurs sans GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que c'est ma première utilisation de RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai refusé la géolocalisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'onboarding se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un écran explicatif s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer avec un bouton "Compris"</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="gestion-de-la-perte-de-reseau-et-buffering-adaptatif">Gestion de la perte de réseau et buffering adaptatif</h1>
|
||
<p><strong>17 scénarios</strong> (16 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis en mode écoute
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu est en cours de lecture</p>
|
||
</blockquote>
|
||
<h2 id="1-plan-parametres-de-buffer-selon-le-type-de-reseau">1. 📋 Plan: Paramètres de buffer selon le type de réseau</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté en "<type_reseau>"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système initialise le buffer audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le buffer minimum est de <buffer_min> secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer cible est de <buffer_cible> secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer maximum est de <buffer_max> secondes</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>type_reseau</th>
|
||
<th>buffer_min</th>
|
||
<th>buffer_cible</th>
|
||
<th>buffer_max</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>WiFi</td>
|
||
<td>5</td>
|
||
<td>30</td>
|
||
<td>120</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4G</td>
|
||
<td>10</td>
|
||
<td>45</td>
|
||
<td>120</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5G</td>
|
||
<td>10</td>
|
||
<td>45</td>
|
||
<td>120</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3G</td>
|
||
<td>30</td>
|
||
<td>90</td>
|
||
<td>300</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="2-connexion-instable-avec-latence-elevee-aucun-message-immediat">2. Connexion instable avec latence élevée - aucun message immédiat</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté en 4G
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le buffer contient 45 secondes de contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la latence réseau dépasse 500ms</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun message n'est affiché immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture continue normalement sur le buffer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système tente de continuer le téléchargement en arrière-plan</p>
|
||
<hr />
|
||
<h2 id="3-connexion-instable-pendant-plus-de-10-secondes-toast-discret">3. Connexion instable pendant plus de 10 secondes - toast discret</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté en 4G
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la latence réseau dépasse 500ms depuis 10 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte la latence prolongée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un toast discret s'affiche: "Connexion instable"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le toast disparaît automatiquement après 3 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture continue normalement</p>
|
||
<hr />
|
||
<h2 id="4-perte-totale-de-reseau-lecture-sur-buffer">4. Perte totale de réseau - lecture sur buffer</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté en WiFi
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le buffer contient 30 secondes de contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je perds totalement la connexion réseau</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture continue sur le buffer disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast s'affiche: "Hors ligne, lecture sur buffer (30s restantes)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un compte à rebours du temps de buffer restant est visible</p>
|
||
<hr />
|
||
<h2 id="5-buffer-qui-sepuise-pendant-la-perte-reseau">5. Buffer qui s'épuise pendant la perte réseau</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis hors ligne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le buffer contient 30 secondes de contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu continue de jouer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compte à rebours diminue en temps réel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le toast affiche "Hors ligne, lecture sur buffer (15s restantes)" après 15 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le toast affiche "Hors ligne, lecture sur buffer (5s restantes)" après 25 secondes</p>
|
||
<hr />
|
||
<h2 id="6-pause-automatique-apres-epuisement-du-buffer">6. Pause automatique après épuisement du buffer</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis hors ligne depuis 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le buffer est complètement épuisé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il n'y a plus de contenu audio à lire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture se met en pause automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un overlay s'affiche: "Connexion perdue. Reconnexion en cours..."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système tente de se reconnecter automatiquement</p>
|
||
<hr />
|
||
<h2 id="7-tentatives-de-reconnexion-automatique">7. Tentatives de reconnexion automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la lecture est en pause suite à l'épuisement du buffer</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système tente de se reconnecter</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une tentative de reconnexion est effectuée toutes les 5 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un maximum de 6 tentatives sont effectuées (30 secondes au total)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'overlay affiche "Tentative de reconnexion... (X/6)"</p>
|
||
<hr />
|
||
<h2 id="8-proposition-du-mode-offline-apres-30-secondes-dechec">8. Proposition du mode offline après 30 secondes d'échec</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 6 tentatives de reconnexion ont échoué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que cela fait 30 secondes que je suis déconnecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la 6ème tentative échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup s'affiche: "Voulez-vous continuer avec vos contenus téléchargés ?"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la popup contient deux boutons:</p>
|
||
<pre><code>| bouton | action |
|
||
|---|---|
|
||
| Réessayer | Nouvelle série de 6 tentatives |
|
||
| Mode offline | Bascule sur contenus téléchargés |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-basculement-reussi-vers-le-mode-offline">9. Basculement réussi vers le mode offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la popup de mode offline est affichée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai téléchargé 20 contenus dans ma zone géographique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Mode offline"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système bascule sur les contenus téléchargés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un nouveau contenu téléchargé démarre automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bandeau permanent indique "Mode hors ligne - Contenus téléchargés"</p>
|
||
<hr />
|
||
<h2 id="10-aucun-contenu-telecharge-disponible">10. Aucun contenu téléchargé disponible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la popup de mode offline est affichée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai aucun contenu téléchargé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Mode offline"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message s'affiche: "Aucun contenu téléchargé disponible"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis invité à me connecter en WiFi pour télécharger du contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton "Réessayer" reste la seule option</p>
|
||
<hr />
|
||
<h2 id="11-reprise-automatique-apres-reconnexion">11. Reprise automatique après reconnexion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la lecture est en pause depuis 15 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'étais à 02:35 du contenu en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la connexion réseau est rétablie</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture reprend automatiquement au point d'arrêt exact (02:35)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast s'affiche: "Connexion rétablie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le toast disparaît après 3 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer se remplit progressivement selon le type de réseau</p>
|
||
<hr />
|
||
<h2 id="12-reconnexion-avec-changement-de-type-de-reseau">12. Reconnexion avec changement de type de réseau</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'étais connecté en WiFi
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai perdu la connexion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me reconnecte en 4G</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système ajuste automatiquement les paramètres de buffer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer minimum passe de 5s à 10s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer cible passe de 30s à 45s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture reprend normalement</p>
|
||
<hr />
|
||
<h2 id="13-passage-dans-un-tunnel-avec-perte-de-signal">13. Passage dans un tunnel avec perte de signal</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à 90 km/h sur autoroute
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en 4G avec un buffer de 45 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'entre dans un tunnel et perds le signal</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture continue sur le buffer pendant 45 secondes maximum
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune notification n'est affichée pendant les 10 premières secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast discret s'affiche après 10 secondes: "Connexion instable"</p>
|
||
<hr />
|
||
<h2 id="14-sortie-du-tunnel-avant-epuisement-du-buffer">14. Sortie du tunnel avant épuisement du buffer</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis dans un tunnel depuis 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il reste 15 secondes de buffer</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sors du tunnel et récupère le signal 4G</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture continue sans interruption
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer se remplit à nouveau
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast s'affiche: "Connexion rétablie"</p>
|
||
<hr />
|
||
<h2 id="15-changement-de-cellule-4g-pendant-la-lecture">15. Changement de cellule 4G pendant la lecture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis et change de cellule mobile toutes les 5-10 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le buffer contient 45 secondes de contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un handoff de cellule se produit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture continue sans interruption grâce au buffer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la connexion à la nouvelle cellule se fait de manière transparente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune notification n'est affichée si le handoff réussit en moins de 5 secondes</p>
|
||
<hr />
|
||
<h2 id="16-telechargement-preventif-en-wifi-avant-trajet">16. Téléchargement préventif en WiFi avant trajet</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté en WiFi
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai activé le téléchargement automatique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte que je suis à l'arrêt en WiFi</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système me propose de télécharger du contenu pour mon trajet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux sélectionner une zone géographique à télécharger
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le téléchargement se fait en arrière-plan</p>
|
||
<hr />
|
||
<h2 id="17-tracking-des-evenements-de-perte-reseau-pour-amelioration">17. Tracking des événements de perte réseau pour amélioration</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je perds la connexion réseau</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'événement de perte est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système enregistre les métriques suivantes:</p>
|
||
<pre><code>| métrique |
|
||
|---|
|
||
| Type de réseau avant perte |
|
||
| Durée de la coupure |
|
||
| Buffer disponible |
|
||
| Position GPS approximative |
|
||
| Heure de la journée |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces métriques sont anonymisées et envoyées en batch lors de la prochaine connexion WiFi
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les données servent à améliorer les paramètres de buffer</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="tests-bdd-documentation-des-fonctionnalites">Tests BDD - Documentation des fonctionnalités</h1>
|
||
<p>Cette documentation est générée automatiquement à partir des fichiers Gherkin (<code>.feature</code>).</p>
|
||
<h2 id="vue-densemble">Vue d'ensemble</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Métrique</th>
|
||
<th>Valeur</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Fonctionnalités</td>
|
||
<td><strong>83</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Scénarios</td>
|
||
<td><strong>2112</strong></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Domaines métier</td>
|
||
<td><strong>18</strong></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="abonnements">🔔 Abonnements</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#abonnements/audio-guides-pieton">Audio-guides multi-séquences pour piétons</a></td>
|
||
<td style="text-align: center;">29</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#abonnements/impact-algorithme">Impact des abonnements sur l'algorithme</a></td>
|
||
<td style="text-align: center;">16</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#abonnements/limites-desabonnement">Limites d'abonnements et désabonnement</a></td>
|
||
<td style="text-align: center;">27</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#abonnements/notifications-contextuelles">Notifications contextuelles selon le mode de déplacement</a></td>
|
||
<td style="text-align: center;">28</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>4 fonctionnalités • 100 scénarios</em></p>
|
||
<h2 id="audio-guides">🎧 Audio Guides</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#audio-guides/mode-pieton">Audio-guide mode piéton (navigation manuelle)</a></td>
|
||
<td style="text-align: center;">29</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#audio-guides/mode-voiture">Audio-guide mode voiture (GPS automatique)</a></td>
|
||
<td style="text-align: center;">45</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#audio-guides/premium-monetisation">Audio-guides Premium et monétisation</a></td>
|
||
<td style="text-align: center;">31</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#audio-guides/modes-velo-transport">Audio-guides modes vélo et transport</a></td>
|
||
<td style="text-align: center;">27</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#audio-guides/creation-audio-guide">Création d'audio-guide multi-séquences</a></td>
|
||
<td style="text-align: center;">35</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#audio-guides/integration-fonctionnalites">Intégration audio-guides avec autres fonctionnalités</a></td>
|
||
<td style="text-align: center;">39</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#audio-guides/progression-sauvegarde">Sauvegarde et reprise de progression audio-guide</a></td>
|
||
<td style="text-align: center;">32</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>7 fonctionnalités • 238 scénarios</em></p>
|
||
<h2 id="authentication">🔐 Authentication</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#authentication/two-factor-authentication">Authentification à deux facteurs (2FA)</a></td>
|
||
<td style="text-align: center;">16</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#authentication/classification-age">Classification des contenus par âge</a></td>
|
||
<td style="text-align: center;">13</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#authentication/connexion">Connexion utilisateur</a></td>
|
||
<td style="text-align: center;">11</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#authentication/sessions-tokens">Gestion des sessions et tokens</a></td>
|
||
<td style="text-align: center;">13</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#authentication/inscription">Inscription utilisateur</a></td>
|
||
<td style="text-align: center;">15</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#authentication/recuperation-compte">Récupération de compte</a></td>
|
||
<td style="text-align: center;">14</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#authentication/verification-email">Vérification d'email</a></td>
|
||
<td style="text-align: center;">10</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>7 fonctionnalités • 92 scénarios</em></p>
|
||
<h2 id="content-creation">🎨 Content Creation</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#content-creation/modification-suppression">Modification et suppression de contenu</a></td>
|
||
<td style="text-align: center;">30</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#content-creation/metadonnees-publication">Métadonnées et publication de contenu</a></td>
|
||
<td style="text-align: center;">34</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#content-creation/upload-encodage">Upload et encodage de contenu audio</a></td>
|
||
<td style="text-align: center;">29</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#content-creation/validation-premiers-contenus">Validation des 3 premiers contenus</a></td>
|
||
<td style="text-align: center;">30</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>4 fonctionnalités • 123 scénarios</em></p>
|
||
<h2 id="error-handling">⚠️ Error Handling</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#error-handling/contenu-supprime-pendant-ecoute">Gestion d'un contenu supprimé pendant l'écoute</a></td>
|
||
<td style="text-align: center;">11</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#error-handling/perte-reseau">Gestion de la perte de réseau et buffering adaptatif</a></td>
|
||
<td style="text-align: center;">17</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#error-handling/geolocalisation-desactivee">Mode dégradé sans géolocalisation</a></td>
|
||
<td style="text-align: center;">19</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#error-handling/aucun-contenu-disponible">Élargissement automatique de zone quand aucun contenu n'est disponible</a></td>
|
||
<td style="text-align: center;">9</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>4 fonctionnalités • 56 scénarios</em></p>
|
||
<h2 id="interest-gauges">📊 Interest Gauges</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#interest-gauges/jauge-initiale">Jauge initiale et cold start</a></td>
|
||
<td style="text-align: center;">15</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#interest-gauges/degradation-temporelle">Pas de dégradation temporelle des jauges</a></td>
|
||
<td style="text-align: center;">16</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#interest-gauges/evolution-jauges">Évolution des jauges d'intérêt</a></td>
|
||
<td style="text-align: center;">21</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>3 fonctionnalités • 52 scénarios</em></p>
|
||
<h2 id="mode-offline">📴 Mode Offline</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#mode-offline/synchronisation-actions">Synchronisation actions offline</a></td>
|
||
<td style="text-align: center;">45</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#mode-offline/telechargement">Téléchargement de contenus offline</a></td>
|
||
<td style="text-align: center;">49</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#mode-offline/validite-renouvellement">Validité et renouvellement contenus offline</a></td>
|
||
<td style="text-align: center;">38</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>3 fonctionnalités • 132 scénarios</em></p>
|
||
<h2 id="moderation_1">🛡️ Moderation</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#moderation/moderation-preventive">Modération préventive</a></td>
|
||
<td style="text-align: center;">22</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#moderation/sanctions-notifications">Sanctions et notifications de modération</a></td>
|
||
<td style="text-align: center;">27</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#moderation/signalement">Signalement de contenu inapproprié</a></td>
|
||
<td style="text-align: center;">23</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#moderation/traitement-signalements">Traitement des signalements par l'IA et les modérateurs</a></td>
|
||
<td style="text-align: center;">25</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>4 fonctionnalités • 97 scénarios</em></p>
|
||
<h2 id="monetisation">💰 Monetisation</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#monetisation/conditions-activation">Conditions d'activation de la monétisation</a></td>
|
||
<td style="text-align: center;">28</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#monetisation/contenus-premium-exclusifs">Contenus Premium exclusifs</a></td>
|
||
<td style="text-align: center;">34</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#monetisation/desactivation-suspension">Désactivation et suspension monétisation</a></td>
|
||
<td style="text-align: center;">35</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#monetisation/kyc-inscription">KYC et inscription à la monétisation</a></td>
|
||
<td style="text-align: center;">37</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#monetisation/obligations-fiscales">Obligations fiscales</a></td>
|
||
<td style="text-align: center;">30</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#monetisation/paiement-createurs">Paiement des créateurs</a></td>
|
||
<td style="text-align: center;">35</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#monetisation/sources-revenus">Sources de revenus créateurs</a></td>
|
||
<td style="text-align: center;">34</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>7 fonctionnalités • 233 scénarios</em></p>
|
||
<h2 id="navigation">🧭 Navigation</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#navigation/actions-complementaires">Actions complémentaires à l'arrêt</a></td>
|
||
<td style="text-align: center;">23</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#navigation/commande-precedent">Commande "Précédent"</a></td>
|
||
<td style="text-align: center;">19</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#navigation/commandes-volant">Commandes au volant et interactions simplifiées</a></td>
|
||
<td style="text-align: center;">21</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#navigation/commandes-vocales">Commandes vocales CarPlay et Android Auto</a></td>
|
||
<td style="text-align: center;">25</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#navigation/file-attente-suivant">File d'attente et commande "Suivant"</a></td>
|
||
<td style="text-align: center;">20</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#navigation/lecture-enchainement">Lecture en boucle et enchaînement automatique</a></td>
|
||
<td style="text-align: center;">27</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>6 fonctionnalités • 135 scénarios</em></p>
|
||
<h2 id="partage">🔗 Partage</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#partage/partage-contenu">Partage de contenu</a></td>
|
||
<td style="text-align: center;">22</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>1 fonctionnalités • 22 scénarios</em></p>
|
||
<h2 id="premium">⭐ Premium</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#premium/avantages-premium">Avantages Premium</a></td>
|
||
<td style="text-align: center;">37</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#premium/gestion-abonnement">Gestion abonnement Premium</a></td>
|
||
<td style="text-align: center;">41</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#premium/multi-devices-detection">Multi-devices et détection simultanée</a></td>
|
||
<td style="text-align: center;">30</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#premium/offre-tarification">Offre et tarification Premium</a></td>
|
||
<td style="text-align: center;">31</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>4 fonctionnalités • 139 scénarios</em></p>
|
||
<h2 id="profil">👤 Profil</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#profil/profil-createur">Profil créateur</a></td>
|
||
<td style="text-align: center;">31</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>1 fonctionnalités • 31 scénarios</em></p>
|
||
<h2 id="publicites_1">📢 Publicites</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#publicites/caracteristiques-pub">Caractéristiques et facturation des publicités</a></td>
|
||
<td style="text-align: center;">32</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#publicites/campagnes-publicitaires">Création de campagnes publicitaires</a></td>
|
||
<td style="text-align: center;">30</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#publicites/gestion-budget-pub">Gestion du budget et alertes publicitaires</a></td>
|
||
<td style="text-align: center;">30</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#publicites/insertion-frequence-pub">Insertion et fréquence des publicités</a></td>
|
||
<td style="text-align: center;">31</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#publicites/metriques-engagement-pub">Métriques d'engagement et dashboard publicitaire</a></td>
|
||
<td style="text-align: center;">27</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#publicites/validation-moderation-pub">Validation et modération des publicités</a></td>
|
||
<td style="text-align: center;">29</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>6 fonctionnalités • 179 scénarios</em></p>
|
||
<h2 id="radio-live_1">📻 Radio Live</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#radio-live/architecture-technique-live">Architecture technique radio live</a></td>
|
||
<td style="text-align: center;">24</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#radio-live/arret-live">Arrêt du live</a></td>
|
||
<td style="text-align: center;">19</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#radio-live/comportement-auditeur">Comportement auditeur pendant un live</a></td>
|
||
<td style="text-align: center;">27</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#radio-live/demarrage-live">Démarrage d'un live</a></td>
|
||
<td style="text-align: center;">20</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>4 fonctionnalités • 90 scénarios</em></p>
|
||
<h2 id="recherche">🔍 Recherche</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#recherche/recherche">Recherche de contenu</a></td>
|
||
<td style="text-align: center;">55</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>1 fonctionnalités • 55 scénarios</em></p>
|
||
<h2 id="recommendation">🎯 Recommendation</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#recommendation/classification-geo">Classification de géo-pertinence des contenus</a></td>
|
||
<td style="text-align: center;">10</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#recommendation/declenchement-geo">Contenus géolocalisés en mode voiture</a></td>
|
||
<td style="text-align: center;">36</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#recommendation/scoring-recommandation">Formule de scoring et recommandation</a></td>
|
||
<td style="text-align: center;">21</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#recommendation/historique-reproposition">Gestion de l'historique et reproposition</a></td>
|
||
<td style="text-align: center;">19</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#recommendation/contenu-politique">Gestion du contenu politique (MVP simplifié)</a></td>
|
||
<td style="text-align: center;">13</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#recommendation/mode-kids">Mode Kids pour utilisateurs 13-15 ans</a></td>
|
||
<td style="text-align: center;">15</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#recommendation/medias-traditionnels">Médias traditionnels sur RoadWave</a></td>
|
||
<td style="text-align: center;">21</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#recommendation/parametrabilite-admin">Paramétrabilité admin et A/B testing</a></td>
|
||
<td style="text-align: center;">20</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#recommendation/parametrabilite-utilisateur">Paramétrabilité utilisateur et profils</a></td>
|
||
<td style="text-align: center;">25</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>9 fonctionnalités • 180 scénarios</em></p>
|
||
<h2 id="rgpd-compliance">🔒 Rgpd Compliance</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Fonctionnalité</th>
|
||
<th style="text-align: center;">Scénarios</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><a href="#rgpd-compliance/anonymisation-gps">Anonymisation des données GPS après 24h</a></td>
|
||
<td style="text-align: center;">18</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#rgpd-compliance/compliance-administrative">Conformité administrative RGPD (Registre, Breach, DPO)</a></td>
|
||
<td style="text-align: center;">22</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#rgpd-compliance/cookies-analytics">Cookies et analytics avec Matomo self-hosted</a></td>
|
||
<td style="text-align: center;">20</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#rgpd-compliance/conservation-donnees">Durée de conservation des données et purge automatique</a></td>
|
||
<td style="text-align: center;">19</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#rgpd-compliance/consentement">Gestion du consentement RGPD</a></td>
|
||
<td style="text-align: center;">16</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#rgpd-compliance/mode-degrade-geoip">Mode dégradé avec GeoIP (sans GPS précis)</a></td>
|
||
<td style="text-align: center;">20</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#rgpd-compliance/portabilite-donnees">Portabilité des données (Article 20 RGPD)</a></td>
|
||
<td style="text-align: center;">22</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="#rgpd-compliance/suppression-compte">Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)</a></td>
|
||
<td style="text-align: center;">21</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><em>8 fonctionnalités • 158 scénarios</em></p>
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="pas-de-degradation-temporelle-des-jauges">Pas de dégradation temporelle des jauges</h1>
|
||
<blockquote>
|
||
<p><em>En tant que système de recommandation</em>
|
||
<em>Je veux que les jauges n'évoluent que par les actions utilisateur</em>
|
||
<em>Afin d'avoir un comportement prévisible et fiable</em></p>
|
||
</blockquote>
|
||
<p><strong>16 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-aucune-degradation-automatique-avec-le-temps">1. Aucune dégradation automatique avec le temps</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma jauge "Économie" est à 80%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'écoute aucun contenu pendant 30 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me reconnecte après 30 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Économie" est toujours à 80%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune dégradation temporelle n'a été appliquée</p>
|
||
<hr />
|
||
<h2 id="2-jauges-conservees-apres-6-mois-dinactivite">2. Jauges conservées après 6 mois d'inactivité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes jauges sont:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 75% |
|
||
| Voyage | 60% |
|
||
| Musique | 45% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> que je pars en vacances pendant 6 mois sans utiliser l'app</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me reconnecte après 6 mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes jauges sont exactement les mêmes:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 75% |
|
||
| Voyage | 60% |
|
||
| Musique | 45% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="3-evolution-naturelle-par-les-actions">3. Évolution naturelle par les actions</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'aimais "Économie" il y a 1 an (jauge 80%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que depuis, je skip tous les contenus "Économie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai skippé 50 contenus "Économie" en 1 an</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Économie" descend naturellement via les skips
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> atteint environ 55% (80% - 50 × 0.5% = 55%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la dégradation vient des actions, pas du temps</p>
|
||
<hr />
|
||
<h2 id="4-pas-de-cron-job-de-degradation">4. Pas de cron job de dégradation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système vérifie les jauges quotidiennement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur n'a pas d'activité depuis 90 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun job de dégradation n'est exécuté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les jauges restent inchangées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune ressource CPU n'est consommée pour la dégradation</p>
|
||
<hr />
|
||
<h2 id="5-comportement-previsible-apres-absence">5. Comportement prévisible après absence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma jauge "Sport" était à 70%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'utilise pas l'app pendant 1 an</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reviens et demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes recommandations reflètent toujours mes goûts d'avant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois du contenu "Sport" prioritaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le comportement est cohérent et prévisible</p>
|
||
<hr />
|
||
<h2 id="6-reinitialiser-manuellement-mes-centres-dinteret">6. Réinitialiser manuellement mes centres d'intérêt</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux repartir de zéro</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vais dans les paramètres
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Réinitialiser mes centres d'intérêt"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je confirme l'action</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes mes jauges reviennent à 50%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vos centres d'intérêt ont été réinitialisés"</p>
|
||
<hr />
|
||
<h2 id="7-confirmation-avant-reinitialisation">7. Confirmation avant réinitialisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis dans les paramètres</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Réinitialiser mes centres d'intérêt"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un message de confirmation:</p>
|
||
<pre><code>| titre | Êtes-vous sûr ? |
|
||
|---|---|
|
||
| message | Cette action remettra toutes vos jauges à 50% |
|
||
| actions | Confirmer / Annuler |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="8-annuler-la-reinitialisation">8. Annuler la réinitialisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai cliqué sur "Réinitialiser mes centres d'intérêt"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la confirmation est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Annuler"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes jauges ne sont pas modifiées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reviens aux paramètres</p>
|
||
<hr />
|
||
<h2 id="9-raison-de-reinitialisation-changement-de-vie">9. Raison de réinitialisation - changement de vie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilisais RoadWave pour mes trajets professionnels
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges reflétaient "Économie" (85%) et "Technologie" (75%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je change de vie et deviens musicien</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je réinitialise mes centres d'intérêt</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux repartir avec toutes les jauges à 50%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> découvrir du contenu "Musique" et "Culture" sans biais</p>
|
||
<hr />
|
||
<h2 id="10-pas-de-suggestion-automatique-de-reinitialisation">10. Pas de suggestion automatique de réinitialisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai pas utilisé l'app depuis 1 an</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me reconnecte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune suggestion de réinitialisation n'est affichée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes jauges sont conservées telles quelles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je garde le contrôle total</p>
|
||
<hr />
|
||
<h2 id="11-historique-conserve-apres-reinitialisation">11. Historique conservé après réinitialisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté 500 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je réinitialise mes centres d'intérêt</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes jauges reviennent à 50%
|
||
<span style="color: #F44336"><strong>Mais</strong></span> mon historique d'écoute est conservé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux toujours consulter mes anciens contenus écoutés</p>
|
||
<hr />
|
||
<h2 id="12-evolution-future-basee-sur-nouvelles-actions">12. Évolution future basée sur nouvelles actions</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai réinitialisé mes jauges à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 5 contenus "Voyage" à >80%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Voyage" monte à 60% (50% + 5 × 2%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme recommence à apprendre mes nouvelles préférences</p>
|
||
<hr />
|
||
<h2 id="13-respect-de-lhistorique-utilisateur">13. Respect de l'historique utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur aime "Cryptomonnaie" depuis 2 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que sa jauge est à 90%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 2 ans s'écoulent sans dégradation temporelle</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> sa jauge reste à 90%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système ne fait pas d'"oubli" artificiel</p>
|
||
<hr />
|
||
<h2 id="14-cout-infrastructure-zero">14. Coût infrastructure zéro</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'aucune dégradation temporelle n'existe</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule les jauges</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun calcul de date n'est nécessaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun batch nocturne ne tourne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun bug de fuseau horaire ne peut survenir
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût CPU est minimal</p>
|
||
<hr />
|
||
<h2 id="15-ux-previsible-jauge-actions">15. UX prévisible - jauge = actions</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur consulte sa jauge "Sport" à 65%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il se demande pourquoi elle est à 65%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut retracer ses actions:</p>
|
||
<pre><code>| action | impact |
|
||
|---|---|
|
||
| 10 likes automatiques | +10% |
|
||
| 3 abonnements Sport | +15% |
|
||
| 5 skips de contenu non-Sport | 0% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> il comprend que c'est le reflet exact de ses actions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de mystère ou automatisme caché</p>
|
||
<hr />
|
||
<h2 id="16-statistiques-affichees-sans-date">16. Statistiques affichées sans date</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte mes centres d'intérêt</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vois mes jauges</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| information | affiché |
|
||
|---|---|
|
||
| Niveau actuel | ✅ 75% |
|
||
| Évolution depuis début | ✅ +25% |
|
||
| Dernière mise à jour | ❌ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> aucune date n'est affichée car non pertinente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les actions comptent</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="evolution-des-jauges-dinteret">Évolution des jauges d'intérêt</h1>
|
||
<blockquote>
|
||
<p><em>En tant que système de recommandation</em>
|
||
<em>Je veux faire évoluer les jauges d'intérêt selon les actions utilisateur</em>
|
||
<em>Afin d'affiner les recommandations personnalisées</em></p>
|
||
</blockquote>
|
||
<p><strong>21 scénarios</strong> (20 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-like-automatique-renforce-apres-ecoute-80">1. Like automatique renforcé après écoute ≥80%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 5 minutes est tagué "Automobile"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Automobile" est à 45%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu pendant 4 minutes 30 secondes (90%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un like automatique renforcé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Automobile" augmente de 2%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Automobile" est maintenant à 47%</p>
|
||
<hr />
|
||
<h2 id="2-like-automatique-renforce-exactement-a-80">2. Like automatique renforcé exactement à 80%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 10 minutes est tagué "Voyage"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Voyage" est à 60%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu pendant exactement 8 minutes (80%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un like automatique renforcé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Voyage" augmente de 2%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Voyage" est maintenant à 62%</p>
|
||
<hr />
|
||
<h2 id="3-like-automatique-standard-apres-ecoute-30-79">3. Like automatique standard après écoute 30-79%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 5 minutes est tagué "Automobile"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Automobile" est à 45%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu pendant 2 minutes 30 secondes (50%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un like automatique standard
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Automobile" augmente de 1%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Automobile" est maintenant à 46%</p>
|
||
<hr />
|
||
<h2 id="4-like-automatique-standard-a-30-exactement">4. Like automatique standard à 30% exactement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 10 minutes est tagué "Musique"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Musique" est à 40%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu pendant exactement 3 minutes (30%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un like automatique standard
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Musique" augmente de 1%</p>
|
||
<hr />
|
||
<h2 id="5-like-automatique-standard-a-79">5. Like automatique standard à 79%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 10 minutes est tagué "Sport"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Sport" est à 55%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu pendant 7 minutes 54 secondes (79%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un like automatique standard
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Sport" augmente de 1%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Sport" est maintenant à 56%</p>
|
||
<hr />
|
||
<h2 id="6-like-explicite-manuel-2">6. Like explicite (manuel) +2%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est tagué "Économie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Économie" est à 70%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu partiellement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique manuellement sur le bouton "Like"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Économie" augmente de 2%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Économie" est maintenant à 72%</p>
|
||
<hr />
|
||
<h2 id="7-like-manuel-cumulable-avec-like-automatique">7. Like manuel cumulable avec like automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 5 minutes est tagué "Automobile"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Automobile" est à 45%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu pendant 2 minutes 30 secondes (50%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un like automatique standard (+1%)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique ensuite sur le bouton "Like"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge augmente encore de 2% (like manuel)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Automobile" a augmenté de 3% au total
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Automobile" est maintenant à 48%</p>
|
||
<hr />
|
||
<h2 id="8-abonnement-createur-impacte-tous-ses-tags">8. Abonnement créateur impacte tous ses tags</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur publie des contenus tagués "Automobile" et "Technologie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges sont:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 50% |
|
||
| Technologie | 45% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'abonne à ce créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Automobile" augmente de 5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Technologie" augmente de 5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes nouvelles jauges sont:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 55% |
|
||
| Technologie | 50% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-skip-rapide-10s-diminue-la-jauge">9. Skip rapide (<10s) diminue la jauge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est tagué "Économie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Économie" est à 45%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je skip le contenu après 5 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Économie" diminue de 0.5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Économie" est maintenant à 44.5%</p>
|
||
<hr />
|
||
<h2 id="10-skip-a-exactement-10s-ne-diminue-pas-la-jauge">10. Skip à exactement 10s ne diminue pas la jauge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est tagué "Politique"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Politique" est à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je skip le contenu après exactement 10 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Politique" ne change pas
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> reste à 50%</p>
|
||
<hr />
|
||
<h2 id="11-skip-tardif-30-est-neutre">11. Skip tardif (≥30%) est neutre</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 10 minutes est tagué "Musique"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Musique" est à 60%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 3 minutes (30%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je skip ensuite</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Musique" ne diminue pas (signal neutre)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge reste à 60% (plus le +1% de like auto si applicable)</p>
|
||
<hr />
|
||
<h2 id="12-contenu-avec-plusieurs-tags-impacte-toutes-les-jauges">12. Contenu avec plusieurs tags impacte toutes les jauges</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est tagué "Automobile" et "Voyage"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges sont:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 45% |
|
||
| Voyage | 60% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu à 90%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les deux jauges augmentent de 2%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes nouvelles jauges sont:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 47% |
|
||
| Voyage | 62% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="13-contenu-avec-3-tags-impacte-les-3-jauges">13. Contenu avec 3 tags impacte les 3 jauges</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est tagué "Sport", "Santé" et "Technologie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges sont à 50% pour chaque catégorie</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je skip rapidement après 5 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 3 jauges diminuent de 0.5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes passent à 49.5%</p>
|
||
<hr />
|
||
<h2 id="14-jauges-bornees-ne-peut-pas-depasser-100">14. Jauges bornées - ne peut pas dépasser 100%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma jauge "Cryptomonnaie" est à 99%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu tagué "Cryptomonnaie" est disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu à 95% (like auto renforcé +2%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Cryptomonnaie" passe à 100% (maximum)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ne dépasse pas 100%</p>
|
||
<hr />
|
||
<h2 id="15-jauges-bornees-ne-peut-pas-descendre-sous-0">15. Jauges bornées - ne peut pas descendre sous 0%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma jauge "Politique" est à 0.3%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu tagué "Politique" est disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je skip rapidement après 3 secondes (-0.5%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Politique" passe à 0% (minimum)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ne devient pas négative</p>
|
||
<hr />
|
||
<h2 id="16-calcul-immediat-a-chaque-action">16. Calcul immédiat à chaque action</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma jauge "Voyage" est à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute un contenu "Voyage" à 85%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la jauge est mise à jour immédiatement (pas de batch)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> passe à 52%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande mes recommandations dans la seconde suivante</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'algorithme utilise déjà la valeur 52%</p>
|
||
<hr />
|
||
<h2 id="17-like-manuel-apres-ecoute-30-pas-de-like-auto">17. Like manuel après écoute <30% (pas de like auto)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 10 minutes est tagué "Culture"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Culture" est à 60%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 2 minutes (20%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de like automatique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton "Like"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Culture" augmente de 2% uniquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Culture" est maintenant à 62%</p>
|
||
<hr />
|
||
<h2 id="18-unlike-retire-le-like-manuel">18. Unlike retire le like manuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai liké manuellement un contenu "Sport"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Sport" est passée de 55% à 57% (+2%)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Unlike"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Sport" diminue de 2%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Sport" revient à 55%</p>
|
||
<hr />
|
||
<h2 id="19-unlike-ne-peut-pas-retirer-un-like-automatique">19. Unlike ne peut pas retirer un like automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté un contenu "Musique" à 90%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu un like automatique renforcé (+2%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Musique" est à 52%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de faire "Unlike"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action n'est pas disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge reste à 52%</p>
|
||
<hr />
|
||
<h2 id="20-tags-definis-par-createur-a-la-publication">20. Tags définis par créateur à la publication</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois sélectionner 1 à 3 tags
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ces tags sont fixés après publication
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> impacteront les jauges de tous les auditeurs</p>
|
||
<hr />
|
||
<h2 id="21-plan-calculs-avec-differentes-durees-decoute">21. 📋 Plan: Calculs avec différentes durées d'écoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de 10 minutes est tagué "Voyage"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Voyage" est à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant <duree> (<pourcentage>)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge évolue de <impact>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma nouvelle jauge est à <nouveau_niveau></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>duree</th>
|
||
<th>pourcentage</th>
|
||
<th>impact</th>
|
||
<th>nouveau_niveau</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1 min</td>
|
||
<td>10%</td>
|
||
<td>0%</td>
|
||
<td>50%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3 min</td>
|
||
<td>30%</td>
|
||
<td>+1%</td>
|
||
<td>51%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5 min</td>
|
||
<td>50%</td>
|
||
<td>+1%</td>
|
||
<td>51%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>7.9 min</td>
|
||
<td>79%</td>
|
||
<td>+1%</td>
|
||
<td>51%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>8 min</td>
|
||
<td>80%</td>
|
||
<td>+2%</td>
|
||
<td>52%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>9.5 min</td>
|
||
<td>95%</td>
|
||
<td>+2%</td>
|
||
<td>52%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5 sec</td>
|
||
<td><1%</td>
|
||
<td>-0.5%</td>
|
||
<td>49.5%</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="jauge-initiale-et-cold-start">Jauge initiale et cold start</h1>
|
||
<blockquote>
|
||
<p><em>En tant que nouvel utilisateur</em>
|
||
<em>Je veux que mes jauges d'intérêt démarrent de manière neutre</em>
|
||
<em>Afin de découvrir du contenu sans biais initial</em></p>
|
||
</blockquote>
|
||
<p><strong>15 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-inscription-toutes-les-jauges-a-50">1. Inscription - toutes les jauges à 50%</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'inscris sur RoadWave</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes mes jauges d'intérêt sont initialisées à 50%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne dois pas remplir de questionnaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'inscription est ultra-rapide</p>
|
||
<hr />
|
||
<h2 id="2-liste-des-categories-disponibles">2. Liste des catégories disponibles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouvel utilisateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes centres d'intérêt</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les catégories suivantes à 50%:</p>
|
||
<pre><code>| catégorie |
|
||
|---|
|
||
| Automobile |
|
||
| Voyage |
|
||
| Famille |
|
||
| Amour |
|
||
| Musique |
|
||
| Économie |
|
||
| Cryptomonnaie |
|
||
| Politique |
|
||
| Culture générale |
|
||
| Sport |
|
||
| Technologie |
|
||
| Santé |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="3-cold-start-premier-contenu-ecoute">3. Cold start - premier contenu écouté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de m'inscrire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que toutes mes jauges sont à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute mon premier podcast "Automobile" à 90%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Automobile" monte à 52% (+2%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les autres jauges restent à 50%</p>
|
||
<hr />
|
||
<h2 id="4-cold-start-premier-skip">4. Cold start - premier skip</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de m'inscrire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que toutes mes jauges sont à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je skip rapidement un contenu "Économie"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Économie" descend à 49.5% (-0.5%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les autres jauges restent à 50%</p>
|
||
<hr />
|
||
<h2 id="5-apres-10-ecoutes-profil-commence-a-se-dessiner">5. Après 10 écoutes, profil commence à se dessiner</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouvel utilisateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai écouté:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes jauges reflètent mes préférences:</p>
|
||
<pre><code>| 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%) |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="6-pas-de-questionnaire-onboarding-par-defaut">6. Pas de questionnaire onboarding par défaut</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je termine l'inscription</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun questionnaire de centres d'intérêt n'est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux commencer à écouter immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme apprend naturellement</p>
|
||
<hr />
|
||
<h2 id="7-algorithme-avec-jauges-a-50-chances-egales">7. Algorithme avec jauges à 50% - chances égales</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que toutes mes jauges sont à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les types de contenus ont une chance égale
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun biais initial n'est appliqué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la géolocalisation prime sur les intérêts</p>
|
||
<hr />
|
||
<h2 id="8-questionnaire-optionnel-apres-3-ecoutes-post-mvp">8. Questionnaire optionnel après 3 écoutes (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté 3 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je termine ma 3ème écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une notification in-app optionnelle:</p>
|
||
<pre><code>| titre | Améliorez vos recommandations |
|
||
|---|---|
|
||
| message | Sélectionnez vos centres d'intérêt |
|
||
| actions | Configurer maintenant / Plus tard |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-remplir-le-questionnaire-optionnel-post-mvp">9. Remplir le questionnaire optionnel (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le questionnaire optionnel est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne les centres d'intérêt suivants:</p>
|
||
<pre><code>| catégorie |
|
||
|---|
|
||
| Automobile |
|
||
| Voyage |
|
||
| Sport |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les jauges sélectionnées passent à 70%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les jauges non sélectionnées passent à 30%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vos préférences ont été enregistrées"</p>
|
||
<hr />
|
||
<h2 id="10-skipper-le-questionnaire-optionnel-post-mvp">10. Skipper le questionnaire optionnel (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le questionnaire optionnel est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Plus tard"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes mes jauges conservent 50%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme continue d'apprendre naturellement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne suis plus sollicité</p>
|
||
<hr />
|
||
<h2 id="11-comportement-deterministe-et-testable">11. Comportement déterministe et testable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> deux nouveaux utilisateurs A et B</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les deux s'inscrivent au même moment</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> leurs jauges sont identiques (toutes à 50%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> leurs recommandations initiales sont identiques (basées sur géo uniquement)</p>
|
||
<hr />
|
||
<h2 id="12-equite-entre-createurs-au-cold-start">12. Équité entre créateurs au cold start</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un nouvel utilisateur s'inscrit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il existe 1000 contenus de catégories variées dans sa zone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les premières recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les contenus ont une pondération intérêts identique (50%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls la géolocalisation et l'engagement différencient les contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun créateur n'a d'avantage initial</p>
|
||
<hr />
|
||
<h2 id="13-categories-extensibles">13. Catégories extensibles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave ajoute une nouvelle catégorie "Gastronomie"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes centres d'intérêt</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la nouvelle catégorie "Gastronomie" à 50%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux commencer à l'explorer normalement</p>
|
||
<hr />
|
||
<h2 id="14-voir-levolution-de-mes-jauges">14. Voir l'évolution de mes jauges</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur avec historique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes centres d'intérêt dans les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois mes jauges actuelles:</p>
|
||
<pre><code>| catégorie | niveau | evolution |
|
||
|---|---|---|
|
||
| Automobile | 67% | +17% |
|
||
| Voyage | 82% | +32% |
|
||
| Économie | 34% | -16% |
|
||
| Sport | 50% | 0% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je comprends mes préférences actuelles</p>
|
||
<hr />
|
||
<h2 id="15-friction-zero-a-linscription">15. Friction zéro à l'inscription</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux m'inscrire rapidement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je remplis les 4 champs obligatoires
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "S'inscrire"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est créé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux commencer à écouter dans les 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune configuration supplémentaire n'est requise</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="synchronisation-actions-offline">Synchronisation actions offline</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux que mes actions offline soient synchronisées quand je me reconnecte</em>
|
||
<em>Afin de ne perdre aucune interaction même sans connexion</em></p>
|
||
</blockquote>
|
||
<p><strong>45 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise l'application RoadWave</p>
|
||
</blockquote>
|
||
<h2 id="1-like-dun-contenu-en-mode-offline">1. Like d'un contenu en mode offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucune connexion Internet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je like un contenu téléchargé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est enregistrée localement dans SQLite:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'UI affiche immédiatement le like (optimistic update)</p>
|
||
<hr />
|
||
<h2 id="2-unlike-dun-contenu-en-mode-offline">2. Unlike d'un contenu en mode offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucune connexion Internet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'avais liké un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je retire mon like</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est enregistrée localement:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'UI retire immédiatement le like</p>
|
||
<hr />
|
||
<h2 id="3-abonnement-a-un-createur-en-mode-offline">3. Abonnement à un créateur en mode offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucune connexion Internet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'abonne à un créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est enregistrée localement:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'UI affiche immédiatement "Abonné ✓"</p>
|
||
<hr />
|
||
<h2 id="4-desabonnement-dun-createur-en-mode-offline">4. Désabonnement d'un créateur en mode offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucune connexion Internet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'étais abonné à un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me désabonne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est enregistrée localement:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'UI affiche "S'abonner"</p>
|
||
<hr />
|
||
<h2 id="5-signalement-dun-contenu-en-mode-offline">5. Signalement d'un contenu en mode offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucune connexion Internet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je signale un contenu pour "Contenu inapproprié"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est enregistrée localement:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Signalement enregistré. Sera envoyé à la reconnexion."</p>
|
||
<hr />
|
||
<h2 id="6-progression-audio-guide-en-mode-offline">6. Progression audio-guide en mode offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucune connexion Internet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute un audio-guide multi-séquences</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je termine la séquence 3/10</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progression est enregistrée localement:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma progression est sauvegardée</p>
|
||
<hr />
|
||
<h2 id="7-multiple-actions-offline-stockees-en-queue">7. Multiple actions offline stockées en queue</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucune connexion Internet pendant 2 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'effectue plusieurs actions:</p>
|
||
<pre><code>| action | cible |
|
||
|---|---|
|
||
| like | contenu A |
|
||
| like | contenu B |
|
||
| subscribe | créateur X |
|
||
| unlike | contenu C |
|
||
| report | contenu D |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 5 actions sont stockées dans pending_actions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elles seront synchronisées dans l'ordre à la reconnexion</p>
|
||
<hr />
|
||
<h2 id="8-detection-reconnexion-internet">8. Détection reconnexion Internet</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'étais en mode offline</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'app détecte une reconnexion Internet</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le processus de synchronisation démarre automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois une notification "Synchronisation en cours..."</p>
|
||
<hr />
|
||
<h2 id="9-recuperation-queue-locale-pendant-sync">9. Récupération queue locale pendant sync</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la synchronisation démarre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'app récupère les actions en attente</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une requête SQL est exécutée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les actions sont récupérées dans l'ordre chronologique</p>
|
||
<hr />
|
||
<h2 id="10-envoi-batch-api-des-actions">10. Envoi batch API des actions</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 15 actions sont en attente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le batch est envoyé au backend</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une requête POST /sync/actions est faite:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les actions sont groupées en une seule requête</p>
|
||
<hr />
|
||
<h2 id="11-backend-traite-chaque-action">11. Backend traite chaque action</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le backend reçoit le batch d'actions</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il traite chaque action</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> pour chaque action:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="12-confirmation-reception-et-suppression-queue-locale">12. Confirmation réception et suppression queue locale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le backend a traité toutes les actions avec succès</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la confirmation est reçue par l'app</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les actions sont supprimées de la queue locale:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la table pending_actions est vidée</p>
|
||
<hr />
|
||
<h2 id="13-toast-confirmation-synchronisation">13. Toast confirmation synchronisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 15 actions ont été synchronisées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la synchronisation se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un toast:</p>
|
||
<hr />
|
||
<h2 id="14-synchronisation-silencieuse-si-peu-dactions">14. Synchronisation silencieuse si peu d'actions</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai seulement 2 actions en attente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la synchronisation se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun toast n'est affiché (sync silencieuse)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience reste fluide
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je peux voir le détail dans l'historique des syncs</p>
|
||
<hr />
|
||
<h2 id="15-echec-synchronisation-retry-automatique">15. Échec synchronisation - Retry automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la synchronisation échoue (erreur réseau)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'échec est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un retry automatique est programmé dans 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les actions restent dans pending_actions</p>
|
||
<hr />
|
||
<h2 id="16-3-tentatives-echouees-notification-utilisateur">16. 3 tentatives échouées - Notification utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 3 tentatives de synchronisation ont échoué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la 3ème tentative échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification:</p>
|
||
<hr />
|
||
<h2 id="17-actions-conservees-jusqua-sync-reussie">17. Actions conservées jusqu'à sync réussie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la synchronisation échoue plusieurs fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les tentatives continuent d'échouer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les actions restent dans pending_actions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune action n'est perdue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elles seront envoyées dès que la connexion sera stable</p>
|
||
<hr />
|
||
<h2 id="18-retention-max-7-jours-purge-automatique">18. Rétention max 7 jours - Purge automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une action est en attente depuis 7 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette ancienneté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est automatiquement supprimée de la queue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "1 action trop ancienne supprimée (>7 jours)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela évite une queue infinie</p>
|
||
<hr />
|
||
<h2 id="19-justification-retention-7-jours">19. Justification rétention 7 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur ne se connecte jamais pendant 2 semaines</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ses actions ont >7 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elles sont purgées automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> évite une queue qui grandit indéfiniment</p>
|
||
<hr />
|
||
<h2 id="20-retry-manuel-apres-echec">20. Retry manuel après échec</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la synchronisation a échoué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Réessayer maintenant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une nouvelle tentative de synchronisation est lancée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si elle réussit, les actions sont synchronisées</p>
|
||
<hr />
|
||
<h2 id="21-backend-retourne-contenus-supprimes">21. Backend retourne contenus supprimés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai liké un contenu offline
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que le contenu a été supprimé entre temps</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le backend traite la synchronisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il retourne:</p>
|
||
<hr />
|
||
<h2 id="22-app-supprime-fichiers-locaux-contenus-supprimes">22. App supprime fichiers locaux contenus supprimés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le backend retourne deleted_content_ids: [123, 456]</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'app traite la réponse</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle supprime les fichiers locaux des contenus 123 et 456
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> libère l'espace disque
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les actions associées sont retirées de la queue</p>
|
||
<hr />
|
||
<h2 id="23-contenu-supprime-en-cours-decoute">23. Contenu supprimé en cours d'écoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute le contenu 123 en offline
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la sync détecte que le contenu a été supprimé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la lecture actuelle se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app attend 2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> passe automatiquement au contenu suivant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier du contenu 123 est supprimé en arrière-plan</p>
|
||
<hr />
|
||
<h2 id="24-toast-notification-contenu-retire">24. Toast notification contenu retiré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 2 contenus téléchargés ont été supprimés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la synchronisation se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un toast:</p>
|
||
<hr />
|
||
<h2 id="25-contenu-modere-apres-telechargement">25. Contenu modéré après téléchargement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé un contenu qui est ensuite modéré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la synchronisation détecte la modération</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est immédiatement supprimé du device
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux plus l'écouter
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela garantit la conformité même offline</p>
|
||
<hr />
|
||
<h2 id="26-justification-pas-de-conflit-possible">26. Justification pas de conflit possible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les actions offline sont unilatérales (likes, abonnements)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> elles sont synchronisées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il n'y a pas de conflit de version possible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> pas de merge complexe nécessaire</p>
|
||
<hr />
|
||
<h2 id="27-justification-ux-fluide-offline">27. Justification UX fluide offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que toutes les actions fonctionnent offline</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur interagit sans connexion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'expérience est identique au mode online
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur n'est pas bloqué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> peut utiliser l'app normalement</p>
|
||
<hr />
|
||
<h2 id="28-justification-batch-economie-requetes">28. Justification batch = Économie requêtes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 15 actions sont en attente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> elles sont synchronisées en batch</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 1 seule requête HTTP est envoyée (vs 15 si individuelles)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela économise la bande passante et la batterie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> réduit la charge serveur</p>
|
||
<hr />
|
||
<h2 id="29-justification-conformite-moderation-offline">29. Justification conformité modération offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu illégal est modéré pendant qu'un user est offline</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le user se reconnecte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est immédiatement supprimé de son device
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela garantit que les contenus illégaux disparaissent même offline</p>
|
||
<hr />
|
||
<h2 id="30-historique-synchronisations">30. Historique synchronisations</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à "Paramètres > Synchronisation"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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 ❌ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="31-detail-dune-synchronisation">31. Détail d'une synchronisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur une ligne de l'historique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le détail s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="32-compteur-actions-en-attente-visible">32. Compteur actions en attente visible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 12 actions en attente de synchronisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à l'onglet Profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un badge "12" sur l'icône de synchronisation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je sais qu'il y a des actions en attente</p>
|
||
<hr />
|
||
<h2 id="33-synchronisation-manuelle-forcee">33. Synchronisation manuelle forcée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux forcer une synchronisation immédiate</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vais dans "Paramètres > Synchronisation"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Synchroniser maintenant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la synchronisation démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les actions en attente sont envoyées</p>
|
||
<hr />
|
||
<h2 id="34-statistiques-utilisateur-syncs-effectuees">34. Statistiques utilisateur - Syncs effectuées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mes statistiques</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la section Synchronisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="35-statistiques-admin-volume-synchronisations">35. Statistiques admin - Volume synchronisations</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin consulte les métriques de synchronisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède au dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="36-alerte-admin-si-taux-echec-sync-10">36. Alerte admin si taux échec sync >10%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le taux d'échec sync dépasse 10%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette anomalie</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte est envoyée:</p>
|
||
<hr />
|
||
<h2 id="37-synchronisation-rapide-2s">37. Synchronisation rapide <2s</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 20 actions en attente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la synchronisation démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le traitement prend <2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne remarque aucun ralentissement de l'app</p>
|
||
<hr />
|
||
<h2 id="38-synchronisation-de-gros-batch-100-actions">38. Synchronisation de gros batch (100 actions)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai pas synchronisé pendant 1 semaine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai 100 actions en attente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la synchronisation démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le batch de 100 actions est traité en <5 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les actions sont synchronisées avec succès</p>
|
||
<hr />
|
||
<h2 id="39-gestion-charge-serveur-10-000-syncs-simultanees">39. Gestion charge serveur - 10 000 syncs simultanées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 10 000 utilisateurs se reconnectent simultanément</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> chacun envoie un batch de 20 actions</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le serveur traite 200 000 actions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> grâce au traitement asynchrone (queue Redis), le temps de réponse reste <3s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun timeout n'est constaté</p>
|
||
<hr />
|
||
<h2 id="40-stockage-sqlite-optimise">40. Stockage SQLite optimisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la table pending_actions stocke des centaines d'actions</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> des requêtes sont exécutées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la table est indexée sur created_at
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les requêtes SELECT et DELETE sont instantanées (<10ms)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience utilisateur reste fluide</p>
|
||
<hr />
|
||
<h2 id="41-nettoyage-automatique-table-pending_actions">41. Nettoyage automatique table pending_actions</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la table pending_actions grossit avec le temps</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les actions sont synchronisées et supprimées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la table est automatiquement optimisée (VACUUM sur SQLite)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace disque est libéré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les performances restent optimales</p>
|
||
<hr />
|
||
<h2 id="42-action-dupliquee-idempotence">42. Action dupliquée - Idempotence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai liké un contenu offline
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la sync échoue et retry</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le backend reçoit 2 fois le même like</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il applique l'idempotence (1 seul like enregistré)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur de likes n'est pas faussé</p>
|
||
<hr />
|
||
<h2 id="43-sequence-likeunlike-offline">43. Séquence like/unlike offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai liké puis unliké un contenu offline</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les 2 actions sont synchronisées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le backend applique les 2 actions dans l'ordre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le résultat final est "pas de like" (état correct)</p>
|
||
<hr />
|
||
<h2 id="44-abonnement-puis-desabonnement-offline">44. Abonnement puis désabonnement offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis abonné puis désabonné d'un créateur offline</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les 2 actions sont synchronisées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le backend applique les 2 actions dans l'ordre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le résultat final est "pas abonné"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les jauges évoluent correctement (+5% puis -5% = 0% net)</p>
|
||
<hr />
|
||
<h2 id="45-createur-supprime-pendant-offline">45. Créateur supprimé pendant offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me suis abonné à un créateur offline
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que le créateur a supprimé son compte entre temps</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la sync traite l'abonnement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le backend retourne "creator_deleted"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'action est ignorée silencieusement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune erreur n'est affichée à l'utilisateur</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="telechargement-de-contenus-offline">Téléchargement de contenus offline</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux télécharger des contenus pour les écouter sans connexion</em>
|
||
<em>Afin de profiter de RoadWave même dans les zones sans réseau</em></p>
|
||
</blockquote>
|
||
<p><strong>49 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté à l'application RoadWave</p>
|
||
</blockquote>
|
||
<h2 id="1-option-autour-de-moi-rayon-50-km">1. Option "Autour de moi" - Rayon 50 km</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à Paris (position GPS détectée)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Télécharger > Autour de moi"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app recherche tous les contenus géolocalisés dans un rayon de 50 km
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois une liste de contenus de Paris et banlieue proche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'estimation affiche "~150 contenus disponibles"</p>
|
||
<hr />
|
||
<h2 id="2-option-ma-ville-limite-administrative-detectee">2. Option "Ma ville" - Limite administrative détectée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis à Lyon (position GPS détectée)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Télécharger > Ma ville"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app détecte automatiquement "Lyon" comme ville
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> recherche tous les contenus géolocalisés "Lyon"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois uniquement les contenus de la ville de Lyon (pas banlieue)</p>
|
||
<hr />
|
||
<h2 id="3-option-mon-departement-selection-dans-liste">3. Option "Mon département" - Sélection dans liste</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux télécharger des contenus pour un département</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Télécharger > Mon département"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une liste de tous les départements français:</p>
|
||
<pre><code>| département |
|
||
|---|
|
||
| 01 - Ain |
|
||
| 02 - Aisne |
|
||
| 75 - Paris |
|
||
| 69 - Rhône |
|
||
| ... |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir un département</p>
|
||
<hr />
|
||
<h2 id="4-selection-departement-et-telechargement-contenus">4. Sélection département et téléchargement contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je sélectionne "75 - Paris" dans la liste des départements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la sélection est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app recherche tous les contenus géolocalisés "Paris"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "~234 contenus disponibles pour Paris"</p>
|
||
<hr />
|
||
<h2 id="5-option-ma-region-selection-dans-liste">5. Option "Ma région" - Sélection dans liste</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux télécharger des contenus pour une région</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Télécharger > Ma région"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une liste de toutes les régions françaises:</p>
|
||
<pre><code>| région |
|
||
|---|
|
||
| Auvergne-Rhône-Alpes |
|
||
| Bretagne |
|
||
| Île-de-France |
|
||
| Nouvelle-Aquitaine |
|
||
| Occitanie |
|
||
| ... |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir une région</p>
|
||
<hr />
|
||
<h2 id="6-selection-region-et-telechargement-contenus">6. Sélection région et téléchargement contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je sélectionne "Bretagne" dans la liste des régions</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la sélection est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app recherche tous les contenus géolocalisés des départements bretons:</p>
|
||
<pre><code>| département |
|
||
|---|
|
||
| Côtes-d'Armor (22) |
|
||
| Finistère (29) |
|
||
| Ille-et-Vilaine (35) |
|
||
| Morbihan (56) |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je vois "~487 contenus disponibles pour Bretagne"</p>
|
||
<hr />
|
||
<h2 id="7-recherche-manuelle-ville">7. Recherche manuelle ville</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux télécharger des contenus pour une ville spécifique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape "Marseille" dans la barre de recherche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app propose des suggestions:</p>
|
||
<pre><code>| suggestion |
|
||
|---|
|
||
| Marseille (13) |
|
||
| Marseille-en-Beauvaisis |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux sélectionner "Marseille (13)"</p>
|
||
<hr />
|
||
<h2 id="8-recherche-manuelle-avec-autocompletion">8. Recherche manuelle avec autocomplétion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je tape "Ly" dans la barre de recherche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'autocomplétion s'active</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois des suggestions:</p>
|
||
<pre><code>| suggestion |
|
||
|---|
|
||
| Lyon (69) |
|
||
| Lys-lez-Lannoy |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux affiner ma recherche</p>
|
||
<hr />
|
||
<h2 id="9-utilisateur-gratuit-limite-50-contenus-max">9. Utilisateur gratuit - Limite 50 contenus max</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai déjà téléchargé 45 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à la page Téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "45 / 50 contenus téléchargés"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux télécharger 5 contenus supplémentaires maximum</p>
|
||
<hr />
|
||
<h2 id="10-utilisateur-gratuit-tentative-depasser-limite-50">10. Utilisateur gratuit - Tentative dépasser limite 50</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis gratuit et j'ai déjà 50 contenus téléchargés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de télécharger un 51ème contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le téléchargement est refusé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message:</p>
|
||
<hr />
|
||
<h2 id="11-utilisateur-premium-telechargements-illimites">11. Utilisateur Premium - Téléchargements illimités</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai déjà téléchargé 245 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à la page Téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "245 contenus (3.2 GB)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune limite n'est affichée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux télécharger autant de contenus que je veux</p>
|
||
<hr />
|
||
<h2 id="12-limite-premium-espace-disque-disponible">12. Limite Premium = Espace disque disponible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon device a 500 MB d'espace disque disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de télécharger 100 contenus (2 GB)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le téléchargement échoue après ~50 contenus (500 MB)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Espace disque insuffisant. Libérez de l'espace pour continuer."</p>
|
||
<hr />
|
||
<h2 id="13-calcul-temps-ecoute-disponible-gratuit">13. Calcul temps écoute disponible gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis gratuit avec 50 contenus téléchargés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la durée moyenne d'un contenu est 5 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule le temps d'écoute disponible</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 50 contenus × 5 min = 250 minutes = 4h10 d'écoute
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela suffit pour un trajet quotidien ou road trip court</p>
|
||
<hr />
|
||
<h2 id="14-calcul-temps-ecoute-disponible-premium-illimite">14. Calcul temps écoute disponible Premium illimité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis Premium avec 300 contenus téléchargés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la durée moyenne est 5 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule le temps d'écoute disponible</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 300 contenus × 5 min = 1500 minutes = 25h d'écoute
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela suffit pour un road trip de plusieurs jours</p>
|
||
<hr />
|
||
<h2 id="15-telechargement-par-defaut-en-wifi-uniquement">15. Téléchargement par défaut en WiFi uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté en WiFi</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Télécharger 20 contenus"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le téléchargement démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune popup de confirmation n'apparaît</p>
|
||
<hr />
|
||
<h2 id="16-tentative-telechargement-en-donnees-mobiles-popup-confirmation">16. Tentative téléchargement en données mobiles - Popup confirmation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté en 4G (pas de WiFi)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Télécharger 20 contenus"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup apparaît:</p>
|
||
<hr />
|
||
<h2 id="17-calcul-estimation-consommation-data-mobile">17. Calcul estimation consommation data mobile</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux télécharger 20 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la durée moyenne est 5 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la qualité Standard est 48 kbps Opus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'estimation est calculée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> consommation = 20 contenus × 5 min × 48 kbps / 8 = 72 MB
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce montant est affiché dans la popup</p>
|
||
<hr />
|
||
<h2 id="18-confirmation-telechargement-en-donnees-mobiles">18. Confirmation téléchargement en données mobiles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je vois la popup de confirmation données mobiles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Continuer quand même"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le téléchargement démarre immédiatement via 4G
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la consommation data est comptabilisée sur mon forfait mobile</p>
|
||
<hr />
|
||
<h2 id="19-refus-telechargement-donnees-mobiles-attendre-wifi">19. Refus téléchargement données mobiles - Attendre WiFi</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je vois la popup de confirmation données mobiles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Attendre WiFi"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les téléchargements sont mis en file d'attente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ils démarreront automatiquement quand le WiFi sera détecté</p>
|
||
<hr />
|
||
<h2 id="20-detection-automatique-wifi-et-reprise-telechargements">20. Détection automatique WiFi et reprise téléchargements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai mis 20 contenus en file d'attente (attente WiFi)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'app détecte une connexion WiFi</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les téléchargements démarrent automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification "Téléchargements en cours via WiFi"</p>
|
||
<hr />
|
||
<h2 id="21-qualite-standard-48-kbps-par-defaut">21. Qualité Standard (48 kbps) par défaut</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je configure mes téléchargements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède aux paramètres de qualité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la qualité "Standard (48 kbps - ~20 MB/h)" est sélectionnée par défaut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est disponible pour tous (gratuit + Premium)</p>
|
||
<hr />
|
||
<h2 id="22-qualite-basse-24-kbps-disponible-pour-tous">22. Qualité Basse (24 kbps) disponible pour tous</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai peu d'espace disque disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne qualité "Basse (24 kbps - ~10 MB/h)"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes prochains téléchargements seront en 24 kbps
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace utilisé sera divisé par 2 par rapport à Standard
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette option est disponible pour gratuit + Premium</p>
|
||
<hr />
|
||
<h2 id="23-qualite-haute-64-kbps-reservee-premium">23. Qualité Haute (64 kbps) réservée Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les options de qualité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'option "Haute (64 kbps - ~30 MB/h)" est grisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "👑 Premium uniquement"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas la sélectionner</p>
|
||
<hr />
|
||
<h2 id="24-utilisateur-premium-peut-choisir-qualite-haute">24. Utilisateur Premium peut choisir qualité Haute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les options de qualité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'option "Haute (64 kbps - ~30 MB/h)" est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux la sélectionner pour mes téléchargements
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité audio sera excellente (meilleure restitution voix et ambiances)</p>
|
||
<hr />
|
||
<h2 id="25-comparaison-taille-fichiers-selon-qualite">25. Comparaison taille fichiers selon qualité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux télécharger 50 contenus de 5 min chacun</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je compare les qualités</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les tailles totales sont:</p>
|
||
<pre><code>| qualité | bitrate | taille totale |
|
||
|---|---|---|
|
||
| Basse | 24 kbps | ~250 MB |
|
||
| Standard | 48 kbps | ~500 MB |
|
||
| Haute | 64 kbps | ~650 MB |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="26-justification-standard-bon-compromis">26. Justification Standard = Bon compromis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu RoadWave est principalement de la voix</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la qualité Standard (48 kbps Opus) est utilisée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la qualité est très correcte pour la voix
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> équivalente à la radio FM
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compromis qualité/taille est optimal</p>
|
||
<hr />
|
||
<h2 id="27-justification-haute-reservee-premium-incitation-upgrade">27. Justification Haute réservée Premium = Incitation upgrade</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur gratuit veut la meilleure qualité</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il voit que Haute est réservée Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cela l'incite à passer Premium pour 4.99€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> c'est un avantage tangible supplémentaire de Premium</p>
|
||
<hr />
|
||
<h2 id="28-changement-qualite-apres-telechargements-existants">28. Changement qualité après téléchargements existants</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai déjà téléchargé 30 contenus en qualité Standard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je change la qualité vers Haute (si Premium)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 30 contenus existants restent en Standard
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les nouveaux téléchargements seront en Haute
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux manuellement re-télécharger les 30 contenus pour les avoir en Haute</p>
|
||
<hr />
|
||
<h2 id="29-telechargement-individuel-dun-contenu">29. Téléchargement individuel d'un contenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page d'un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur l'icône de téléchargement 📥</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le téléchargement démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une barre de progression apparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'icône devient ✅ quand terminé</p>
|
||
<hr />
|
||
<h2 id="30-telechargement-batch-de-contenus-selectionnes">30. Téléchargement batch de contenus sélectionnés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte une liste de contenus pour "Paris"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne 15 contenus manuellement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Télécharger la sélection"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 15 contenus sont téléchargés en parallèle (max 3 simultanés)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification affiche "15 contenus téléchargés"</p>
|
||
<hr />
|
||
<h2 id="31-telechargement-automatique-recommandations-zone">31. Téléchargement automatique recommandations zone</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je sélectionne "Autour de moi" (Paris)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Télécharger les 50 meilleurs contenus"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'algorithme sélectionne automatiquement les 50 contenus les mieux notés/récents
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les télécharge tous
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai pas besoin de choisir manuellement</p>
|
||
<hr />
|
||
<h2 id="32-barre-de-progression-telechargement-global">32. Barre de progression téléchargement global</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je télécharge 20 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les téléchargements sont en cours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une barre de progression globale:</p>
|
||
<hr />
|
||
<h2 id="33-telechargements-en-tache-de-fond">33. Téléchargements en tâche de fond</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je lance le téléchargement de 30 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je ferme l'app ou passe à une autre activité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les téléchargements continuent en arrière-plan
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification quand tous sont terminés</p>
|
||
<hr />
|
||
<h2 id="34-pause-et-reprise-telechargements">34. Pause et reprise téléchargements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je télécharge 20 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Pause"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les téléchargements en cours se terminent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les téléchargements en attente sont mis en pause
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer sur "Reprendre" plus tard</p>
|
||
<hr />
|
||
<h2 id="35-annulation-telechargements">35. Annulation téléchargements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je télécharge 20 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Annuler"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les téléchargements sont arrêtés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fichiers partiels sont supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace disque est libéré</p>
|
||
<hr />
|
||
<h2 id="36-gestion-erreurs-telechargement">36. Gestion erreurs téléchargement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je télécharge un contenu
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que la connexion Internet coupe au milieu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la connexion revient</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le téléchargement reprend automatiquement où il s'était arrêté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune perte de progression n'a lieu</p>
|
||
<hr />
|
||
<h2 id="37-retry-automatique-apres-echec">37. Retry automatique après échec</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un téléchargement échoue 3 fois consécutives</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'échec est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est marqué "Échec"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois une notification "3 contenus n'ont pas pu être téléchargés"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux retry manuellement en cliquant sur "Réessayer"</p>
|
||
<hr />
|
||
<h2 id="38-liste-contenus-telecharges">38. Liste contenus téléchargés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé 45 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à "Téléchargements"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste complète de mes 45 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> pour chaque contenu: titre, créateur, durée, taille, date téléchargement</p>
|
||
<hr />
|
||
<h2 id="39-tri-contenus-telecharges">39. Tri contenus téléchargés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte ma liste de téléchargements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Trier par"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux trier par:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="40-recherche-dans-contenus-telecharges">40. Recherche dans contenus téléchargés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 200 contenus téléchargés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je tape "Tesla" dans la barre de recherche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus contenant "Tesla" s'affichent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux rapidement trouver un contenu spécifique</p>
|
||
<hr />
|
||
<h2 id="41-suppression-individuelle-contenu-telecharge">41. Suppression individuelle contenu téléchargé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux supprimer un contenu téléchargé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je swipe left (iOS) ou long press (Android) sur le contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Supprimer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est supprimé du device
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace disque est libéré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur est décrémenté (ex: 45/50 → 44/50)</p>
|
||
<hr />
|
||
<h2 id="42-suppression-batch-contenus-telecharges">42. Suppression batch contenus téléchargés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux supprimer plusieurs contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne 10 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Supprimer la sélection"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 10 fichiers sont supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ~100 MB d'espace disque sont libérés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification confirme "10 contenus supprimés"</p>
|
||
<hr />
|
||
<h2 id="43-suppression-tous-les-contenus-telecharges">43. Suppression tous les contenus téléchargés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 45 contenus téléchargés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Supprimer tout"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je confirme l'action</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les 45 contenus sont supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace disque total est libéré (~450 MB)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur repasse à 0/50</p>
|
||
<hr />
|
||
<h2 id="44-espace-disque-utilise-visible">44. Espace disque utilisé visible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé 45 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à la page Téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'espace disque utilisé:</p>
|
||
<hr />
|
||
<h2 id="45-statistiques-telechargements">45. Statistiques téléchargements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mes statistiques</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la section Téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="46-lecture-contenu-telecharge-sans-connexion">46. Lecture contenu téléchargé sans connexion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucune connexion Internet (mode avion)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai des contenus téléchargés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance un contenu téléchargé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture démarre normalement depuis le fichier local
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune erreur de connexion n'apparaît</p>
|
||
<hr />
|
||
<h2 id="47-badge-telecharge-sur-contenus-offline">47. Badge "Téléchargé" sur contenus offline</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé certains contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte une liste de contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus téléchargés ont un badge ✅ "Offline"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je sais immédiatement lesquels sont disponibles sans connexion</p>
|
||
<hr />
|
||
<h2 id="48-filtre-telecharges-uniquement">48. Filtre "Téléchargés uniquement"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux voir uniquement mes contenus offline</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'active le filtre "Téléchargés uniquement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus téléchargés s'affichent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux facilement naviguer dans mon catalogue offline</p>
|
||
<hr />
|
||
<h2 id="49-playlist-offline-automatique">49. Playlist offline automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé 45 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à "Téléchargements"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux lancer une playlist aléatoire de mes 45 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> profiter d'une écoute continue offline</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="validite-et-renouvellement-contenus-offline">Validité et renouvellement contenus offline</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux que mes contenus téléchargés restent valides un certain temps</em>
|
||
<em>Afin de garantir la légalité et la fraîcheur du contenu</em></p>
|
||
</blockquote>
|
||
<p><strong>38 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté à l'application RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai des contenus téléchargés</p>
|
||
</blockquote>
|
||
<h2 id="1-validite-de-30-jours-apres-telechargement">1. Validité de 30 jours après téléchargement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je télécharge un contenu le 1er juin 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le téléchargement est terminé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est valide jusqu'au 1er juillet 2025 (30 jours)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la date d'expiration est stockée en local</p>
|
||
<hr />
|
||
<h2 id="2-affichage-date-expiration-sur-contenu-telecharge">2. Affichage date expiration sur contenu téléchargé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé un contenu il y a 20 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les détails du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "Expire dans 10 jours"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je sais combien de temps il reste avant expiration</p>
|
||
<hr />
|
||
<h2 id="3-standard-industrie-aligne-spotify-youtube-deezer">3. Standard industrie aligné (Spotify, YouTube, Deezer)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Spotify, YouTube Music et Deezer utilisent 30 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe également 30 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> c'est le standard accepté par les utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de confusion avec les autres plateformes</p>
|
||
<hr />
|
||
<h2 id="4-justification-30-jours-force-reconnexion-reguliere">4. Justification 30 jours - Force reconnexion régulière</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur ne se connecte jamais</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ses contenus expirent après 30 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est obligé de se reconnecter pour les renouveler
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système peut vérifier:</p>
|
||
<pre><code>| vérification |
|
||
|---|
|
||
| Abonnement Premium toujours actif |
|
||
| Contenus non modérés/supprimés |
|
||
| Métadonnées à jour |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="5-justification-30-jours-evite-stockage-obsolete">5. Justification 30 jours - Évite stockage obsolète</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a été modéré après téléchargement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu expire après 30 jours maximum</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu illégal est automatiquement supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ne reste pas indéfiniment sur le device</p>
|
||
<hr />
|
||
<h2 id="6-detection-wifi-et-contenus-25-jours">6. Détection WiFi et contenus >25 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai des contenus téléchargés il y a 26 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'app détecte une connexion WiFi</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une requête GET /offline/contents/refresh est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le backend vérifie chaque contenu</p>
|
||
<hr />
|
||
<h2 id="7-verification-abonnement-premium-toujours-actif">7. Vérification abonnement Premium toujours actif</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu téléchargé en Premium est à renouveler</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le backend vérifie le statut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'abonnement Premium est toujours actif</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validité est renouvelée à 30 jours supplémentaires</p>
|
||
<hr />
|
||
<h2 id="8-abonnement-premium-expire-contenu-non-renouvele">8. Abonnement Premium expiré - Contenu non renouvelé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu Premium téléchargé est à renouveler</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le backend vérifie le statut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'abonnement Premium a expiré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu n'est pas renouvelé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il sera supprimé à l'expiration (J-0)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur voit "Contenu Premium expiré (abonnement inactif)"</p>
|
||
<hr />
|
||
<h2 id="9-verification-contenu-pas-moderesupprime">9. Vérification contenu pas modéré/supprimé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu téléchargé est à renouveler</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le backend vérifie le statut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu a été modéré ou supprimé entre temps</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu n'est pas renouvelé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sera supprimé immédiatement du device
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur voit "1 contenu retiré (violation règles)"</p>
|
||
<hr />
|
||
<h2 id="10-mise-a-jour-metadonnees-lors-du-renouvellement">10. Mise à jour métadonnées lors du renouvellement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu téléchargé est renouvelé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le backend traite le renouvellement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métadonnées sont mises à jour:</p>
|
||
<pre><code>| métadonnée | mise à jour si changée |
|
||
|---|---|
|
||
| Titre | ✅ |
|
||
| Nom créateur | ✅ |
|
||
| Description | ✅ |
|
||
| Tags | ✅ |
|
||
| Statut Premium | ✅ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur voit les infos à jour</p>
|
||
<hr />
|
||
<h2 id="11-pas-de-re-telechargement-audio-si-fichier-ok">11. Pas de re-téléchargement audio si fichier OK</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est renouvelé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le fichier audio local est intact</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules les métadonnées sont mises à jour
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier audio n'est pas re-téléchargé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela économise la bande passante</p>
|
||
<hr />
|
||
<h2 id="12-re-telechargement-audio-si-fichier-corrompu">12. Re-téléchargement audio si fichier corrompu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est renouvelé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le fichier audio local est corrompu (checksum invalide)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier audio est re-téléchargé entièrement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le nouveau fichier remplace le corrompu</p>
|
||
<hr />
|
||
<h2 id="13-renouvellement-silencieux-si-wifi-regulier">13. Renouvellement silencieux si WiFi régulier</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me connecte en WiFi tous les jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mes contenus atteignent 25-30 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ils sont automatiquement renouvelés en arrière-plan
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne vois aucune notification (processus transparent)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes contenus restent valides indéfiniment</p>
|
||
<hr />
|
||
<h2 id="14-renouvellement-batch-de-plusieurs-contenus">14. Renouvellement batch de plusieurs contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 30 contenus à renouveler</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le renouvellement automatique se déclenche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une requête batch est envoyée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le backend traite les 30 contenus en une seule requête
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela économise les requêtes HTTP</p>
|
||
<hr />
|
||
<h2 id="15-temps-de-traitement-renouvellement">15. Temps de traitement renouvellement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 30 contenus sont à renouveler</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la requête batch est traitée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le backend répond en <2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les métadonnées sont mises à jour localement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur ne remarque aucun ralentissement</p>
|
||
<hr />
|
||
<h2 id="16-notification-j-3-avant-expiration">16. Notification J-3 avant expiration</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 15 contenus qui expirent dans 3 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système vérifie les expirations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux agir avant l'expiration</p>
|
||
<hr />
|
||
<h2 id="17-pas-de-notification-si-connexion-wifi-reguliere">17. Pas de notification si connexion WiFi régulière</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me connecte en WiFi tous les jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes contenus sont automatiquement renouvelés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système vérifie les expirations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification J-3 n'est envoyée</p>
|
||
<hr />
|
||
<h2 id="18-notification-uniquement-si-contenus-non-renouveles">18. Notification uniquement si contenus non renouvelés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 20 contenus dont 15 renouvelés et 5 non renouvelés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le J-3 arrive pour les 5 non renouvelés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois "5 contenus expirent dans 3 jours"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les contenus à risque sont mentionnés</p>
|
||
<hr />
|
||
<h2 id="19-action-utilisateur-apres-notification-j-3">19. Action utilisateur après notification J-3</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je reçois la notification J-3</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur la notification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app s'ouvre sur la page Téléchargements
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois les contenus qui vont expirer en rouge
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux me connecter en WiFi pour les renouveler</p>
|
||
<hr />
|
||
<h2 id="20-suppression-automatique-j-0-expiration">20. Suppression automatique J-0 (expiration)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu n'a pas été renouvelé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le jour d'expiration arrive (J-0)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est automatiquement supprimé du device
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace disque est libéré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur est décrémenté (ex: 45/50 → 44/50)</p>
|
||
<hr />
|
||
<h2 id="21-toast-apres-suppression-automatique-j-0">21. Toast après suppression automatique J-0</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 15 contenus viennent d'expirer</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur ouvre l'app</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit un toast:</p>
|
||
<hr />
|
||
<h2 id="22-liste-contenus-supprimes-apres-expiration">22. Liste contenus supprimés après expiration</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 15 contenus ont expiré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'historique des suppressions</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste des 15 contenus supprimés:</p>
|
||
<pre><code>| titre | créateur | date expiration |
|
||
|---|---|---|
|
||
| Mon épisode préféré | JeanDupont | 15 juin 2025 |
|
||
| Road trip Bretagne | MarieLambert | 15 juin 2025 |
|
||
| ... | ... | ... |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux les re-télécharger si je veux</p>
|
||
<hr />
|
||
<h2 id="23-re-telechargement-apres-expiration">23. Re-téléchargement après expiration</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a expiré et été supprimé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je retrouve ce contenu dans l'app</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge ✅ "Offline" n'est plus affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux le re-télécharger normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la validité repart à 30 jours</p>
|
||
<hr />
|
||
<h2 id="24-utilisateur-ne-se-connecte-jamais-pendant-30-jours">24. Utilisateur ne se connecte jamais pendant 30 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je télécharge 50 contenus le 1er juin
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que je ne me connecte jamais en WiFi pendant 30 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 1er juillet arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les 50 contenus expirent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sont automatiquement supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai plus aucun contenu offline</p>
|
||
<hr />
|
||
<h2 id="25-utilisateur-en-zone-blanche-30-jours">25. Utilisateur en zone blanche 30+ jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je télécharge 50 contenus avant de partir en zone sans réseau
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je reste 45 jours sans connexion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les contenus expirent après 30 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ils sont supprimés même si je ne peux pas me connecter
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je perds l'accès à mes contenus offline</p>
|
||
<hr />
|
||
<h2 id="26-recommandation-telechargement-avant-zone-blanche-longue">26. Recommandation téléchargement avant zone blanche longue</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je prépare un road trip de 60 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la FAQ</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la recommandation:</p>
|
||
<hr />
|
||
<h2 id="27-changement-statut-premium-en-gratuit-pendant-validite">27. Changement statut Premium en gratuit pendant validité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis Premium et j'ai téléchargé 200 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon abonnement Premium expire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je repasse en gratuit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> au prochain renouvellement, seulement 50 contenus sont conservés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 150 autres sont supprimés (limite gratuit)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Limite gratuit (50 contenus) appliquée. 150 contenus supprimés."</p>
|
||
<hr />
|
||
<h2 id="28-selection-automatique-50-meilleurs-contenus-si-passage-gratuit">28. Sélection automatique 50 meilleurs contenus si passage gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je repasse en gratuit avec 200 contenus téléchargés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système applique la limite de 50</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 50 contenus les plus récemment écoutés sont conservés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 150 autres sont supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela maximise les chances de garder les contenus que j'aime</p>
|
||
<hr />
|
||
<h2 id="29-contenus-premium-exclusifs-supprimes-si-abonnement-expire">29. Contenus Premium exclusifs supprimés si abonnement expire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé 20 contenus Premium exclusifs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon abonnement Premium expire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 20 contenus Premium sont immédiatement supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "20 contenus Premium supprimés (abonnement expiré)"</p>
|
||
<hr />
|
||
<h2 id="30-affichage-temps-restant-avant-expiration">30. Affichage temps restant avant expiration</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 45 contenus téléchargés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la page Téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois pour chaque contenu:</p>
|
||
<pre><code>| contenu | temps restant |
|
||
|---|---|
|
||
| Mon épisode (récent) | Expire dans 28 jours |
|
||
| Road trip (ancien) | Expire dans 3 jours |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je sais lesquels sont prioritaires pour renouvellement</p>
|
||
<hr />
|
||
<h2 id="31-tri-par-date-expiration">31. Tri par date expiration</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 45 contenus avec différentes dates d'expiration</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je trie par "Expiration"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus qui expirent le plus tôt apparaissent en premier
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux voir rapidement lesquels nécessitent une reconnexion urgente</p>
|
||
<hr />
|
||
<h2 id="32-badge-rouge-si-expiration-3-jours">32. Badge rouge si expiration <3 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu expire dans 2 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la liste des téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu a un badge rouge "⚠️ Expire bientôt"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est visuellement mis en avant</p>
|
||
<hr />
|
||
<h2 id="33-statistiques-utilisateur-taux-de-renouvellement">33. Statistiques utilisateur - Taux de renouvellement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mes statistiques</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la section Téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| métrique | valeur |
|
||
|---|---|
|
||
| Contenus actuels | 45 |
|
||
| Contenus expirés depuis début | 87 |
|
||
| Contenus renouvelés (auto) | 234 |
|
||
| Taux renouvellement automatique | 73% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="34-statistiques-admin-taux-expiration-global">34. Statistiques admin - Taux expiration global</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin consulte les métriques offline</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède au dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="35-alerte-admin-si-taux-expiration-10">35. Alerte admin si taux expiration >10%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le taux d'expiration mensuel dépasse 10%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette anomalie</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte est envoyée:</p>
|
||
<hr />
|
||
<h2 id="36-email-rappel-si-pas-de-connexion-wifi-depuis-20-jours">36. Email rappel si pas de connexion WiFi depuis 20 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai pas connecté l'app en WiFi depuis 20 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai 45 contenus téléchargés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette inactivité WiFi</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="37-performance-renouvellement-avec-10-000-utilisateurs-simultanes">37. Performance renouvellement avec 10 000 utilisateurs simultanés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 10 000 utilisateurs se connectent en WiFi simultanément</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> chacun demande le renouvellement de 50 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le serveur traite 500 000 vérifications
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> grâce au cache Redis et index PostgreSQL, le temps de réponse reste <3s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les serveurs gèrent la charge sans problème</p>
|
||
<hr />
|
||
<h2 id="38-logs-audit-renouvellements">38. Logs audit renouvellements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est renouvelé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'opération se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un log est enregistré:</p>
|
||
<pre><code>| 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) |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces logs aident à débugger les problèmes</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="moderation-preventive_1">Modération préventive</h1>
|
||
<p><strong>22 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de modération préventive est actif</p>
|
||
</blockquote>
|
||
<h2 id="1-createur-nouvellement-inscrit">1. Créateur nouvellement inscrit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de créer un compte créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai jamais publié de contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'examine mon statut de créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est marqué comme "Nouveau créateur"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes 3 premiers contenus devront être validés manuellement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis informé de ce processus lors de l'onboarding</p>
|
||
<hr />
|
||
<h2 id="2-publication-du-premier-contenu-par-un-nouveau-createur">2. Publication du premier contenu par un nouveau créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouveau créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai publié aucun contenu auparavant</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie mon premier contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu entre en file d'attente de validation manuelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut du contenu est "En attente de validation"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu n'est pas diffusé sur la plateforme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification:</p>
|
||
<hr />
|
||
<h2 id="3-validation-manuelle-par-un-moderateur">3. Validation manuelle par un modérateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié mon premier contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu est en attente de validation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un modérateur examine mon contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le modérateur utilise la transcription automatique Whisper
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le modérateur vérifie:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> si tout est conforme, le contenu est validé</p>
|
||
<hr />
|
||
<h2 id="4-delai-de-validation-de-24-48h-jours-ouvres">4. Délai de validation de 24-48h jours ouvrés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié mon premier contenu lundi à 10:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu entre en file de validation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est validé avant mercredi 10:00 (48h jours ouvrés)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> dans la plupart des cas, la validation est effectuée sous 24h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification dès que le contenu est validé</p>
|
||
<hr />
|
||
<h2 id="5-notification-de-validation-reussie">5. Notification de validation réussie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon premier contenu a été validé par un modérateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la validation est approuvée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut du contenu passe à "Publié"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu devient visible pour tous les utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il entre dans l'algorithme de recommandation</p>
|
||
<hr />
|
||
<h2 id="6-refus-de-validation-si-contenu-non-conforme">6. Refus de validation si contenu non conforme</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon premier contenu viole les règles de la communauté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur examine le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est refusé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification détaillée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu reste en statut "Refusé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux modifier et republier</p>
|
||
<hr />
|
||
<h2 id="7-les-3-premiers-contenus-sont-valides-manuellement">7. Les 3 premiers contenus sont validés manuellement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouveau créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie mes contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus suivants nécessitent une validation manuelle:</p>
|
||
<pre><code>| contenu | validation manuelle |
|
||
|---|---|
|
||
| 1er | oui |
|
||
| 2ème | oui |
|
||
| 3ème | oui |
|
||
| 4ème | non (auto) |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> après 3 contenus validés, mes futurs contenus sont publiés automatiquement</p>
|
||
<hr />
|
||
<h2 id="8-passage-en-mode-automatique-apres-3-validations">8. Passage en mode automatique après 3 validations</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes 3 premiers contenus ont été validés avec succès</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie mon 4ème contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est publié automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune validation manuelle n'est requise
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut passe directement à "Publié"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification:</p>
|
||
<hr />
|
||
<h2 id="9-evolution-du-score-de-confiance">9. Évolution du score de confiance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur établi</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système évalue mon historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un score de confiance est calculé basé sur:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le score évolue dynamiquement</p>
|
||
<hr />
|
||
<h2 id="10-createur-fiable-publication-automatique">10. Créateur fiable - Publication automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai 0 strike depuis 6 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que tous mes contenus précédents ont été conformes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon score de confiance est calculé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis classé comme "Créateur fiable"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous mes nouveaux contenus sont publiés automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune validation manuelle n'est nécessaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je bénéficie d'une publication instantanée</p>
|
||
<hr />
|
||
<h2 id="11-createur-suspect-validation-manuelle-systematique">11. Créateur suspect - Validation manuelle systématique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu 2 strikes récents (< 3 mois)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon score de confiance est recalculé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis classé comme "Créateur suspect"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous mes nouveaux contenus nécessitent une validation manuelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque contenu est examiné avant publication
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis notifié de ce changement de statut:</p>
|
||
<hr />
|
||
<h2 id="12-rehabilitation-apres-periode-sans-incident">12. Réhabilitation après période sans incident</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'étais un "Créateur suspect"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je publie 10 contenus conformes sur 6 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je ne reçois aucun nouveau strike</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système réévalue mon score de confiance</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je passe en "Créateur fiable"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publication automatique est rétablie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification de réhabilitation:</p>
|
||
<hr />
|
||
<h2 id="13-toute-publicite-necessite-validation-manuelle">13. Toute publicité nécessite validation manuelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un annonceur soumet une publicité audio</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la publicité est créée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle entre automatiquement en file de validation manuelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune publicité n'est diffusée sans validation préalable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela est obligatoire pour des raisons de responsabilité juridique</p>
|
||
<hr />
|
||
<h2 id="14-validation-dune-publicite-processus-complet">14. Validation d'une publicité - Processus complet</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité est en attente de validation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un modérateur senior examine la publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le modérateur vérifie:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> si tout est conforme, la publicité est validée</p>
|
||
<hr />
|
||
<h2 id="15-delai-de-validation-dune-publicite-24-48h">15. Délai de validation d'une publicité - 24-48h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un annonceur soumet une publicité lundi à 10:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la publicité entre en file de validation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité est validée avant mercredi 10:00 (48h jours ouvrés)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'annonceur est notifié dès la validation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne publicitaire peut alors démarrer</p>
|
||
<hr />
|
||
<h2 id="16-refus-de-validation-dune-publicite">16. Refus de validation d'une publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité contient des éléments non conformes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur examine la publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité est refusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'annonceur reçoit une notification détaillée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'annonceur peut modifier et resoumettre la publicité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun remboursement n'est effectué pour une publicité refusée</p>
|
||
<hr />
|
||
<h2 id="17-economie-de-moderation-grace-a-la-prevention">17. Économie de modération grâce à la prévention</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la modération préventive est active</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on analyse l'efficacité du système</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 80% des contenus inappropriés sont détectés avant publication
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela réduit le nombre de signalements de 70%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les ressources de modération sont optimisées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité de la plateforme est préservée dès le début</p>
|
||
<hr />
|
||
<h2 id="18-qualite-de-la-plateforme-maintenue">18. Qualité de la plateforme maintenue</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que tous les nouveaux créateurs sont vérifiés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on analyse la qualité globale des contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le taux de contenus inappropriés est <1%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs font confiance à la plateforme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la réputation de RoadWave est préservée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience utilisateur est optimale</p>
|
||
<hr />
|
||
<h2 id="19-information-claire-sur-le-processus-de-validation">19. Information claire sur le processus de validation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouveau créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la page d'aide "Validation des contenus"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> j'apprends que:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le processus est clair et transparent</p>
|
||
<hr />
|
||
<h2 id="20-badge-createur-verifie-apres-validation">20. Badge "Créateur vérifié" après validation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes 3 premiers contenus ont été validés avec succès</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mon profil créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un badge discret "✓ Créateur vérifié" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce badge rassure les auditeurs sur la qualité de mes contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il améliore ma crédibilité sur la plateforme</p>
|
||
<hr />
|
||
<h2 id="21-justification-de-la-moderation-preventive">21. Justification de la modération préventive</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la modération préventive est en place</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on évalue les bénéfices</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les avantages suivants sont constatés:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> l'investissement dans la prévention est rentable</p>
|
||
<hr />
|
||
<h2 id="22-cout-de-la-moderation-preventive">22. Coût de la modération préventive</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 nouveaux créateurs publient 3 contenus chacun
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 50 publicités sont soumises par mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût de modération préventive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût en temps modérateur est:</p>
|
||
<pre><code>| type | nombre | temps/contenu | total |
|
||
|---|---|---|---|
|
||
| Nouveaux créateurs | 300 | 5 min | 25h |
|
||
| Publicités | 50 | 10 min | 8.3h |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le coût total est d'environ 33h de modération/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> c'est largement compensé par la réduction des signalements réactifs</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="sanctions-et-notifications-de-moderation">Sanctions et notifications de modération</h1>
|
||
<p><strong>27 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur de contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai publié un contenu</p>
|
||
</blockquote>
|
||
<h2 id="1-notification-multi-canal-apres-sanction">1. Notification multi-canal après sanction</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu a été modéré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la sanction est appliquée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification sur 3 canaux:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque canal contient un lien vers les détails complets</p>
|
||
<hr />
|
||
<h2 id="2-notification-push-immediate">2. Notification push immédiate</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu vient d'être modéré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la sanction est appliquée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push immédiate
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message est court: "⚠️ Votre contenu a été modéré"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer pour voir les détails
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la notification utilise Firebase Cloud Messaging (Android) ou APNs (iOS)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût est de 0€</p>
|
||
<hr />
|
||
<h2 id="3-popup-in-app-au-prochain-lancement">3. Popup in-app au prochain lancement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu a été modéré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup détaillée s'affiche automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la popup contient:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas fermer la popup sans l'avoir vue</p>
|
||
<hr />
|
||
<h2 id="4-email-de-notification-complet-dans-lheure">4. Email de notification complet dans l'heure</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu a été modéré à 14:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la sanction est appliquée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email avant 15:00 (dans l'heure)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'objet de l'email est "Modération de votre contenu \"[Titre du contenu]\""
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'email contient toutes les informations détaillées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût est d'environ 0.001€ par email (Brevo, Resend)</p>
|
||
<hr />
|
||
<h2 id="5-email-de-notification-complet-et-structure">5. Email de notification complet et structuré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon contenu "Mon podcast #42" a été modéré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois l'email de notification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'email contient la structure suivante:</p>
|
||
<hr />
|
||
<h2 id="6-page-detaillee-de-la-sanction-in-app">6. Page détaillée de la sanction in-app</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Voir détails" dans la notification</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page détaillée s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les 6 éléments obligatoires:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="7-affichage-du-passage-problematique-avec-timestamp">7. Affichage du passage problématique avec timestamp</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la page détaillée de la sanction est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'extrait audio concerné</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le timestamp exact est affiché: "3:42-4:15"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux écouter uniquement cette portion de l'audio
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un player audio intégré permet l'écoute du passage
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transcription correspondante est affichée en dessous
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les mots/phrases problématiques sont surlignés en rouge</p>
|
||
<hr />
|
||
<h2 id="8-reference-precise-aux-cgu">8. Référence précise aux CGU</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la sanction fait référence à l'Article 3.2 des CGU</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Article 3.2"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers la section correspondante des CGU
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la section "Haine & violence" est mise en évidence
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux lire exactement ce qui est interdit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela m'aide à comprendre mon erreur</p>
|
||
<hr />
|
||
<h2 id="9-gravite-de-la-sanction-avec-systeme-de-strikes">9. Gravité de la sanction avec système de strikes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que c'est mon 2ème strike</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les détails de la sanction</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois clairement "Strike 2/4"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les conséquences sont explicitées:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je comprends l'escalade des sanctions</p>
|
||
<hr />
|
||
<h2 id="10-acces-au-formulaire-dappel-depuis-la-notification">10. Accès au formulaire d'appel depuis la notification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu une notification de modération</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Contester cette décision"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers le formulaire d'appel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le formulaire est pré-rempli avec les informations de la sanction
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux commencer à rédiger mon appel</p>
|
||
<hr />
|
||
<h2 id="11-acces-au-formulaire-dappel-depuis-mes-sanctions">11. Accès au formulaire d'appel depuis "Mes sanctions"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu une sanction il y a 2 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre "Profil créateur > Mes sanctions"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste de mes sanctions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque sanction a un bouton "Faire appel" (si délai <7j)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accéder au formulaire d'appel</p>
|
||
<hr />
|
||
<h2 id="12-structure-du-formulaire-dappel">12. Structure du formulaire d'appel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ouvre le formulaire d'appel</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le formulaire s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les champs suivants:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> tous les champs obligatoires sont marqués d'un astérisque</p>
|
||
<hr />
|
||
<h2 id="13-validation-du-formulaire-dappel">13. Validation du formulaire d'appel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je remplis le formulaire d'appel</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Soumettre l'appel"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système valide les champs obligatoires
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si un champ obligatoire est vide, une erreur s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si la raison fait moins de 50 caractères, une erreur s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si tout est valide, l'appel est soumis</p>
|
||
<hr />
|
||
<h2 id="14-confirmation-apres-soumission-de-lappel">14. Confirmation après soumission de l'appel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai soumis un appel valide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'appel est enregistré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un numéro de ticket unique est généré: "#MOD-2026-00142"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un email de confirmation est envoyé:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut de l'appel est "En cours d'examen"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux suivre le statut dans "Mes sanctions"</p>
|
||
<hr />
|
||
<h2 id="15-delai-de-soumission-de-7-jours-maximum">15. Délai de soumission de 7 jours maximum</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu une sanction le 2026-01-15</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de faire appel le 2026-01-25 (10 jours plus tard)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le formulaire d'appel est désactivé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux plus contester la sanction</p>
|
||
<hr />
|
||
<h2 id="16-bouton-faire-appel-visible-si-delai-respecte">16. Bouton "Faire appel" visible si délai respecté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu une sanction il y a 3 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte "Mes sanctions"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Faire appel" est actif
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un compteur indique "4 jours restants pour faire appel"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer pour soumettre un appel</p>
|
||
<hr />
|
||
<h2 id="17-sla-de-72h-garanti-pour-appel-standard">17. SLA de 72h garanti pour appel standard</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai soumis un appel standard le lundi à 10:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'appel est en cours de traitement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un modérateur senior est assigné
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'appel doit être traité avant jeudi 10:00 (72h - 3 jours ouvrés)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une réponse dans ce délai</p>
|
||
<hr />
|
||
<h2 id="18-appel-complexe-avec-notification-intermediaire">18. Appel complexe avec notification intermédiaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai soumis un appel complexe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le traitement nécessite plus de 72h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 3 jours se sont écoulés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email de notification intermédiaire:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'appel est traité sous 5 jours ouvrés au total
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un modérateur senior + admin modération examinent le cas</p>
|
||
<hr />
|
||
<h2 id="19-appel-critique-traite-en-24h">19. Appel CRITIQUE traité en 24h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu une suspension longue ou un ban
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je soumets un appel</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'appel est classé en priorité CRITIQUE</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'admin modération traite l'appel sous 24h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une réponse rapide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cas est examiné en priorité absolue</p>
|
||
<hr />
|
||
<h2 id="20-reponse-finale-detaillee-appel-accepte">20. Réponse finale détaillée - Appel accepté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon appel est accepté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois la réponse finale</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'email contient:</p>
|
||
<pre><code>| é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" |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le strike est retiré de mon compte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est rétabli sur la plateforme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer normalement</p>
|
||
<hr />
|
||
<h2 id="21-reponse-finale-detaillee-appel-rejete">21. Réponse finale détaillée - Appel rejeté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon appel est rejeté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois la réponse finale</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'email contient:</p>
|
||
<pre><code>| é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" |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> la sanction reste active
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas faire de second appel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois respecter la suspension</p>
|
||
<hr />
|
||
<h2 id="22-reponse-finale-reduction-de-sanction">22. Réponse finale - Réduction de sanction</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon appel est partiellement accepté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois la réponse finale</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la décision est "Réduction de sanction"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'email explique:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le strike est réduit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la suspension est raccourcie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis notifié de la nouvelle date de fin</p>
|
||
<hr />
|
||
<h2 id="23-suivi-du-statut-de-lappel-in-app">23. Suivi du statut de l'appel in-app</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai soumis un appel</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte "Mes sanctions"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le statut actuel de l'appel:</p>
|
||
<pre><code>| statut | badge | couleur |
|
||
|---|---|---|
|
||
| En cours d'examen | En cours 🔍 | orange |
|
||
| Appel accepté | Accepté ✓ | vert |
|
||
| Appel rejeté | Rejeté ✗ | rouge |
|
||
| Sanction réduite | Partiellement accepté | bleu |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> une notification badge m'alerte quand le statut change</p>
|
||
<hr />
|
||
<h2 id="24-historique-complet-des-sanctions-visible">24. Historique complet des sanctions visible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre "Profil créateur > Mes sanctions"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste complète de mes sanctions passées:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> les sanctions sont triées par date décroissante</p>
|
||
<hr />
|
||
<h2 id="25-conformite-dsa-transparence-obligatoire">25. Conformité DSA - Transparence obligatoire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de sanction est en place</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un audit DSA est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> chaque sanction contient:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le système est conforme au Digital Services Act</p>
|
||
<hr />
|
||
<h2 id="26-decision-definitive-apres-premier-appel">26. Décision définitive après premier appel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon premier appel a été rejeté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de faire un second appel</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Faire appel" est désactivé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message s'affiche: "Cette décision est définitive. Aucun second appel n'est possible."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux plus contester la sanction
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois respecter la décision finale</p>
|
||
<hr />
|
||
<h2 id="27-cout-des-notifications-multi-canal">27. Coût des notifications multi-canal</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 sanctions sont appliquées en un mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût des notifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût total est d'environ 0.10€:</p>
|
||
<pre><code>| canal | coût unitaire | coût pour 100 |
|
||
|---|---|---|
|
||
| Email | 0.001€ | 0.10€ |
|
||
| Push | 0€ | 0€ |
|
||
| In-app | 0€ | 0€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le coût est négligeable même à grande échelle</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="signalement-de-contenu-inapproprie">Signalement de contenu inapproprié</h1>
|
||
<p><strong>23 scénarios</strong> (22 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis en train d'écouter un contenu</p>
|
||
</blockquote>
|
||
<h2 id="1-affichage-du-formulaire-de-signalement">1. Affichage du formulaire de signalement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu inapproprié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre le menu du contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Signaler"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un formulaire de signalement s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le formulaire contient une liste déroulante "Catégorie du problème"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le formulaire contient un champ texte "Commentaire (optionnel)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le formulaire contient un bouton "Envoyer le signalement"</p>
|
||
<hr />
|
||
<h2 id="2-liste-des-7-categories-predefinies">2. Liste des 7 catégories prédéfinies</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le formulaire de signalement est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur la liste déroulante "Catégorie du problème"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les 7 catégories suivantes:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque catégorie a une description claire</p>
|
||
<hr />
|
||
<h2 id="3-selection-de-la-categorie-haine-violence">3. Sélection de la catégorie "Haine & violence"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le formulaire de signalement est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne la catégorie "🚫 Haine & violence"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la catégorie est sélectionnée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la description "Incitation à la haine, discrimination, menaces" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux passer au champ commentaire</p>
|
||
<hr />
|
||
<h2 id="4-categorie-autre-necessite-un-commentaire-obligatoire">4. Catégorie "Autre" nécessite un commentaire obligatoire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le formulaire de signalement est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne la catégorie "🔧 Autre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le champ "Commentaire" devient obligatoire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message s'affiche: "Veuillez décrire le problème (obligatoire)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le placeholder change en "Décrivez le problème rencontré"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas envoyer le signalement sans commentaire</p>
|
||
<hr />
|
||
<h2 id="5-champ-commentaire-optionnel-avec-incitation">5. Champ commentaire optionnel avec incitation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le formulaire de signalement est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai sélectionné une catégorie autre que "Autre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le champ "Commentaire"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le champ est optionnel (pas d'astérisque rouge)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le placeholder indique "Décrivez le problème (optionnel mais recommandé)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la limite de caractères est de 500
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un compteur affiche "0/500"</p>
|
||
<hr />
|
||
<h2 id="6-envoi-de-signalement-sans-commentaire">6. Envoi de signalement sans commentaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai sélectionné la catégorie "📧 Spam"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas rempli le champ commentaire</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Envoyer le signalement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est envoyé avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune erreur de validation ne s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le commentaire est enregistré comme vide</p>
|
||
<hr />
|
||
<h2 id="7-envoi-de-signalement-avec-commentaire">7. Envoi de signalement avec commentaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai sélectionné la catégorie "🚫 Haine & violence"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai saisi le commentaire "Le créateur tient des propos discriminatoires à 2:30"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Envoyer le signalement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est envoyé avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le commentaire est enregistré avec le signalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il sera visible par les modérateurs</p>
|
||
<hr />
|
||
<h2 id="8-limite-de-500-caracteres-pour-le-commentaire">8. Limite de 500 caractères pour le commentaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le formulaire de signalement est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis un commentaire de 501 caractères</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le champ limite automatiquement à 500 caractères
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur affiche "500/500"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les caractères supplémentaires ne sont pas acceptés</p>
|
||
<hr />
|
||
<h2 id="9-toast-de-confirmation-apres-signalement">9. Toast de confirmation après signalement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai envoyé un signalement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement est enregistré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un toast notification s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le toast contient le message "✓ Signalement envoyé. Nous l'examinerons sous 24-48h."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le toast s'affiche pendant 5 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le toast contient un bouton "Voir mes signalements"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux fermer le toast manuellement avec un bouton X</p>
|
||
<hr />
|
||
<h2 id="10-acces-a-lhistorique-des-signalements-via-le-toast">10. Accès à l'historique des signalements via le toast</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le toast de confirmation est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Voir mes signalements"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers la page "Mes signalements"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois la liste de tous mes signalements
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement que je viens d'envoyer apparaît en premier</p>
|
||
<hr />
|
||
<h2 id="11-historique-personnel-des-signalements">11. Historique personnel des signalements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai envoyé 3 signalements précédemment</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre "Profil > Mes signalements"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste de mes 3 signalements
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque signalement affiche:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> les signalements sont triés par date décroissante</p>
|
||
<hr />
|
||
<h2 id="12-plan-statuts-possibles-dun-signalement">12. 📋 Plan: Statuts possibles d'un signalement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai envoyé un signalement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le statut du signalement est "<statut>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge affiché est "<badge>"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la couleur du badge est "<couleur>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>statut</th>
|
||
<th>badge</th>
|
||
<th>couleur</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>En cours</td>
|
||
<td>En cours</td>
|
||
<td>orange</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Traité</td>
|
||
<td>Traité ✓</td>
|
||
<td>vert</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Rejeté</td>
|
||
<td>Rejeté ✗</td>
|
||
<td>rouge</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="13-notification-in-app-si-action-prise">13. Notification in-app si action prise</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai signalé un contenu il y a 24h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur traite mon signalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu est effectivement retiré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification in-app
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la notification indique "Votre signalement a été traité. Le contenu a été retiré."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut de mon signalement passe à "Traité ✓"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux voir les détails de l'action prise</p>
|
||
<hr />
|
||
<h2 id="14-notification-si-signalement-rejete">14. Notification si signalement rejeté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai signalé un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur rejette mon signalement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification in-app
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la notification indique "Votre signalement a été examiné. Le contenu ne viole pas les règles de la communauté."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut de mon signalement passe à "Rejeté ✗"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux voir la raison du rejet</p>
|
||
<hr />
|
||
<h2 id="15-un-contenu-peut-etre-signale-plusieurs-fois">15. Un contenu peut être signalé plusieurs fois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a déjà été signalé par 5 autres utilisateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je signale le même contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon signalement est enregistré indépendamment
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur de signalements du contenu passe à 6
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon signalement rejoint la file d'attente de modération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les signalements cumulés augmentent la priorité de traitement</p>
|
||
<hr />
|
||
<h2 id="16-limite-de-signalements-par-utilisateur">16. Limite de signalements par utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai déjà signalé le même contenu il y a 2 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de signaler à nouveau le même contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message m'informe "Vous avez déjà signalé ce contenu"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le formulaire de signalement n'est pas affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux consulter le statut de mon signalement précédent</p>
|
||
<hr />
|
||
<h2 id="17-detection-de-signalements-abusifs-repetes">17. Détection de signalements abusifs répétés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai envoyé 10 signalements ce mois-ci
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 8 d'entre eux ont été rejetés comme infondés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'envoyer un nouveau signalement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est marqué comme "signaleur suspect"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un avertissement s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux toujours envoyer le signalement
|
||
<span style="color: #F44336"><strong>Mais</strong></span> mes futurs signalements auront une priorité réduite</p>
|
||
<hr />
|
||
<h2 id="18-sanction-pour-signalements-abusifs-graves">18. Sanction pour signalements abusifs graves</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai envoyé 20 signalements abusifs en 1 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que tous ont été rejetés comme volontairement faux</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur détecte le pattern abusif</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte reçoit un avertissement formel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je perds la possibilité de signaler pendant 30 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email m'expliquant la sanction</p>
|
||
<hr />
|
||
<h2 id="19-signalement-depuis-le-player-audio">19. Signalement depuis le player audio</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre le menu "⋮" du player</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'option "Signaler"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux ouvrir le formulaire de signalement</p>
|
||
<hr />
|
||
<h2 id="20-signalement-depuis-la-page-de-details-du-contenu">20. Signalement depuis la page de détails du contenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page de détails d'un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton "⋮" en haut à droite</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'option "Signaler"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux ouvrir le formulaire de signalement</p>
|
||
<hr />
|
||
<h2 id="21-signalement-depuis-lhistorique-decoute">21. Signalement depuis l'historique d'écoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte mon historique d'écoute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "⋮" à côté d'un contenu passé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'option "Signaler"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux signaler ce contenu même si je ne l'écoute plus actuellement</p>
|
||
<hr />
|
||
<h2 id="22-identite-du-signaleur-anonyme-pour-le-createur">22. Identité du signaleur anonyme pour le créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai signalé un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur est notifié de la modération</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon identité reste anonyme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur ne peut pas savoir qui a signalé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les modérateurs ont accès à l'identité du signaleur</p>
|
||
<hr />
|
||
<h2 id="23-cout-du-systeme-de-signalement">23. Coût du système de signalement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de signalement est en place</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût est de 0€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le formulaire est développé en interne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun service tiers n'est utilisé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les notifications in-app sont gratuites</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="traitement-des-signalements-par-lia-et-les-moderateurs">Traitement des signalements par l'IA et les modérateurs</h1>
|
||
<p><strong>25 scénarios</strong> (21 standards, 4 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de modération est actif</p>
|
||
</blockquote>
|
||
<h2 id="1-signalement-ajoute-a-la-file-dattente-asynchrone">1. Signalement ajouté à la file d'attente asynchrone</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur envoie un signalement pour un contenu audio</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement est reçu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est ajouté à la file d'attente asynchrone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un worker de traitement est déclenché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le traitement se fait en arrière-plan sans bloquer l'utilisateur</p>
|
||
<hr />
|
||
<h2 id="2-transcription-automatique-avec-whisper-large-v3">2. Transcription automatique avec Whisper large-v3</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu audio signalé dure 5 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le worker de traitement démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système utilise Whisper large-v3 pour transcrire l'audio
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transcription est en self-hosted (pas de service cloud)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le texte transcrit est enregistré en base de données
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le délai de transcription est de 1-3 minutes</p>
|
||
<hr />
|
||
<h2 id="3-plan-delai-de-transcription-selon-duree-audio">3. 📋 Plan: Délai de transcription selon durée audio</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu audio signalé dure <duree> minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système transcrit l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la transcription prend environ <delai></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>duree</th>
|
||
<th>delai</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>1-3 minutes</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>3-10 minutes</td>
|
||
</tr>
|
||
<tr>
|
||
<td>45</td>
|
||
<td>10-20 minutes</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="4-analyse-automatique-du-contenu-transcrit">4. Analyse automatique du contenu transcrit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la transcription audio est terminée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système analyse le texte transcrit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les analyses suivantes sont effectuées:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque analyse génère un score de confiance (0-100%)</p>
|
||
<hr />
|
||
<h2 id="5-generation-du-score-de-confiance-ia">5. Génération du score de confiance IA</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que toutes les analyses sont terminées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule le score final</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un score de confiance IA entre 0-100% est généré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le score indique la probabilité que le contenu viole les règles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la catégorie la plus probable est identifiée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les timestamps des passages problématiques sont extraits</p>
|
||
<hr />
|
||
<h2 id="6-detection-automatique-de-contenu-clairement-inapproprie">6. Détection automatique de contenu clairement inapproprié</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu contient des insultes graves et répétées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'IA analyse la transcription</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score de confiance IA est >95%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la catégorie détectée est "Haine & violence"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les passages problématiques sont identifiés avec timestamps:</p>
|
||
<pre><code>| timestamp | texte problématique |
|
||
|---|---|
|
||
| 02:15 | [insulte discriminatoire] |
|
||
| 03:42 | [propos haineux] |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le signalement est classé en priorité CRITIQUE</p>
|
||
<hr />
|
||
<h2 id="7-plan-sla-selon-priorite-du-signalement">7. 📋 Plan: SLA selon priorité du signalement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement a une priorité "<priorite>"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement entre en file d'attente</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le délai de traitement cible est "<delai>"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le responsable du traitement est "<responsable>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>priorite</th>
|
||
<th>delai</th>
|
||
<th>responsable</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>CRITIQUE</td>
|
||
<td><2h (24/7)</td>
|
||
<td>Modérateur senior (astreinte)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>HAUTE</td>
|
||
<td><24h (jours ouvrés)</td>
|
||
<td>Modérateur junior/senior</td>
|
||
</tr>
|
||
<tr>
|
||
<td>MOYENNE</td>
|
||
<td><24h (jours ouvrés)</td>
|
||
<td>Modérateur junior</td>
|
||
</tr>
|
||
<tr>
|
||
<td>BASSE</td>
|
||
<td><72h (jours ouvrés)</td>
|
||
<td>Modérateur junior</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="8-traitement-automatique-pour-score-ia-95">8. Traitement automatique pour score IA >95%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement a un score IA de 97%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la catégorie détectée est "Spam" (évidente)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système évalue le signalement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une action automatique immédiate est déclenchée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est retiré automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur est notifié de la modération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur peut faire appel de la décision
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un modérateur senior vérifie l'action a posteriori</p>
|
||
<hr />
|
||
<h2 id="9-signalement-critique-traite-en-moins-de-2h">9. Signalement CRITIQUE traité en moins de 2h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement de priorité CRITIQUE est reçu à 14:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu concerne une menace de violence</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement est assigné à un modérateur senior d'astreinte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le modérateur est alerté immédiatement (push + SMS)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement est traité avant 16:00 (2h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une décision est prise et appliquée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les autorités peuvent être contactées si nécessaire</p>
|
||
<hr />
|
||
<h2 id="10-astreinte-moderateur-247-pour-signalements-critiques">10. Astreinte modérateur 24/7 pour signalements CRITIQUES</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement CRITIQUE est reçu un dimanche à 03:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement est classé en priorité CRITIQUE</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le modérateur senior d'astreinte est alerté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement est traité dans les 2h (avant 05:00)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le service d'astreinte garantit une disponibilité 24/7</p>
|
||
<hr />
|
||
<h2 id="11-signalement-haute-priorite-traite-en-moins-de-24h">11. Signalement HAUTE priorité traité en moins de 24h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement de priorité HAUTE est reçu lundi à 10:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu concerne du harcèlement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement entre en file d'attente</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est assigné à un modérateur (junior ou senior)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement est traité avant mardi 10:00 (24h jours ouvrés)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une décision est prise et appliquée</p>
|
||
<hr />
|
||
<h2 id="12-signalement-basse-priorite-traite-en-moins-de-72h">12. Signalement BASSE priorité traité en moins de 72h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement de priorité BASSE est reçu lundi à 10:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu concerne des tags incorrects</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement entre en file d'attente</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est traité avant jeudi 10:00 (72h jours ouvrés)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un modérateur junior peut traiter ce type de signalement</p>
|
||
<hr />
|
||
<h2 id="13-calcul-du-score-de-priorite">13. Calcul du score de priorité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement a les caractéristiques suivantes:</p>
|
||
<pre><code>| caractéristique | valeur |
|
||
|---|---|
|
||
| Score IA | 85% |
|
||
| Signalements cumulés | 3 |
|
||
| Fiabilité du signaleur | 75% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule la priorité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la formule appliquée est:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le score de priorité est: (85 × 0.7) + (3 × 0.2) + (75 × 0.1) = 67.5
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement est classé en priorité MOYENNE</p>
|
||
<hr />
|
||
<h2 id="14-plan-classification-selon-score-de-priorite">14. 📋 Plan: Classification selon score de priorité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement a un score de priorité de <score></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système classe le signalement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la priorité assignée est "<priorite>"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement entre dans la file "<file>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>score</th>
|
||
<th>priorite</th>
|
||
<th>file</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>95</td>
|
||
<td>CRITIQUE</td>
|
||
<td>Immédiate</td>
|
||
</tr>
|
||
<tr>
|
||
<td>82</td>
|
||
<td>HAUTE</td>
|
||
<td>Prioritaire</td>
|
||
</tr>
|
||
<tr>
|
||
<td>55</td>
|
||
<td>MOYENNE</td>
|
||
<td>Normale</td>
|
||
</tr>
|
||
<tr>
|
||
<td>25</td>
|
||
<td>BASSE</td>
|
||
<td>Différée</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="15-boost-de-priorite-avec-signalements-cumules">15. Boost de priorité avec signalements cumulés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a été signalé par 1 utilisateur avec un score IA de 60%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le signalement est classé en priorité MOYENNE (score 42)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 5 autres utilisateurs signalent le même contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le nombre de signalements cumulés passe à 6
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le score de priorité augmente significativement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement peut passer en priorité HAUTE
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le traitement est accéléré</p>
|
||
<hr />
|
||
<h2 id="16-impact-de-la-fiabilite-du-signaleur">16. Impact de la fiabilité du signaleur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur de confiance (90% fiabilité) envoie un signalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur suspect (20% fiabilité) envoie un signalement similaire</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule les priorités</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement de l'utilisateur de confiance a un score plus élevé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> son signalement est traité en priorité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement de l'utilisateur suspect est traité plus tard</p>
|
||
<hr />
|
||
<h2 id="17-evolution-du-score-de-fiabilite-du-signaleur">17. Évolution du score de fiabilité du signaleur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a envoyé 10 signalements
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 8 d'entre eux ont été acceptés par les modérateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule son score de fiabilité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score est de 80% (8 acceptés / 10 total)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ses futurs signalements auront plus de poids
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut devenir "utilisateur de confiance"</p>
|
||
<hr />
|
||
<h2 id="18-files-dattente-separees-par-priorite">18. Files d'attente séparées par priorité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 50 signalements sont en attente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système organise la file d'attente</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les signalements sont répartis dans les files suivantes:</p>
|
||
<pre><code>| file | nombre | priorité |
|
||
|---|---|---|
|
||
| Immédiate (24/7) | 5 | CRITIQUE |
|
||
| Prioritaire | 15 | HAUTE |
|
||
| Normale | 20 | MOYENNE |
|
||
| Différée | 10 | BASSE |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> les modérateurs traitent en priorité la file Immédiate</p>
|
||
<hr />
|
||
<h2 id="19-moderateurs-assignes-selon-competences">19. Modérateurs assignés selon compétences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement complexe de harcèlement est reçu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système assigne un modérateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un modérateur senior est prioritairement assigné
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les modérateurs juniors peuvent traiter les cas simples (spam, tags)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les modérateurs seniors traitent les cas complexes (haine, violence, appels)</p>
|
||
<hr />
|
||
<h2 id="20-stack-technique-100-opensource">20. Stack technique 100% opensource</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de modération IA est déployé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on analyse les technologies utilisées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les technologies sont opensource:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> aucune dépendance à Google, AWS, Azure</p>
|
||
<hr />
|
||
<h2 id="21-plan-cout-selon-phase-du-projet">21. 📋 Plan: Coût selon phase du projet</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave est en phase "<phase>"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût de l'infrastructure IA</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût mensuel est "<cout>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>phase</th>
|
||
<th>cout</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>MVP</td>
|
||
<td>0-50€ (CPU)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Scale</td>
|
||
<td>50-200€ (GPU VPS)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="22-processing-asynchrone-en-mvp-avec-cpu">22. Processing asynchrone en MVP avec CPU</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave est en phase MVP
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le volume est <1000 signalements/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système traite les signalements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un serveur CPU standard est suffisant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût est de 0€ (serveur existant)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le processing asynchrone absorbe les pics de charge
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les délais restent acceptables (1-20 minutes)</p>
|
||
<hr />
|
||
<h2 id="23-scaling-avec-gpu-pour-gros-volumes">23. Scaling avec GPU pour gros volumes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave reçoit >1000 signalements/jour</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système nécessite un scaling</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un VPS avec GPU est requis
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût passe à 50-200€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les délais de transcription sont divisés par 5-10
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système peut gérer 10 000+ signalements/mois</p>
|
||
<hr />
|
||
<h2 id="24-logs-daudit-pour-chaque-traitement">24. Logs d'audit pour chaque traitement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un signalement est traité</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une action est prise (rejet, acceptation, sanction)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un log d'audit complet est créé:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le log est conservé pour conformité DSA
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les logs sont anonymisés après 3 ans (RGPD)</p>
|
||
<hr />
|
||
<h2 id="25-tracabilite-complete-pour-conformite-dsa">25. Traçabilité complète pour conformité DSA</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de modération est actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un audit DSA est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les actions de modération sont tracées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les délais de traitement sont mesurés et respectés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les décisions sont justifiées et documentées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transparence vis-à-vis des utilisateurs est garantie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système est conforme au Digital Services Act</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="conditions-dactivation-de-la-monetisation">Conditions d'activation de la monétisation</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur</em>
|
||
<em>Je veux pouvoir activer la monétisation quand je remplis les critères</em>
|
||
<em>Afin de générer des revenus avec mes contenus</em></p>
|
||
</blockquote>
|
||
<p><strong>28 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant que créateur</p>
|
||
</blockquote>
|
||
<h2 id="1-critere-1-anciennete-de-3-mois-validee">1. Critère 1 - Ancienneté de 3 mois validée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte a été créé il y a 91 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "Ancienneté ≥ 3 mois" est validé ✅</p>
|
||
<hr />
|
||
<h2 id="2-critere-1-anciennete-insuffisante">2. Critère 1 - Ancienneté insuffisante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte a été créé il y a 60 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "Ancienneté ≥ 3 mois" n'est pas validé ❌
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Encore 30 jours avant d'être éligible"</p>
|
||
<hr />
|
||
<h2 id="3-critere-2-500-abonnes-atteints">3. Critère 2 - 500 abonnés atteints</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai exactement 500 abonnés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "≥ 500 abonnés" est validé ✅</p>
|
||
<hr />
|
||
<h2 id="4-critere-2-pas-assez-dabonnes">4. Critère 2 - Pas assez d'abonnés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 347 abonnés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "≥ 500 abonnés" n'est pas validé ❌
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Encore 153 abonnés nécessaires"</p>
|
||
<hr />
|
||
<h2 id="5-critere-3-10-000-ecoutes-completes-atteintes">5. Critère 3 - 10 000 écoutes complètes atteintes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes contenus ont cumulé 10 487 écoutes complètes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "≥ 10 000 écoutes complètes" est validé ✅</p>
|
||
<hr />
|
||
<h2 id="6-critere-3-ecoutes-incompletes-non-comptabilisees">6. Critère 3 - Écoutes incomplètes non comptabilisées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes contenus ont:</p>
|
||
<pre><code>| type écoute | nombre |
|
||
|---|---|
|
||
| Écoutes complètes | 8 500 |
|
||
| Écoutes <80% | 3 000 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules les 8 500 écoutes complètes comptent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Encore 1 500 écoutes complètes nécessaires"</p>
|
||
<hr />
|
||
<h2 id="7-critere-4-aucun-strike-actif">7. Critère 4 - Aucun strike actif</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai aucun strike actif
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai eu aucun contenu modéré dans les 6 derniers mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "Fiabilité" est validé ✅</p>
|
||
<hr />
|
||
<h2 id="8-critere-4-strike-actif-bloque-leligibilite">8. Critère 4 - Strike actif bloque l'éligibilité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 1 strike actif pour contenu inapproprié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "Fiabilité" n'est pas validé ❌
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Vous devez résoudre votre strike avant d'être éligible"</p>
|
||
<hr />
|
||
<h2 id="9-critere-4-contenu-modere-dans-les-6-derniers-mois">9. Critère 4 - Contenu modéré dans les 6 derniers mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai pas de strike actif
|
||
<span style="color: #F44336"><strong>Mais</strong></span> qu'un de mes contenus a été modéré il y a 4 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "Fiabilité" n'est pas validé ❌
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Attendre 2 mois après le dernier contenu modéré"</p>
|
||
<hr />
|
||
<h2 id="10-critere-5-5-contenus-publies-dans-les-90-derniers-jours">10. Critère 5 - 5 contenus publiés dans les 90 derniers jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le critère "≥ 5 contenus publiés dans les 90 derniers jours" est validé ✅</p>
|
||
<hr />
|
||
<h2 id="11-critere-5-contenus-trop-anciens-ne-comptent-pas">11. Critère 5 - Contenus trop anciens ne comptent pas</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les critères de monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls 2 contenus comptent (dans les 90 jours)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Encore 3 contenus à publier dans les 90 prochains jours"</p>
|
||
<hr />
|
||
<h2 id="12-tous-les-criteres-valides-bouton-disponible">12. Tous les critères validés - Bouton disponible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que tous mes critères sont validés:</p>
|
||
<pre><code>| critère | statut |
|
||
|---|---|
|
||
| Ancienneté ≥ 3 mois | ✅ |
|
||
| ≥ 500 abonnés | ✅ |
|
||
| ≥ 10 000 écoutes | ✅ |
|
||
| Fiabilité | ✅ |
|
||
| Régularité (5 contenus) | ✅ |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mon profil créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Demander la monétisation" est actif
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer pour démarrer le KYC</p>
|
||
<hr />
|
||
<h2 id="13-criteres-incomplets-bouton-grise-avec-progression">13. Critères incomplets - Bouton grisé avec progression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes critères sont:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mon profil créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Demander la monétisation" est grisé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois la progression détaillée de chaque critère</p>
|
||
<hr />
|
||
<h2 id="14-verification-automatique-sql-lors-de-la-demande">14. Vérification automatique SQL lors de la demande</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Demander la monétisation"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système vérifie mes critères</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une requête SQL est exécutée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si tous les critères sont TRUE, je suis redirigé vers le KYC</p>
|
||
<hr />
|
||
<h2 id="15-notification-par-email-quand-criteres-atteints">15. Notification par email quand critères atteints</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens d'atteindre 500 abonnés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que c'était mon dernier critère manquant</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte l'éligibilité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="16-badge-eligible-monetisation-dans-profil">16. Badge "Éligible monétisation" dans profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je remplis tous les critères
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que je n'ai pas encore activé la monétisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur consulte mon profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit un badge "Éligible monétisation 💰"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela renforce ma crédibilité de créateur</p>
|
||
<hr />
|
||
<h2 id="17-justification-anti-fraude-delai-3-mois">17. Justification anti-fraude - Délai 3 mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un compte suspect crée du contenu frauduleux</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le compte est détecté dans les 2 premiers mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compte est banni avant d'atteindre les 3 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur n'a jamais été éligible à la monétisation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun paiement n'a été effectué</p>
|
||
<hr />
|
||
<h2 id="18-justification-qualite-10-000-ecoutes">18. Justification qualité - 10 000 écoutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur produit du contenu de mauvaise qualité</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ses contenus ne génèrent que 2 000 écoutes complètes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il ne peut pas activer la monétisation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les créateurs avec contenu apprécié sont monétisés</p>
|
||
<hr />
|
||
<h2 id="19-reduction-cout-administratif-plateforme">19. Réduction coût administratif plateforme</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave a 10 000 créateurs inscrits
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que seuls 500 remplissent tous les critères</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule le coût administratif</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seulement 500 KYC sont à gérer (vs 10 000)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seulement 500 virements mensuels (vs 10 000)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la charge comptable est réduite de 95%</p>
|
||
<hr />
|
||
<h2 id="20-statistiques-publiques-pour-transparence">20. Statistiques publiques pour transparence</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur consulte la page "Devenir créateur"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit les statistiques:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela permet de fixer des attentes réalistes</p>
|
||
<hr />
|
||
<h2 id="21-cache-redis-pour-calcul-rapide-criteres">21. Cache Redis pour calcul rapide critères</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte mes critères de monétisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système charge la page</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les compteurs sont récupérés depuis Redis:</p>
|
||
<pre><code>| clé Redis | exemple valeur |
|
||
|---|---|
|
||
| creator:[id]:subscribers_count | 347 |
|
||
| creator:[id]:complete_listens_total | 8500 |
|
||
| creator:[id]:recent_contents_count | 7 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le temps de réponse est <50ms</p>
|
||
<hr />
|
||
<h2 id="22-mise-a-jour-temps-reel-des-compteurs">22. Mise à jour temps réel des compteurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de publier un nouveau contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur écoute ce contenu en entier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur "complete_listens_total" est incrémenté immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je rafraîchis la page critères, je vois la nouvelle valeur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela encourage les créateurs à continuer de produire</p>
|
||
<hr />
|
||
<h2 id="23-historique-des-tentatives-dactivation">23. Historique des tentatives d'activation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai tenté d'activer la monétisation il y a 2 mois
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que les critères n'étaient pas remplis</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mes logs d'activité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| date | action | résultat | raison |
|
||
|---|---|---|---|
|
||
| 2025-11-15 | Demande monétisation | Refusée | Seulement 300 abonnés |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela m'aide à suivre ma progression</p>
|
||
<hr />
|
||
<h2 id="24-performance-avec-100-000-createurs">24. Performance avec 100 000 créateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave a 100 000 créateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que chacun consulte ses critères 1 fois par jour</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système traite ces requêtes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la table users est indexée sur created_at
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la table subscriptions est indexée sur creator_id
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la table contents est indexée sur creator_id et published_at
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque requête reste <50ms grâce aux index</p>
|
||
<hr />
|
||
<h2 id="25-export-des-criteres-pour-support-client">25. Export des critères pour support client</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je contacte le support car je pense être éligible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'agent support consulte mon compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit un export JSON complet:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'agent peut expliquer précisément pourquoi je ne suis pas éligible</p>
|
||
<hr />
|
||
<h2 id="26-notification-30-jours-avant-eligibilite-probable">26. Notification 30 jours avant éligibilité probable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes critères sont:</p>
|
||
<pre><code>| critère | statut | progression |
|
||
|---|---|---|
|
||
| Ancienneté ≥ 3 mois | ❌ | 60/90 jours |
|
||
| Tous les autres critères | ✅ | 100% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il reste exactement 30 jours avant les 90 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification:</p>
|
||
<hr />
|
||
<h2 id="27-pas-de-bypass-possible-pour-amisinfluenceurs">27. Pas de bypass possible pour amis/influenceurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur influent me contacte directement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il demande un bypass des critères</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la politique RoadWave</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la réponse est "Aucune exception possible, critères automatiques uniquement"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela garantit l'équité pour tous les créateurs</p>
|
||
<hr />
|
||
<h2 id="28-ab-test-futur-sur-seuils-post-mvp">28. A/B test futur sur seuils (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave veut tester des seuils différents</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un A/B test est lancé en 2027</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> groupe A voit: 500 abonnés, 10 000 écoutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> groupe B voit: 300 abonnés, 5 000 écoutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les métriques (taux activation, fraude, qualité) sont comparées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le meilleur seuil est déployé définitivement</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="contenus-premium-exclusifs">Contenus Premium exclusifs</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur monétisé</em>
|
||
<em>Je veux pouvoir rendre certains contenus exclusifs aux abonnés Premium</em>
|
||
<em>Afin d'inciter les utilisateurs à s'abonner</em></p>
|
||
</blockquote>
|
||
<p><strong>34 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec la monétisation activée</p>
|
||
</blockquote>
|
||
<h2 id="1-toggle-reserve-premium-lors-de-la-creation">1. Toggle "Réservé Premium" lors de la création</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée un nouveau contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède aux options de publication</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un toggle "Réservé aux abonnés Premium 👑"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux l'activer ou le désactiver</p>
|
||
<hr />
|
||
<h2 id="2-contenu-marque-premium-lors-de-la-creation">2. Contenu marqué Premium lors de la création</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée un nouveau contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'active le toggle "Réservé Premium"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je publie le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le champ <code>is_premium</code> en base est mis à <code>true</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est visible uniquement pour les utilisateurs Premium</p>
|
||
<hr />
|
||
<h2 id="3-contenu-gratuit-par-defaut">3. Contenu gratuit par défaut</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée un nouveau contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je ne touche pas au toggle "Réservé Premium"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je publie le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le champ <code>is_premium</code> en base est mis à <code>false</code> (défaut)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est accessible à tous les utilisateurs</p>
|
||
<hr />
|
||
<h2 id="4-modification-dun-contenu-existant-en-premium">4. Modification d'un contenu existant en Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié un contenu gratuit il y a 2 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie le contenu et active le toggle "Réservé Premium"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'enregistre les modifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu devient immédiatement Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs gratuits ne peuvent plus y accéder</p>
|
||
<hr />
|
||
<h2 id="5-passage-dun-contenu-premium-en-gratuit">5. Passage d'un contenu Premium en gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié un contenu Premium il y a 1 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie le contenu et désactive le toggle "Réservé Premium"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'enregistre les modifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu devient immédiatement gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les utilisateurs peuvent maintenant y accéder</p>
|
||
<hr />
|
||
<h2 id="6-aucune-limite-sur-pourcentage-de-contenus-premium">6. Aucune limite sur pourcentage de contenus Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je publie 10 nouveaux contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je décide de rendre les 10 contenus Premium (100%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système accepte sans limitation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux avoir 100% de mon catalogue en Premium</p>
|
||
<hr />
|
||
<h2 id="7-strategie-freemium-mix-gratuitpremium">7. Stratégie freemium - Mix gratuit/premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je publie 10 nouveaux contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je décide de rendre 5 contenus Premium et 5 gratuits (50/50)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système accepte cette stratégie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux tester différents mix pour optimiser mes revenus</p>
|
||
<hr />
|
||
<h2 id="8-strategie-tout-gratuit-possible">8. Stratégie tout gratuit possible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis monétisé via publicités</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je décide de ne mettre aucun contenu en Premium (0%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système accepte cette stratégie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je génère des revenus uniquement via les publicités</p>
|
||
<hr />
|
||
<h2 id="9-badge-visible-sur-linterface-utilisateur">9. Badge 👑 visible sur l'interface utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur consulte ma liste de contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il voit un contenu Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un badge 👑 "Premium" est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est clairement identifiable comme réservé</p>
|
||
<hr />
|
||
<h2 id="10-utilisateur-gratuit-voit-les-contenus-premium-dans-la-liste">10. Utilisateur gratuit voit les contenus Premium dans la liste</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les contenus d'un créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois aussi les contenus Premium dans la liste
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ils sont affichés avec un badge 👑
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je ne peux pas les lire</p>
|
||
<hr />
|
||
<h2 id="11-tentative-de-lecture-premium-par-utilisateur-gratuit-overlay-bloquant">11. Tentative de lecture Premium par utilisateur gratuit - Overlay bloquant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur un contenu Premium pour le lire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un overlay bloquant apparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Passer Premium" est affiché</p>
|
||
<hr />
|
||
<h2 id="12-cta-passer-premium-redirige-vers-abonnement">12. CTA "Passer Premium" redirige vers abonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je vois l'overlay de contenu Premium bloqué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Passer Premium"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers la page d'abonnement Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux m'abonner pour 4.99€/mois</p>
|
||
<hr />
|
||
<h2 id="13-utilisateur-premium-peut-lire-tous-les-contenus-premium">13. Utilisateur Premium peut lire tous les contenus Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur un contenu Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu se lance immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai aucun overlay bloquant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux profiter pleinement du contenu exclusif</p>
|
||
<hr />
|
||
<h2 id="14-contenus-premium-inclus-dans-les-recommandations">14. Contenus Premium inclus dans les recommandations</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'algorithme génère ma file de 5 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus Premium peuvent apparaître dans les recommandations
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela me fait découvrir qu'il existe du contenu exclusif</p>
|
||
<hr />
|
||
<h2 id="15-contenu-premium-skippe-automatiquement-pour-utilisateur-gratuit">15. Contenu Premium skippé automatiquement pour utilisateur gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu Premium apparaît dans ma file de recommandation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu précédent jusqu'à la fin</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu Premium est automatiquement skippé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu suivant (gratuit) est lancé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le slot Premium ne compte pas dans ma file de 5 contenus</p>
|
||
<hr />
|
||
<h2 id="16-contenu-premium-diffuse-normalement-pour-utilisateur-premium">16. Contenu Premium diffusé normalement pour utilisateur Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu Premium apparaît dans ma file de recommandation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute le contenu précédent jusqu'à la fin</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu Premium est lancé normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je profite du contenu exclusif sans interruption</p>
|
||
<hr />
|
||
<h2 id="17-champ-is_premium-boolean-en-base-postgresql">17. Champ <code>is_premium</code> boolean en base PostgreSQL</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est créé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il est stocké en base de données</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la table <code>contents</code> contient un champ <code>is_premium BOOLEAN DEFAULT FALSE</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce champ est indexé pour requêtes rapides</p>
|
||
<hr />
|
||
<h2 id="18-index-postgresql-sur-is_premium">18. Index PostgreSQL sur <code>is_premium</code></h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'algorithme doit filtrer les contenus selon le statut Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une requête SQL est exécutée:</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'index sur <code>is_premium</code> accélère la requête
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le temps de réponse reste <20ms</p>
|
||
<hr />
|
||
<h2 id="19-cache-redis-pour-statut-premium">19. Cache Redis pour statut Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu Premium est consulté fréquemment</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'API vérifie le statut Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la valeur est récupérée depuis Redis:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cache a un TTL de 1 heure
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela évite des requêtes SQL inutiles</p>
|
||
<hr />
|
||
<h2 id="20-invalidation-cache-lors-de-modification-statut-premium">20. Invalidation cache lors de modification statut Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est passé de gratuit à Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur enregistre la modification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le cache Redis <code>content:[id]:premium</code> est invalidé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la nouvelle valeur est mise à jour
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs voient le changement en temps réel</p>
|
||
<hr />
|
||
<h2 id="21-justification-liberte-createur-strategie-personnalisee">21. Justification liberté créateur - Stratégie personnalisée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que chaque créateur a une audience différente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un créateur décide de sa stratégie Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut tester différentes approches:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="22-justification-incitation-premium-argument-fort-pour-sabonner">22. Justification incitation Premium - Argument fort pour s'abonner</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur gratuit voit beaucoup de contenus Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte les profils de ses créateurs préférés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit que 60% de leur contenu est réservé Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela l'incite à s'abonner pour 4.99€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave augmente son taux de conversion vers Premium</p>
|
||
<hr />
|
||
<h2 id="23-justification-equite-petit-createur-peut-tout-mettre-en-premium">23. Justification équité - Petit créateur peut tout mettre en Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un petit créateur avec 600 abonnés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 50 sont abonnés Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je mets 100% de mon contenu en Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je génère des revenus uniquement via mes 50 abonnés Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela me permet de vivre de mon contenu malgré une petite audience</p>
|
||
<hr />
|
||
<h2 id="24-justification-equite-gros-createur-peut-tout-offrir-gratuitement">24. Justification équité - Gros créateur peut tout offrir gratuitement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un gros créateur avec 50 000 abonnés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je génère déjà beaucoup de revenus publicitaires</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je laisse 100% de mon contenu gratuit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je maximise mon audience et mes revenus pub
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai pas besoin de mettre du contenu en Premium</p>
|
||
<hr />
|
||
<h2 id="25-statistiques-createur-ratio-premiumgratuit">25. Statistiques créateur - Ratio Premium/Gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mon tableau de bord créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques de contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="26-statistiques-createur-revenus-par-type">26. Statistiques créateur - Revenus par type</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai des contenus gratuits et Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes revenus détaillés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| source | montant |
|
||
|---|---|
|
||
| Revenus pub (gratuit) | 86.70€ |
|
||
| Revenus Premium (exclusifs) | 34.20€ |
|
||
| Revenus Premium (tout contenu) | 78.90€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux comparer l'efficacité de chaque stratégie</p>
|
||
<hr />
|
||
<h2 id="27-notification-createur-contenu-premium-tres-ecoute">27. Notification créateur - Contenu Premium très écouté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié un contenu Premium il y a 3 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il a généré 5 000 écoutes Premium (très élevé)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette performance</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification:</p>
|
||
<hr />
|
||
<h2 id="28-ab-test-utilisateur-impact-badge-premium-sur-conversion">28. A/B test utilisateur - Impact badge Premium sur conversion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave veut optimiser le taux de conversion Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un A/B test est lancé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> groupe A voit le badge 👑 "Premium"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> groupe B voit le badge 💎 "Exclusif"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les taux de clic et conversion sont mesurés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le badge le plus performant est déployé définitivement</p>
|
||
<hr />
|
||
<h2 id="29-analytics-plateforme-adoption-fonctionnalite-premium">29. Analytics plateforme - Adoption fonctionnalité Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave suit l'adoption de la fonctionnalité</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un admin consulte les métriques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="30-impact-sur-churn-contenus-premium-reduisent-le-churn-premium">30. Impact sur churn - Contenus Premium réduisent le churn Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium envisage de résilier
|
||
<span style="color: #F44336"><strong>Mais</strong></span> qu'il a accès à 150 contenus Premium de ses créateurs préférés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il voit la valeur exclusive qu'il perdrait</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est moins susceptible de résilier (churn réduit de ~30%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus Premium augmentent la rétention</p>
|
||
<hr />
|
||
<h2 id="31-transparence-createur-voit-combien-de-contenus-premium-il-a">31. Transparence - Créateur voit combien de contenus Premium il a</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mon profil créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux filtrer par statut:</p>
|
||
<pre><code>| filtre | résultats |
|
||
|---|---|
|
||
| Tous | 47 |
|
||
| Gratuits | 32 |
|
||
| Premium 👑 | 15 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux facilement gérer mon catalogue</p>
|
||
<hr />
|
||
<h2 id="32-export-liste-contenus-avec-statut-premium-rgpd">32. Export liste contenus avec statut Premium (RGPD)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je demande l'export de mes données</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la liste de mes contenus inclut le statut Premium:</p>
|
||
<hr />
|
||
<h2 id="33-suppression-compte-createur-et-contenus-premium">33. Suppression compte créateur et contenus Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je supprime définitivement mon compte créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous mes contenus (gratuits et Premium) sont supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs Premium ne peuvent plus y accéder
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fichiers audio sont supprimés du CDN sous 7 jours</p>
|
||
<hr />
|
||
<h2 id="34-performance-avec-1-million-de-contenus-premium">34. Performance avec 1 million de contenus Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave a 1 million de contenus dont 300 000 Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère une recommandation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la requête SQL filtre efficacement avec l'index <code>is_premium</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le temps de réponse reste <50ms
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la scalabilité est garantie</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="desactivation-et-suspension-monetisation">Désactivation et suspension monétisation</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur ou plateforme</em>
|
||
<em>Je veux pouvoir désactiver ou suspendre la monétisation selon certaines conditions</em>
|
||
<em>Afin de gérer les pauses, problèmes techniques ou violations des règles</em></p>
|
||
</blockquote>
|
||
<p><strong>35 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec la monétisation activée</p>
|
||
</blockquote>
|
||
<h2 id="1-desactivation-temporaire-par-le-createur">1. Désactivation temporaire par le créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux faire une pause dans ma création de contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à "Paramètres > Monétisation"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Désactiver temporairement la monétisation"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est désactivée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne génère plus de revenus à partir de maintenant</p>
|
||
<hr />
|
||
<h2 id="2-confirmation-avant-desactivation">2. Confirmation avant désactivation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Désactiver temporairement"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une popup de confirmation apparaît</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois confirmer pour continuer</p>
|
||
<hr />
|
||
<h2 id="3-solde-conserve-pendant-desactivation">3. Solde conservé pendant désactivation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon solde actuel est 87.45€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive ma monétisation le 15 du mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon solde de 87.45€ est conservé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il sera reporté au mois suivant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le total dépasse 50€, il sera versé normalement le 15 du mois prochain</p>
|
||
<hr />
|
||
<h2 id="4-contenus-restent-accessibles-pendant-desactivation">4. Contenus restent accessibles pendant désactivation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai désactivé ma monétisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> des utilisateurs écoutent mes contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes contenus restent accessibles normalement
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je ne génère aucun revenu (ni pub ni Premium)</p>
|
||
<hr />
|
||
<h2 id="5-reactivation-sans-refaire-le-kyc-si-2-ans">5. Réactivation sans refaire le KYC si <2 ans</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai désactivé ma monétisation il y a 8 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes documents KYC sont toujours valides</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Réactiver la monétisation"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la réactivation est immédiate
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai pas besoin de refaire le KYC
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je recommence à générer des revenus dès maintenant</p>
|
||
<hr />
|
||
<h2 id="6-nouveau-kyc-requis-si-inactivite-2-ans">6. Nouveau KYC requis si inactivité >2 ans</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai désactivé ma monétisation il y a 25 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de réactiver</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système demande un nouveau KYC
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois soumettre à nouveau mes documents</p>
|
||
<hr />
|
||
<h2 id="7-historique-des-desactivationsreactivations">7. Historique des désactivations/réactivations</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai désactivé et réactivé ma monétisation plusieurs fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à "Paramètres > Monétisation > Historique"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste complète:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="8-suspension-si-3-strikes-actifs">8. Suspension si 3+ strikes actifs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je reçois un 3ème strike pour violation des règles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le strike devient actif</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est suspendue automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="9-reactivation-apres-resolution-des-strikes">9. Réactivation après résolution des strikes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation est suspendue pour 3 strikes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je résous tous mes strikes (après expiration ou contestation)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon compteur de strikes passe à 0</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est réactivée automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de confirmation</p>
|
||
<hr />
|
||
<h2 id="10-suspension-si-rib-invalide-apres-3-echecs-de-virement">10. Suspension si RIB invalide après 3 échecs de virement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 3 tentatives de virement ont échoué (15, 18, 22 du mois)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 3ème échec est confirmé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est suspendue automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="11-reactivation-apres-mise-a-jour-rib-valide">11. Réactivation après mise à jour RIB valide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation est suspendue pour RIB invalide</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je mets à jour mon RIB avec un compte bancaire valide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que Mangopay valide le nouveau RIB</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est réactivée automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un virement est tenté immédiatement pour le solde en attente</p>
|
||
<hr />
|
||
<h2 id="12-suspension-si-documents-kyc-expires">12. Suspension si documents KYC expirés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma carte d'identité expire dans 30 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois un email de rappel de mise à jour
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que je ne mets pas à jour mes documents
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma CNI expire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est suspendue automatiquement après 30 jours de grâce</p>
|
||
<hr />
|
||
<h2 id="13-preavis-30-jours-avant-suspension-pour-docs-expires">13. Préavis 30 jours avant suspension pour docs expirés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma CNI expire le 15 juin 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 15 mai 2025 arrive (30 jours avant)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email d'alerte:</p>
|
||
<hr />
|
||
<h2 id="14-reactivation-apres-renouvellement-documents-kyc">14. Réactivation après renouvellement documents KYC</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation est suspendue pour CNI expirée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je soumets une nouvelle CNI valide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que Mangopay valide le document sous 24-72h</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est réactivée automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je recommence à générer des revenus</p>
|
||
<hr />
|
||
<h2 id="15-suspension-si-fraude-detectee">15. Suspension si fraude détectée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système détecte une activité frauduleuse (bots, écoutes artificielles)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe modération confirme la fraude</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est suspendue immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon compte est mis sous enquête
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email m'informant de la suspension</p>
|
||
<hr />
|
||
<h2 id="16-enquete-fraude-verification-manuelle">16. Enquête fraude - Vérification manuelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation est suspendue pour suspicion de fraude</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe modération enquête</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle analyse:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-levee-suspension-si-fraude-non-confirmee">17. Levée suspension si fraude non confirmée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte était suspendu pour suspicion de fraude</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'enquête conclut qu'il n'y a pas eu de fraude</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est réactivée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les revenus suspendus pendant l'enquête sont versés normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email d'excuses avec explication</p>
|
||
<hr />
|
||
<h2 id="18-suspension-definitive-si-fraude-confirmee">18. Suspension définitive si fraude confirmée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'enquête confirme une fraude avérée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe modération prend la décision</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est définitivement désactivée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon solde en attente est gelé (non versé)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux recevoir un strike 4 (ban définitif du compte)</p>
|
||
<hr />
|
||
<h2 id="19-suppression-definitive-sur-demande-createur">19. Suppression définitive sur demande créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux arrêter définitivement la monétisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à "Paramètres > Monétisation > Supprimer définitivement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une confirmation stricte est demandée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois taper "SUPPRIMER" pour confirmer</p>
|
||
<hr />
|
||
<h2 id="20-solde-verse-sous-30-jours-apres-suppression">20. Solde versé sous 30 jours après suppression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je supprime définitivement ma monétisation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon solde en attente est 127.45€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon solde sera versé sous 30 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un dernier virement de clôture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon e-wallet Mangopay est clôturé</p>
|
||
<hr />
|
||
<h2 id="21-suppression-auto-si-inactivite-24-mois-solde-50">21. Suppression auto si inactivité 24 mois + solde <50€</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai plus publié de contenu depuis 24 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon solde en attente est 12.30€ (<50€)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le processus de purge RGPD s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est automatiquement supprimée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon solde de 12.30€ est perdu (trop faible pour virement)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes données KYC sont archivées puis supprimées selon la législation</p>
|
||
<hr />
|
||
<h2 id="22-email-de-preavis-60-jours-avant-purge-rgpd">22. Email de préavis 60 jours avant purge RGPD</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis inactif depuis 22 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte l'inactivité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="23-ban-definitif-compte-strike-4">23. Ban définitif compte - Strike 4</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je reçois un 4ème strike (violation grave ou répétée)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe modération applique le strike 4</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est banni définitivement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma monétisation est supprimée définitivement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon solde en attente est gelé (non versé)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux plus créer de nouveau compte (blacklist email/SIRET)</p>
|
||
<hr />
|
||
<h2 id="24-email-pour-toute-suspension">24. Email pour toute suspension</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation est suspendue (quelle qu'en soit la raison)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suspension devient effective</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois immédiatement un email:</p>
|
||
<hr />
|
||
<h2 id="25-notification-in-app-avec-raison-explicite">25. Notification in-app avec raison explicite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation est suspendue</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte à l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une bannière en haut de mon dashboard:</p>
|
||
<hr />
|
||
<h2 id="26-email-de-confirmation-lors-de-reactivation">26. Email de confirmation lors de réactivation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation était suspendue</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> elle est réactivée (automatiquement ou manuellement)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="27-dashboard-admin-suspensions-actives">27. Dashboard admin - Suspensions actives</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin RoadWave consulte les suspensions</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède au dashboard admin "Monétisation > Suspensions"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="28-alertes-si-taux-de-suspension-5">28. Alertes si taux de suspension >5%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le taux de suspension dépasse 5%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette anomalie</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte est envoyée à l'équipe:</p>
|
||
<hr />
|
||
<h2 id="29-statistiques-personnelles-temps-actif-monetisation">29. Statistiques personnelles - Temps actif monétisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mon dashboard créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte "Statistiques > Monétisation"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="30-export-donnees-suspension-rgpd">30. Export données suspension (RGPD)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je demande l'export de mes données</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique des suspensions est inclus:</p>
|
||
<hr />
|
||
<h2 id="31-suppression-compte-et-donnees-monetisation">31. Suppression compte et données monétisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je supprime définitivement mon compte RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes mes données de monétisation sont supprimées:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="32-conservation-archives-10-ans-obligation-legale">32. Conservation archives 10 ans obligation légale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je supprime mon compte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mes données sont archivées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave conserve 10 ans:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> après 10 ans, tout est supprimé définitivement</p>
|
||
<hr />
|
||
<h2 id="33-suspension-temporaire-pour-maintenance-technique">33. Suspension temporaire pour maintenance technique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Mangopay effectue une maintenance planifiée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la maintenance est programmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les créateurs reçoivent un email préventif 7 jours avant:</p>
|
||
<hr />
|
||
<h2 id="34-reactivation-progressive-apres-incident-majeur">34. Réactivation progressive après incident majeur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un incident technique majeur suspend toutes les monétisations</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'incident est résolu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les réactivations se font progressivement:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela évite une surcharge système lors de la reprise</p>
|
||
<hr />
|
||
<h2 id="35-support-prioritaire-pour-createurs-suspendus-injustement">35. Support prioritaire pour créateurs suspendus injustement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation est suspendue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je pense que c'est une erreur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je contacte le support avec tag "Suspension monétisation"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon ticket est traité en priorité (SLA 24h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un agent expert examine mon cas
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si suspension injustifiée, je suis réactivé immédiatement avec excuses</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="kyc-et-inscription-a-la-monetisation">KYC et inscription à la monétisation</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur éligible</em>
|
||
<em>Je veux compléter le KYC pour activer la monétisation</em>
|
||
<em>Afin de recevoir des paiements légalement</em></p>
|
||
</blockquote>
|
||
<p><strong>37 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je remplis tous les critères de monétisation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai cliqué sur "Demander la monétisation"</p>
|
||
</blockquote>
|
||
<h2 id="1-redirection-vers-formulaire-kyc-mangopay">1. Redirection vers formulaire KYC Mangopay</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je démarre le processus d'activation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers un formulaire KYC
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le formulaire est fourni par Mangopay (iframe sécurisée)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les données sont chiffrées et hébergées en EU</p>
|
||
<hr />
|
||
<h2 id="2-statut-auto-entrepreneur-accepte">2. Statut auto-entrepreneur accepté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis auto-entrepreneur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je renseigne mon statut juridique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'option "Auto-entrepreneur (micro-BNC)" est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer le processus</p>
|
||
<hr />
|
||
<h2 id="3-statut-societe-sarlsassasu-accepte">3. Statut société SARL/SAS/SASU accepté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai créé une société</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je renseigne mon statut juridique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les options suivantes sont disponibles:</p>
|
||
<pre><code>| statut juridique |
|
||
|---|
|
||
| SARL |
|
||
| SAS |
|
||
| SASU |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer le processus</p>
|
||
<hr />
|
||
<h2 id="4-statut-particulier-refuse">4. Statut particulier refusé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai pas de statut professionnel</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de m'inscrire en tant que "Particulier"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le formulaire affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas continuer sans statut professionnel</p>
|
||
<hr />
|
||
<h2 id="5-document-siret-obligatoire-et-valide">5. Document SIRET obligatoire et validé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je renseigne mon SIRET</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis "12345678901234" (14 chiffres)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le format est validé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Mangopay vérifie l'existence du SIRET auprès du répertoire SIRENE
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si valide, le document est accepté</p>
|
||
<hr />
|
||
<h2 id="6-siret-invalide-ou-inexistant">6. SIRET invalide ou inexistant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je renseigne un SIRET inexistant</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je saisis "99999999999999"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay rejette le SIRET
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "SIRET non trouvé dans le répertoire SIRENE. Vérifiez le numéro."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois corriger avant de continuer</p>
|
||
<hr />
|
||
<h2 id="7-rib-professionnel-obligatoire">7. RIB professionnel obligatoire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'upload mon RIB</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le RIB est scanné par Mangopay</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système vérifie que le titulaire correspond à mon SIRET
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'IBAN commence par "FR" (compte français)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si valide, le document est accepté</p>
|
||
<hr />
|
||
<h2 id="8-rib-particulier-refuse">8. RIB particulier refusé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'upload un RIB de compte particulier</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay détecte que le compte n'est pas professionnel</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le RIB est rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="9-piece-didentite-cni-en-cours-de-validite">9. Pièce d'identité CNI en cours de validité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'upload ma carte nationale d'identité</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay analyse le document</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la date d'expiration est vérifiée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si la CNI est valide, le document est accepté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon identité est vérifiée par OCR + vérification manuelle</p>
|
||
<hr />
|
||
<h2 id="10-piece-didentite-expiree-refusee">10. Pièce d'identité expirée refusée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'upload une CNI expirée depuis 2 ans</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay analyse le document</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le document est rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Pièce d'identité expirée. Veuillez fournir un document en cours de validité."</p>
|
||
<hr />
|
||
<h2 id="11-passeport-accepte-comme-alternative">11. Passeport accepté comme alternative</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai pas de CNI</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'upload mon passeport en cours de validité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay accepte le passeport
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon identité est vérifiée de la même manière</p>
|
||
<hr />
|
||
<h2 id="12-numero-tva-intracommunautaire-si-applicable">12. Numéro TVA intracommunautaire si applicable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon CA dépasse 37 000€/an
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis sorti de la franchise en base</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je renseigne mon numéro TVA intracommunautaire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le format "FR + 11 chiffres" est validé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Mangopay vérifie l'existence auprès de la Commission Européenne (VIES)</p>
|
||
<hr />
|
||
<h2 id="13-tva-non-applicable-pour-micro-bnc-sous-franchise">13. TVA non applicable pour micro-BNC sous franchise</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis auto-entrepreneur sous franchise en base
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon CA est <37 000€/an</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je remplis le formulaire KYC</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le champ "Numéro TVA" est optionnel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer sans TVA</p>
|
||
<hr />
|
||
<h2 id="14-kbis-3-mois-pour-societes">14. Kbis <3 mois pour sociétés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis gérant d'une SARL</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'upload mon extrait Kbis</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay vérifie que le Kbis date de moins de 3 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le SIRET correspond
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si valide, le document est accepté</p>
|
||
<hr />
|
||
<h2 id="15-kbis-trop-ancien-refuse">15. Kbis trop ancien refusé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'upload un Kbis de 5 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay analyse le document</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le Kbis est rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Le Kbis doit dater de moins de 3 mois. Téléchargez un extrait récent sur infogreffe.fr"</p>
|
||
<hr />
|
||
<h2 id="16-verification-identite-ne-correspond-pas-au-compte">16. Vérification identité ne correspond pas au compte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte RoadWave est au nom de "Jean Dupont"
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que ma CNI est au nom de "Pierre Martin"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay compare les identités</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le KYC est rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="17-liste-noire-anti-blanchiment-detectee">17. Liste noire anti-blanchiment détectée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon identité apparaît sur une liste anti-blanchiment</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay effectue la vérification AML (Anti-Money Laundering)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le KYC est automatiquement rejeté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Votre demande ne peut être acceptée pour des raisons de conformité légale"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon compte créateur peut être suspendu</p>
|
||
<hr />
|
||
<h2 id="18-delai-de-verification-24-72h-si-documents-conformes">18. Délai de vérification 24-72h si documents conformes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai soumis tous les documents valides</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay traite ma demande</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email "KYC en cours de vérification (24-72h)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon statut est "En attente de validation"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer à publier des contenus en attendant</p>
|
||
<hr />
|
||
<h2 id="19-validation-kyc-reussie">19. Validation KYC réussie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes documents sont conformes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay valide mon KYC après 48h</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email "Monétisation activée !"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon statut passe à "Monétisé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je commence à générer des revenus dès maintenant</p>
|
||
<hr />
|
||
<h2 id="20-rejet-kyc-pour-documents-invalides">20. Rejet KYC pour documents invalides</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai soumis une CNI floue et illisible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay analyse les documents</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le KYC est rejeté après 24h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email détaillant les documents à refournir:</p>
|
||
<hr />
|
||
<h2 id="21-e-wallet-mangopay-cree-automatiquement">21. E-wallet Mangopay créé automatiquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon KYC est validé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay finalise mon inscription</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un e-wallet Mangopay est créé automatiquement à mon nom
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous mes futurs revenus seront transférés vers ce wallet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les virements SEPA vers mon RIB seront effectués depuis ce wallet</p>
|
||
<hr />
|
||
<h2 id="22-conformite-rgpd-donnees-hebergees-eu">22. Conformité RGPD - Données hébergées EU</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je fournis mes documents KYC</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay stocke mes données</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les données sont hébergées en Union Européenne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Mangopay est régulé par l'ACPR (Autorité de Contrôle Prudentiel)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes données sont protégées selon le RGPD</p>
|
||
<hr />
|
||
<h2 id="23-kyc-gratuit-inclus-dans-mangopay">23. KYC gratuit inclus dans Mangopay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je complète le KYC</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le processus se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun frais ne m'est facturé (0€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun frais n'est facturé à RoadWave (inclus dans l'offre Mangopay)</p>
|
||
<hr />
|
||
<h2 id="24-base-legale-conformite-fiscale-francaise">24. Base légale - Conformité fiscale française</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave est une plateforme française</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je génère des revenus >1200€/an</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave doit déclarer ces revenus aux impôts (DAS2)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le KYC permet de garantir l'identité réelle du bénéficiaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela respecte la réglementation fiscale française</p>
|
||
<hr />
|
||
<h2 id="25-base-legale-directive-anti-blanchiment-eu-2018843">25. Base légale - Directive anti-blanchiment EU 2018/843</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave verse de l'argent aux créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le KYC est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave respecte la 5ème directive anti-blanchiment EU
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Mangopay effectue les vérifications requises (identité, liste noire, origine fonds)</p>
|
||
<hr />
|
||
<h2 id="26-notification-de-mise-a-jour-documents-expires">26. Notification de mise à jour documents expirés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma CNI va expirer dans 30 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte l'expiration proche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="27-suspension-monetisation-si-documents-expires">27. Suspension monétisation si documents expirés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma CNI est expirée depuis 10 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas mis à jour mes documents</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système vérifie mon statut KYC</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est suspendue automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne génère plus de revenus jusqu'à mise à jour</p>
|
||
<hr />
|
||
<h2 id="28-reactivation-sans-nouveau-kyc-si-donnees-a-jour">28. Réactivation sans nouveau KYC si données à jour</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai désactivé temporairement ma monétisation il y a 6 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes documents KYC sont toujours valides</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je réactive la monétisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je n'ai pas besoin de refaire le KYC
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la réactivation est immédiate</p>
|
||
<hr />
|
||
<h2 id="29-nouveau-kyc-requis-apres-2-ans-dinactivite">29. Nouveau KYC requis après 2 ans d'inactivité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai désactivé ma monétisation il y a 25 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de réactiver</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système demande un nouveau KYC
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois soumettre des documents à jour (CNI peut avoir changé)</p>
|
||
<hr />
|
||
<h2 id="30-support-createur-pour-problemes-kyc">30. Support créateur pour problèmes KYC</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon KYC est rejeté et je ne comprends pas pourquoi</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je contacte le support RoadWave</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un agent peut consulter les raisons du rejet Mangopay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> m'aider à fournir les bons documents</p>
|
||
<hr />
|
||
<h2 id="31-export-donnees-kyc-pour-rgpd">31. Export données KYC pour RGPD</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je demande l'export de mes données personnelles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les informations KYC sont incluses:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les documents scannés (CNI, RIB) sont exclus pour sécurité</p>
|
||
<hr />
|
||
<h2 id="32-suppression-compte-et-donnees-kyc">32. Suppression compte et données KYC</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je supprime définitivement mon compte RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes données KYC chez Mangopay sont archivées 10 ans (obligation légale)
|
||
<span style="color: #F44336"><strong>Mais</strong></span> supprimées de la base RoadWave immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon e-wallet est clôturé après versement du solde final</p>
|
||
<hr />
|
||
<h2 id="33-statistiques-kyc-pour-monitoring-plateforme">33. Statistiques KYC pour monitoring plateforme</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave suit la qualité du processus KYC</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un admin consulte les métriques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| métrique | valeur exemple |
|
||
|---|---|
|
||
| Demandes KYC ce mois | 247 |
|
||
| Taux de validation | 87% |
|
||
| Délai moyen validation | 36h |
|
||
| Taux de rejet (documents invalides) | 13% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela permet d'optimiser le processus</p>
|
||
<hr />
|
||
<h2 id="34-verification-siret-via-api-insee">34. Vérification SIRET via API INSEE</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je saisis mon SIRET</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système le valide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une requête est faite à l'API SIRENE de l'INSEE
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système vérifie que le SIRET existe et est actif
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> récupère le nom de l'entreprise pour pré-remplir le formulaire</p>
|
||
<hr />
|
||
<h2 id="35-detection-fraude-meme-siret-utilise-par-plusieurs-comptes">35. Détection fraude - Même SIRET utilisé par plusieurs comptes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un SIRET "12345678901234" est déjà utilisé par un autre créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'utiliser le même SIRET</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système détecte la duplication
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> affiche "Ce SIRET est déjà utilisé par un autre compte RoadWave"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois contacter le support si c'est une erreur</p>
|
||
<hr />
|
||
<h2 id="36-protection-donnees-sensibles-logs-chiffres">36. Protection données sensibles - Logs chiffrés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que des données KYC sensibles transitent dans le système</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les logs sont enregistrés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les numéros SIRET, IBAN et données CNI sont masqués:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule l'équipe sécurité peut accéder aux données complètes</p>
|
||
<hr />
|
||
<h2 id="37-backup-mangopay-des-documents-kyc">37. Backup Mangopay des documents KYC</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes documents KYC sont stockés chez Mangopay</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un audit est demandé par les autorités</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay peut fournir les documents originaux
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave n'a pas besoin de stocker ces documents (réduction risque RGPD)</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="obligations-fiscales">Obligations fiscales</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur monétisé</em>
|
||
<em>Je veux que RoadWave génère automatiquement les documents fiscaux requis</em>
|
||
<em>Afin de faciliter ma comptabilité et respecter la loi</em></p>
|
||
</blockquote>
|
||
<p><strong>30 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec la monétisation activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je génère des revenus sur RoadWave</p>
|
||
</blockquote>
|
||
<h2 id="1-generation-automatique-releve-mensuel-pdf">1. Génération automatique relevé mensuel PDF</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mois de janvier se termine</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule mes revenus du mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un relevé mensuel PDF est généré automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le PDF est disponible dans mon tableau de bord</p>
|
||
<hr />
|
||
<h2 id="2-contenu-du-releve-mensuel-pdf">2. Contenu du relevé mensuel PDF</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon relevé de janvier est généré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je télécharge le PDF</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le document contient:</p>
|
||
<hr />
|
||
<h2 id="3-telechargement-releve-depuis-tableau-de-bord">3. Téléchargement relevé depuis tableau de bord</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis sur mon tableau de bord créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à l'onglet "Revenus > Historique"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste de mes relevés mensuels:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="4-conservation-releves-accessibles-10-ans">4. Conservation relevés accessibles 10 ans</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai commencé la monétisation en janvier 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes relevés en janvier 2035 (10 ans plus tard)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les relevés depuis 2025 sont toujours accessibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux télécharger n'importe quel relevé historique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela respecte l'obligation de conservation comptable de 10 ans</p>
|
||
<hr />
|
||
<h2 id="5-export-csv-a-la-demande">5. Export CSV à la demande</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Exporter pour comptable"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je choisis la période "Année 2025"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un fichier CSV est généré et téléchargé</p>
|
||
<hr />
|
||
<h2 id="6-contenu-export-csv-detaille">6. Contenu export CSV détaillé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'exporte mes données comptables 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je télécharge le fichier CSV</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier contient:</p>
|
||
<hr />
|
||
<h2 id="7-transmission-a-lexpert-comptable">7. Transmission à l'expert-comptable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé mon export CSV 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je l'envoie à mon expert-comptable</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut importer le fichier dans son logiciel comptable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il saisit rapidement mes revenus RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela facilite ma déclaration fiscale annuelle</p>
|
||
<hr />
|
||
<h2 id="8-das2-genere-automatiquement-si-revenus-1200an">8. DAS2 généré automatiquement si revenus >1200€/an</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus 2025 totalisent 2,450€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'année 2025 se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave génère automatiquement une DAS2 pour les impôts
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la DAS2 est transmise à la DGFIP en janvier 2026</p>
|
||
<hr />
|
||
<h2 id="9-contenu-de-la-das2">9. Contenu de la DAS2</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave génère ma DAS2 pour 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la DGFIP reçoit la déclaration</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le document contient:</p>
|
||
<hr />
|
||
<h2 id="10-createur-recoit-une-copie-de-la-das2">10. Créateur reçoit une copie de la DAS2</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave transmet ma DAS2 aux impôts</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la transmission est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email avec une copie de la DAS2 en pièce jointe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux consulter le document dans mon tableau de bord</p>
|
||
<hr />
|
||
<h2 id="11-pas-de-das2-si-revenus-1200an">11. Pas de DAS2 si revenus <1200€/an</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus 2025 totalisent seulement 890€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'année 2025 se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune DAS2 n'est générée car le seuil de 1200€ n'est pas atteint
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je dois quand même déclarer mes revenus dans ma déclaration personnelle</p>
|
||
<hr />
|
||
<h2 id="12-base-legale-das2-obligation-france">12. Base légale DAS2 - Obligation France</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave verse des honoraires à des prestataires</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les revenus dépassent 1200€/an</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la déclaration DAS2 est obligatoire selon l'article 87 du Code Général des Impôts
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le non-respect entraîne une amende de 15€ par bénéficiaire non déclaré</p>
|
||
<hr />
|
||
<h2 id="13-transmission-das2-via-edi-tdfc">13. Transmission DAS2 via EDI-TDFC</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave génère 1,247 DAS2 pour l'année 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la transmission aux impôts est effectuée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la transmission se fait via le portail EDI-TDFC de la DGFIP
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transmission est automatisée (pas de saisie manuelle)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un accusé de réception est reçu sous 48h</p>
|
||
<hr />
|
||
<h2 id="14-createur-responsable-de-declarer-aux-impots">14. Créateur responsable de déclarer aux impôts</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu 2,450€ de revenus RoadWave en 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je fais ma déclaration fiscale en mai 2026</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois déclarer ces 2,450€ dans ma déclaration annuelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je suis auto-entrepreneur, je déclare en BNC (Bénéfices Non Commerciaux)</p>
|
||
<hr />
|
||
<h2 id="15-createur-responsable-des-cotisations-urssaf">15. Créateur responsable des cotisations URSSAF</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis auto-entrepreneur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu 2,450€ de revenus RoadWave en 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je fais ma déclaration URSSAF trimestrielle</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois déclarer ces revenus à l'URSSAF
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je paie ~22% de cotisations sociales (soit ~539€)</p>
|
||
<hr />
|
||
<h2 id="16-tva-non-applicable-en-franchise-en-base">16. TVA non applicable en franchise en base</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis auto-entrepreneur en micro-BNC
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon chiffre d'affaires est <37,800€/an</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je génère des revenus sur RoadWave</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je bénéficie de la franchise en base de TVA
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne facture pas de TVA à RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne récupère pas la TVA sur mes achats</p>
|
||
<hr />
|
||
<h2 id="17-tva-applicable-si-ca-37800an">17. TVA applicable si CA >37,800€/an</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon chiffre d'affaires total 2025 est 45,000€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dépasse le seuil de franchise en base (37,800€)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois facturer de la TVA (20%) à RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois obtenir un numéro TVA intracommunautaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois déclarer ma TVA mensuellement ou trimestriellement</p>
|
||
<hr />
|
||
<h2 id="18-conservation-justificatifs-10-ans-obligation-legale">18. Conservation justificatifs 10 ans - Obligation légale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je génère des revenus sur RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je télécharge mes relevés mensuels et exports CSV</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois les conserver 10 ans (obligation comptable France)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> en cas de contrôle fiscal, je dois pouvoir les fournir</p>
|
||
<hr />
|
||
<h2 id="19-mangopay-transmet-automatiquement-via-dac7">19. Mangopay transmet automatiquement via DAC7</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur monétisé sur RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'année se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay transmet automatiquement mes revenus aux autorités fiscales EU
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela respecte la directive DAC7 (2021/514) sur la transparence fiscale des plateformes</p>
|
||
<hr />
|
||
<h2 id="20-directive-dac7-obligations-plateforme">20. Directive DAC7 - Obligations plateforme</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave est une plateforme facilitant des transactions</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay gère les paiements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay transmet automatiquement:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave n'a pas besoin de faire cette transmission manuellement</p>
|
||
<hr />
|
||
<h2 id="21-justificatif-virement-preuve-bancaire-comptable">21. Justificatif virement = Preuve bancaire comptable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je reçois un virement de 150.00€ de Mangopay</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mon relevé bancaire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le virement avec la référence MANGOPAY-ABC123
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce relevé bancaire sert de justificatif comptable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux le fournir à mon expert-comptable ou aux impôts</p>
|
||
<hr />
|
||
<h2 id="22-notification-annuelle-rappel-declaration-fiscale">22. Notification annuelle rappel déclaration fiscale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur monétisé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le mois d'avril 2026 arrive (période déclaration impôts France)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email de rappel:</p>
|
||
<hr />
|
||
<h2 id="23-page-ressources-fiscales-pour-createurs">23. Page ressources fiscales pour créateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur monétisé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à "Aide > Fiscalité"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une page avec:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="24-dashboard-createur-recapitulatif-annuel">24. Dashboard créateur - Récapitulatif annuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte mon dashboard en décembre 2025</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à "Revenus > Récapitulatif annuel"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="25-generation-automatique-minimise-erreurs">25. Génération automatique minimise erreurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que tous les documents fiscaux sont générés automatiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un créateur télécharge ses documents</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les montants sont garantis corrects (issus de la base de données)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas d'erreur de saisie manuelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela réduit les risques de contrôle fiscal</p>
|
||
<hr />
|
||
<h2 id="26-conformite-rgpd-donnees-fiscales-chiffrees">26. Conformité RGPD - Données fiscales chiffrées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les documents fiscaux contiennent des données sensibles (SIRET, revenus)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les documents sont stockés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ils sont chiffrés au repos (encryption AES-256)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul le créateur et les admins autorisés peuvent y accéder
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les logs d'accès sont conservés pour audit</p>
|
||
<hr />
|
||
<h2 id="27-backup-documents-fiscaux-10-ans">27. Backup documents fiscaux 10 ans</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un document fiscal est généré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il est stocké dans la base de données</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une copie est sauvegardée sur S3 (stockage durable)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les backups sont répliqués sur 3 zones de disponibilité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la conservation est garantie 10 ans minimum</p>
|
||
<hr />
|
||
<h2 id="28-audit-trail-generation-das2">28. Audit trail génération DAS2</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 1,247 DAS2 sont générées en janvier 2026</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un audit est demandé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les événements sont loggés:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="29-statistiques-admin-conformite-fiscale">29. Statistiques admin - Conformité fiscale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin RoadWave consulte les métriques fiscales</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède au dashboard admin</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="30-support-createur-pour-questions-fiscales">30. Support créateur pour questions fiscales</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai une question sur ma déclaration fiscale</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je contacte le support RoadWave</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'agent peut consulter mes documents fiscaux
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> m'aider à comprendre ce que je dois déclarer
|
||
<span style="color: #F44336"><strong>Mais</strong></span> il ne peut pas me conseiller fiscalement (pas expert-comptable)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il me recommande de consulter un expert-comptable si nécessaire</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="paiement-des-createurs">Paiement des créateurs</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur monétisé</em>
|
||
<em>Je veux recevoir mes paiements mensuels de manière fiable</em>
|
||
<em>Afin d'être rémunéré pour mon travail</em></p>
|
||
</blockquote>
|
||
<p><strong>35 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec la monétisation activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon KYC est validé</p>
|
||
</blockquote>
|
||
<h2 id="1-seuil-minimum-de-50-atteint-paiement-effectue">1. Seuil minimum de 50€ atteint - Paiement effectué</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus du mois sont 73.45€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le dernier jour du mois arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon solde de 73.45€ est transféré vers "en attente de paiement"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le paiement sera effectué le 15 du mois prochain</p>
|
||
<hr />
|
||
<h2 id="2-seuil-minimum-de-50-non-atteint-report-mois-suivant">2. Seuil minimum de 50€ non atteint - Report mois suivant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus du mois sont 32.17€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le dernier jour du mois arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon solde de 32.17€ est reporté au mois suivant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Solde insuffisant pour paiement (<50€). Report mois prochain."</p>
|
||
<hr />
|
||
<h2 id="3-cumul-sur-plusieurs-mois-jusqua-atteindre-50">3. Cumul sur plusieurs mois jusqu'à atteindre 50€</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus sont:</p>
|
||
<pre><code>| mois | revenus | solde cumulé |
|
||
|---|---|---|
|
||
| Janvier | 18.50€ | 18.50€ |
|
||
| Février | 22.30€ | 40.80€ |
|
||
| Mars | 15.70€ | 56.50€ |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la fin du mois de mars arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le solde cumulé de 56.50€ dépasse les 50€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un paiement de 56.50€ est effectué le 15 avril</p>
|
||
<hr />
|
||
<h2 id="4-calcul-des-revenus-le-dernier-jour-du-mois">4. Calcul des revenus le dernier jour du mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que nous sommes le 31 janvier à 23h59</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule les revenus du mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une requête SQL agrège tous les revenus pub et premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le solde final du mois est figé dans monthly_revenues
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur du mois en cours repart à 0€ le 1er février</p>
|
||
<hr />
|
||
<h2 id="5-periode-de-traitement-contestations-1-14-du-mois">5. Période de traitement contestations 1-14 du mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus de janvier sont calculés à 150.00€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la période du 1-14 février arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave analyse les éventuelles fraudes ou contestations
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si une fraude est détectée, les revenus concernés sont retirés du solde
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le solde final est validé le 14 février</p>
|
||
<hr />
|
||
<h2 id="6-virement-sepa-le-15-du-mois-suivant">6. Virement SEPA le 15 du mois suivant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus de janvier validés sont 150.00€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 15 février arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay initie un virement SEPA depuis mon e-wallet vers mon RIB
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut du paiement passe à "En cours"</p>
|
||
<hr />
|
||
<h2 id="7-reception-virement-16-18-du-mois-1-3-jours-sepa">7. Réception virement 16-18 du mois (1-3 jours SEPA)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un virement SEPA a été initié le 15 février</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 1-3 jours ouvrés s'écoulent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois le virement sur mon compte bancaire entre le 16 et 18 février
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux consulter l'historique des paiements dans mon dashboard</p>
|
||
<hr />
|
||
<h2 id="8-virement-sepa-gratuit-pour-comptes-eu">8. Virement SEPA gratuit pour comptes EU</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon RIB est français (IBAN FR)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay effectue le virement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun frais n'est prélevé (virement SEPA gratuit)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois 100% du montant annoncé</p>
|
||
<hr />
|
||
<h2 id="9-virement-international-hors-eu-avec-frais-variables">9. Virement international hors EU avec frais variables</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur expatrié avec RIB hors Union Européenne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay effectue le virement international</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> des frais variables s'appliquent selon le pays
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les frais sont déduits du montant final
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le détail des frais dans mon historique</p>
|
||
<hr />
|
||
<h2 id="10-e-wallet-mangopay-automatique">10. E-wallet Mangopay automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon KYC est validé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mes revenus sont calculés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les revenus sont automatiquement transférés vers mon e-wallet Mangopay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'e-wallet est débité lors du virement SEPA vers mon RIB
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai aucune action manuelle à faire</p>
|
||
<hr />
|
||
<h2 id="11-tableau-de-bord-revenus-pub-temps-reel">11. Tableau de bord - Revenus pub temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mon tableau de bord créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'onglet "Revenus"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces valeurs sont mises à jour en temps réel (cache Redis, refresh 10 min)</p>
|
||
<hr />
|
||
<h2 id="12-tableau-de-bord-solde-en-attente-de-paiement">12. Tableau de bord - Solde en attente de paiement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus de janvier sont calculés et validés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que nous sommes le 10 février</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mon tableau de bord</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| métrique | valeur exemple |
|
||
|---|---|
|
||
| Solde en attente | 150.00€ |
|
||
| Date de paiement | 15 février 2025 |
|
||
| Statut | En attente |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="13-historique-des-virements-permanents">13. Historique des virements permanents</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis monétisé depuis 6 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'historique des paiements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste complète:</p>
|
||
<pre><code>| 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 |
|
||
| ... | ... | ... | ... |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="14-export-comptable-csv-telechargeable">14. Export comptable CSV téléchargeable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Télécharger export comptable"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le fichier CSV est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je télécharge un fichier contenant:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux transmettre ce fichier à mon expert-comptable</p>
|
||
<hr />
|
||
<h2 id="15-echec-virement-tentative-1-echouee">15. Échec virement - Tentative 1 échouée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un virement est initié le 15 février
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que mon RIB est invalide ou le compte est fermé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay détecte l'échec</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le statut passe à "Échec - Retry programmé le 18 février"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email m'alertant du problème</p>
|
||
<hr />
|
||
<h2 id="16-echec-virement-retry-automatique-j3">16. Échec virement - Retry automatique J+3</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le virement du 15 février a échoué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 18 février arrive (J+3)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay tente automatiquement un nouveau virement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le RIB est toujours invalide, le virement échoue à nouveau</p>
|
||
<hr />
|
||
<h2 id="17-echec-virement-retry-automatique-j7">17. Échec virement - Retry automatique J+7</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les 2 premières tentatives ont échoué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 22 février arrive (J+7)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay tente une 3ème et dernière fois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le virement échoue encore, la monétisation est suspendue</p>
|
||
<hr />
|
||
<h2 id="18-echec-virement-suspension-monetisation-apres-3-echecs">18. Échec virement - Suspension monétisation après 3 échecs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les 3 tentatives de virement ont échoué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte le 3ème échec</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma monétisation est suspendue automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="19-mise-a-jour-rib-et-reactivation-paiement">19. Mise à jour RIB et réactivation paiement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma monétisation est suspendue pour RIB invalide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon solde en attente est 150.00€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je mets à jour mon RIB avec un compte valide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay tente immédiatement un nouveau virement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le virement réussit, ma monétisation est réactivée automatiquement</p>
|
||
<hr />
|
||
<h2 id="20-notification-email-lors-de-chaque-paiement">20. Notification email lors de chaque paiement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un virement de 150.00€ est effectué le 15 février</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le virement est confirmé par Mangopay</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="21-justification-seuil-50-eviter-frais-bancaires-micro-sommes">21. Justification seuil 50€ - Éviter frais bancaires micro-sommes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Mangopay facture des frais fixes par virement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que les banques peuvent facturer des frais de réception</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un créateur génère seulement 5€/mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un virement mensuel coûterait proportionnellement trop cher
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le seuil de 50€ garantit des frais proportionnels raisonnables</p>
|
||
<hr />
|
||
<h2 id="22-comparaison-avec-youtube-seuil-100">22. Comparaison avec YouTube (seuil 100$)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que YouTube fixe le seuil à 100$ (~90€)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe le seuil à 50€</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave est plus accessible pour petits créateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les paiements arrivent plus rapidement</p>
|
||
<hr />
|
||
<h2 id="23-comparaison-avec-twitch-seuil-50">23. Comparaison avec Twitch (seuil 50$)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Twitch fixe le seuil à 50$ (~45€)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe le seuil à 50€</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le seuil est aligné sur Twitch
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les créateurs comprennent facilement le système</p>
|
||
<hr />
|
||
<h2 id="24-comparaison-avec-spotify-seuil-10-mais-delais-longs">24. Comparaison avec Spotify (seuil 10€ mais délais longs)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Spotify a un seuil bas de 10€ mais verse tous les 3 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave a un seuil de 50€ mais verse chaque mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les créateurs reçoivent leurs paiements plus régulièrement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la trésorerie est plus prévisible</p>
|
||
<hr />
|
||
<h2 id="25-releve-mensuel-pdf-automatique">25. Relevé mensuel PDF automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus de janvier sont calculés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 1er février arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un relevé mensuel PDF est généré automatiquement:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le PDF est téléchargeable depuis mon tableau de bord</p>
|
||
<hr />
|
||
<h2 id="26-conservation-releves-10-ans-obligation-comptable">26. Conservation relevés 10 ans (obligation comptable)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je génère des revenus sur RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je télécharge mes relevés mensuels</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois les conserver 10 ans (obligation légale France)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave conserve également une copie pendant 10 ans pour audit</p>
|
||
<hr />
|
||
<h2 id="27-dashboard-admin-monitoring-paiements">27. Dashboard admin - Monitoring paiements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin RoadWave consulte les paiements du mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède au dashboard admin</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="28-alerte-admin-si-taux-echec-5">28. Alerte admin si taux échec >5%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 8% des virements du mois ont échoué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte le taux d'échec élevé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte est envoyée à l'équipe technique:</p>
|
||
<hr />
|
||
<h2 id="29-statistiques-personnelles-moyenne-revenus-sur-6-mois">29. Statistiques personnelles - Moyenne revenus sur 6 mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis monétisé depuis 6 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| métrique | valeur |
|
||
|---|---|
|
||
| Revenus moyens/mois | 134.50€ |
|
||
| Meilleur mois | 189.00€ |
|
||
| Mois le plus bas | 87.30€ |
|
||
| Tendance | +12% ↗ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela m'aide à suivre ma progression</p>
|
||
<hr />
|
||
<h2 id="30-projection-revenus-annuels">30. Projection revenus annuels</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus moyens sont 134.50€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les projections</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système estime mes revenus annuels à ~1,614€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux anticiper mes déclarations fiscales</p>
|
||
<hr />
|
||
<h2 id="31-notification-seuil-symbolique-1000-cumules">31. Notification seuil symbolique 1000€ cumulés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes revenus cumulés depuis inscription atteignent 1000€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le paiement qui franchit ce seuil est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification:</p>
|
||
<hr />
|
||
<h2 id="32-performance-calcul-avec-100-000-createurs-monetises">32. Performance calcul avec 100 000 créateurs monétisés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave a 100 000 créateurs monétisés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le calcul des paiements du 15 du mois est lancé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job asynchrone traite les paiements par batch de 1000
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les virements sont initiés en 2-4 heures
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les serveurs Mangopay gèrent la charge sans problème</p>
|
||
<hr />
|
||
<h2 id="33-backup-des-donnees-de-paiement">33. Backup des données de paiement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les paiements sont critiques pour les créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un paiement est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les données sont sauvegardées dans PostgreSQL (principal)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> répliquées vers une base de backup (replica)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une copie d'archive est stockée sur S3 (conservation 10 ans)</p>
|
||
<hr />
|
||
<h2 id="34-audit-trail-complet-des-paiements">34. Audit trail complet des paiements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un paiement est initié, traité et complété</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un audit est demandé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les événements sont loggés:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces logs sont conservés 10 ans pour conformité</p>
|
||
<hr />
|
||
<h2 id="35-protection-fraude-detection-pattern-suspect">35. Protection fraude - Détection pattern suspect</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur génère subitement 10 000€ de revenus en 1 mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> que sa moyenne est de 50€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette anomalie</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le paiement est mis en attente pour vérification manuelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'équipe modération analyse le compte avant validation</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="sources-de-revenus-createurs">Sources de revenus créateurs</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur monétisé</em>
|
||
<em>Je veux générer des revenus via publicités et abonnés Premium</em>
|
||
<em>Afin d'être rémunéré pour mon travail</em></p>
|
||
</blockquote>
|
||
<p><strong>34 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec la monétisation activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon KYC est validé</p>
|
||
</blockquote>
|
||
<h2 id="1-cpm-createur-de-3-1000-ecoutes-completes">1. CPM créateur de 3€ / 1000 écoutes complètes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes contenus ont généré 1000 écoutes complètes par des utilisateurs gratuits</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le calcul des revenus du mois est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je touche 3.00€ pour ces 1000 écoutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce montant est ajouté à mon solde disponible</p>
|
||
<hr />
|
||
<h2 id="2-10-000-ecoutes-gratuits-30-de-revenus-pub">2. 10 000 écoutes gratuits → 30€ de revenus pub</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes contenus ont généré 10 000 écoutes complètes (utilisateurs gratuits)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le mois se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je touche 30.00€ de revenus publicitaires
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ces revenus sont visibles en temps réel dans mon tableau de bord</p>
|
||
<hr />
|
||
<h2 id="3-50-000-ecoutes-gratuits-150-de-revenus-pub">3. 50 000 écoutes gratuits → 150€ de revenus pub</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes contenus ont généré 50 000 écoutes complètes (utilisateurs gratuits)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le mois se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je touche 150.00€ de revenus publicitaires</p>
|
||
<hr />
|
||
<h2 id="4-100-000-ecoutes-gratuits-300-de-revenus-pub">4. 100 000 écoutes gratuits → 300€ de revenus pub</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes contenus ont généré 100 000 écoutes complètes (utilisateurs gratuits)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le mois se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je touche 300.00€ de revenus publicitaires</p>
|
||
<hr />
|
||
<h2 id="5-repartition-economique-plateforme-garde-94">5. Répartition économique - Plateforme garde 94%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité facturée 0.05€/écoute génère 50€ CPM</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la plateforme calcule la répartition</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le créateur touche 3€ (6% du CA pub)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la plateforme garde 47€ (94%) pour:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="6-ecoute-complete-80-du-contenu-ecoute">6. Écoute complète = ≥80% du contenu écouté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur gratuit écoute mon contenu de 10 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il écoute 8 minutes (80%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute compte comme "complète"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je génère 0.003€ de revenus pub (3€/1000)</p>
|
||
<hr />
|
||
<h2 id="7-ecoute-incomplete-80-ne-compte-pas">7. Écoute incomplète <80% ne compte pas</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur gratuit écoute mon contenu de 10 minutes
|
||
<span style="color: #F44336"><strong>Mais</strong></span> il skip après 5 minutes (50%)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le calcul des revenus est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cette écoute ne compte pas comme "complète"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne génère aucun revenu publicitaire pour cette écoute</p>
|
||
<hr />
|
||
<h2 id="8-ecoutes-premium-ne-comptent-pas-pour-les-revenus-pub">8. Écoutes Premium ne comptent pas pour les revenus pub</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium écoute 100% de mon contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le calcul des revenus publicitaires est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cette écoute ne compte pas dans les revenus pub
|
||
<span style="color: #F44336"><strong>Mais</strong></span> elle compte dans les revenus Premium (système séparé)</p>
|
||
<hr />
|
||
<h2 id="9-detection-bots-ecoutes-exclues">9. Détection bots - Écoutes exclues</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un bot génère 10 000 écoutes artificielles sur mes contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte le pattern suspect (rate limiting, IP unique, etc.)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ces écoutes sont marquées comme frauduleuses
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elles sont exclues du calcul des revenus publicitaires</p>
|
||
<hr />
|
||
<h2 id="10-comparaison-avec-youtube-3-51000-vues">10. Comparaison avec YouTube (3-5€/1000 vues)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que YouTube paie 3-5€/1000 vues</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe le CPM créateur à 3€/1000 écoutes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le tarif est aligné sur le bas de la fourchette YouTube
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela est compétitif pour un MVP sans marché publicitaire mature</p>
|
||
<hr />
|
||
<h2 id="11-comparaison-avec-spotify-3-41000-ecoutes">11. Comparaison avec Spotify (3-4€/1000 écoutes)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Spotify paie ~3-4€/1000 écoutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe le CPM créateur à 3€/1000 écoutes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le tarif est aligné sur l'industrie musicale
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les créateurs audio peuvent anticiper des revenus similaires</p>
|
||
<hr />
|
||
<h2 id="12-tableau-de-bord-revenus-pub-temps-reel">12. Tableau de bord - Revenus pub temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mon tableau de bord créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes revenus publicitaires</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| métrique | valeur exemple |
|
||
|---|---|
|
||
| Écoutes complètes ce mois (gratuit) | 23 456 |
|
||
| Revenus pub ce mois | 70.37€ |
|
||
| CPM effectif | 3.00€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces valeurs sont mises à jour toutes les 10 minutes</p>
|
||
<hr />
|
||
<h2 id="13-repartition-7030-createur-touche-70">13. Répartition 70/30 - Créateur touche 70%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium paie 4.99€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la répartition est calculée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 3.49€ sont reversés aux créateurs écoutés (70%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 1.50€ sont gardés par la plateforme (30%)</p>
|
||
<hr />
|
||
<h2 id="14-utilisateur-ecoute-3-createurs-repartition-proportionnelle">14. Utilisateur écoute 3 créateurs - Répartition proportionnelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium paie 4.99€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il écoute 3 créateurs ce mois:</p>
|
||
<pre><code>| créateur | temps écoute | ratio |
|
||
|---|---|---|
|
||
| Créateur A | 10h | 50% |
|
||
| Créateur B | 6h | 30% |
|
||
| Créateur C | 4h | 20% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le calcul des revenus Premium est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la répartition est:</p>
|
||
<pre><code>| créateur | revenus |
|
||
|---|---|
|
||
| Créateur A | 1.75€ |
|
||
| Créateur B | 1.05€ |
|
||
| Créateur C | 0.70€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> la somme totale versée aux créateurs est 3.50€ (70% de 4.99€)</p>
|
||
<hr />
|
||
<h2 id="15-calcul-sql-proportionnel-au-temps-decoute">15. Calcul SQL proportionnel au temps d'écoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium a écouté plusieurs créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule les revenus du mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la requête SQL suivante est exécutée:</p>
|
||
<hr />
|
||
<h2 id="16-utilisateur-ecoute-un-seul-createur-100-a-ce-createur">16. Utilisateur écoute un seul créateur - 100% à ce créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium paie 4.99€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il n'écoute qu'un seul créateur (moi)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le mois se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je touche 3.49€ (70% de 4.99€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois 100% de la part créateurs</p>
|
||
<hr />
|
||
<h2 id="17-utilisateur-premium-inactif-aucun-revenu-genere">17. Utilisateur Premium inactif - Aucun revenu généré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium paie 4.99€/mois
|
||
<span style="color: #F44336"><strong>Mais</strong></span> qu'il n'écoute aucun contenu ce mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le calcul des revenus Premium est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun créateur ne reçoit de revenus de cet utilisateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 3.49€ de la part créateurs restent à la plateforme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela couvre les coûts d'infrastructure</p>
|
||
<hr />
|
||
<h2 id="18-comparaison-avec-youtube-premium-7030">18. Comparaison avec YouTube Premium (70/30)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que YouTube Premium reverse 70% aux créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe également 70/30</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le modèle est aligné sur le standard industrie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les créateurs ont confiance dans l'équité du système</p>
|
||
<hr />
|
||
<h2 id="19-comparaison-avec-spotify-7030">19. Comparaison avec Spotify (70/30)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Spotify reverse 70% aux artistes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe également 70/30</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le modèle est identique à Spotify
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les créateurs audio comprennent facilement le système</p>
|
||
<hr />
|
||
<h2 id="20-apple-music-moins-avantageux-5248">20. Apple Music moins avantageux (52/48)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'Apple Music ne reverse que 52% aux artistes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave offre 70% aux créateurs</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave est plus avantageux de 18 points
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela devient un argument marketing fort</p>
|
||
<hr />
|
||
<h2 id="21-justification-equite-createurs-les-plus-ecoutes-gagnent-plus">21. Justification équité - Créateurs les plus écoutés gagnent plus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 2 créateurs ont le même nombre d'abonnés Premium
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que le Créateur A est écouté 20h/mois et le Créateur B seulement 2h/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les revenus Premium sont calculés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le Créateur A gagne 10× plus que le Créateur B
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela récompense la qualité et l'engagement (pas juste l'abonnement)</p>
|
||
<hr />
|
||
<h2 id="22-pas-de-winner-takes-all-equite-totale">22. Pas de "winner takes all" - Équité totale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium écoute 10 créateurs différents</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les revenus sont calculés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> chacun des 10 créateurs reçoit sa part proportionnelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de système où un seul créateur prend tout</p>
|
||
<hr />
|
||
<h2 id="23-marge-plateforme-30-couvre-absence-revenus-pub-premium">23. Marge plateforme 30% couvre absence revenus pub Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium ne voit aucune publicité</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la plateforme calcule ses revenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle ne touche que les 30% de l'abonnement Premium (1.50€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette marge compense la perte des revenus publicitaires (qui auraient été ~47€/1000 écoutes)</p>
|
||
<hr />
|
||
<h2 id="24-tableau-de-bord-revenus-premium-temps-reel">24. Tableau de bord - Revenus Premium temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mon tableau de bord créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes revenus Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| métrique | valeur exemple |
|
||
|---|---|
|
||
| Abonnés Premium actifs ayant écouté | 47 |
|
||
| Heures d'écoute Premium ce mois | 234h |
|
||
| Revenus Premium ce mois | 89.23€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces valeurs sont mises à jour toutes les 10 minutes</p>
|
||
<hr />
|
||
<h2 id="25-revenus-cumules-pub-premium">25. Revenus cumulés pub + premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai généré ce mois:</p>
|
||
<pre><code>| source | montant |
|
||
|---|---|
|
||
| Revenus pub | 150.00€ |
|
||
| Revenus Premium | 89.23€ |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mon solde disponible</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le total est 239.23€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce solde sera versé le 15 du mois prochain (si ≥50€)</p>
|
||
<hr />
|
||
<h2 id="26-dashboard-createur-vue-densemble">26. Dashboard créateur - Vue d'ensemble</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à mon tableau de bord créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la page revenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="27-export-comptable-csv-pour-expert-comptable">27. Export comptable CSV pour expert-comptable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Exporter pour comptable"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je télécharge un fichier CSV:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux transmettre ce fichier à mon expert-comptable</p>
|
||
<hr />
|
||
<h2 id="28-notification-hebdomadaire-progression-revenus">28. Notification hebdomadaire progression revenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis créateur monétisé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> chaque lundi matin arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email récapitulatif:</p>
|
||
<hr />
|
||
<h2 id="29-graphique-evolution-revenus-sur-12-mois">29. Graphique évolution revenus sur 12 mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis monétisé depuis 12 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un graphique en courbes montrant:</p>
|
||
<pre><code>| mois | revenus pub | revenus premium | total |
|
||
|---|---|---|---|
|
||
| Jan 25 | 150€ | 89€ | 239€ |
|
||
| Déc 24 | 123€ | 55€ | 178€ |
|
||
| Nov 24 | 100€ | 56€ | 156€ |
|
||
| ... | ... | ... | ... |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela m'aide à suivre ma progression</p>
|
||
<hr />
|
||
<h2 id="30-top-3-contenus-les-plus-rentables-du-mois">30. Top 3 contenus les plus rentables du mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié 20 contenus ce mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques détaillées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois mon top 3 contenus:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela m'aide à comprendre quel type de contenu plaît le plus</p>
|
||
<hr />
|
||
<h2 id="31-alertes-seuils-de-revenus">31. Alertes seuils de revenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai activé les notifications de seuils</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mes revenus du mois dépassent 100€ pour la première fois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification:</p>
|
||
<hr />
|
||
<h2 id="32-performance-calcul-avec-100-000-createurs">32. Performance calcul avec 100 000 créateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave a 100 000 créateurs monétisés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le calcul des revenus mensuels est lancé le dernier jour du mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job asynchrone traite tous les créateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le calcul prend environ 2-4 heures pour tous les créateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les résultats sont stockés dans la table monthly_revenues</p>
|
||
<hr />
|
||
<h2 id="33-cache-redis-pour-metriques-temps-reel">33. Cache Redis pour métriques temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte mon dashboard plusieurs fois par jour</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page se charge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les compteurs sont récupérés depuis Redis:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le temps de réponse est <30ms</p>
|
||
<hr />
|
||
<h2 id="34-prevision-revenus-fin-de-mois">34. Prévision revenus fin de mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que nous sommes le 20 du mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes revenus actuels sont 160€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule la projection</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il estime les revenus fin de mois à ~240€ (extrapolation linéaire)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> affiche "Projection fin de mois: ~240€"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela m'aide à anticiper mes revenus</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="actions-complementaires-a-larret">Actions complémentaires à l'arrêt</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur avec véhicule arrêté</em>
|
||
<em>Je veux accéder à des actions avancées depuis l'application mobile</em>
|
||
<em>Afin de liker explicitement, m'abonner ou signaler du contenu</em></p>
|
||
</blockquote>
|
||
<p><strong>23 scénarios</strong> (21 standards, 2 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le véhicule est à l'arrêt (vitesse GPS = 0 km/h)</p>
|
||
</blockquote>
|
||
<h2 id="1-like-explicite-avec-bouton-cur">1. Like explicite avec bouton cœur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu tagué "Automobile"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Automobile" est à 60%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton cœur "Like"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Automobile" augmente de 2%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une animation de cœur rouge s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une vibration courte est déclenchée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Automobile" est maintenant à 62%</p>
|
||
<hr />
|
||
<h2 id="2-like-explicite-cumulable-avec-like-automatique">2. Like explicite cumulable avec like automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté un contenu "Voyage" à 85%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu un like automatique renforcé (+2%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Voyage" est à 52%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton cœur "Like"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Voyage" augmente encore de 2%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Voyage" passe à 54%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les deux likes sont cumulés</p>
|
||
<hr />
|
||
<h2 id="3-unlike-retire-le-like-manuel-uniquement">3. Unlike retire le like manuel uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai liké manuellement un contenu "Sport"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Sport" est à 57%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique à nouveau sur le bouton cœur (toggle)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le cœur redevient vide (unlike)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Sport" diminue de 2%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Sport" revient à 55%</p>
|
||
<hr />
|
||
<h2 id="4-unlike-ne-retire-pas-le-like-automatique">4. Unlike ne retire pas le like automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté un contenu "Musique" à 90%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu un like automatique renforcé (+2%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Musique" est à 52%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai PAS liké manuellement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'interface</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Unlike" n'est pas disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cœur reste grisé (aucun like manuel)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge reste à 52%</p>
|
||
<hr />
|
||
<h2 id="5-abonnement-a-un-createur">5. Abonnement à un créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur publie des contenus tagués "Automobile" et "Technologie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges sont:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 50% |
|
||
| Technologie | 45% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "S'abonner" sur le profil du créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Automobile" augmente de 5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Technologie" augmente de 5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une animation d'étoile dorée s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge "Abonné ✓" apparaît sur le profil
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes nouvelles jauges sont:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 55% |
|
||
| Technologie | 50% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="6-desabonnement-dun-createur">6. Désabonnement d'un créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges "Automobile" et "Technologie" sont à 55% et 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Se désabonner"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma jauge "Automobile" diminue de 5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Technologie" diminue de 5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le badge "Abonné ✓" disparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes nouvelles jauges sont:</p>
|
||
<pre><code>| catégorie | niveau |
|
||
|---|---|
|
||
| Automobile | 50% |
|
||
| Technologie | 45% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="7-signalement-dun-contenu-inapproprie">7. Signalement d'un contenu inapproprié</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le menu contextuel "⋮"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je sélectionne "Signaler"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un formulaire de signalement s'ouvre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois sélectionner une catégorie:</p>
|
||
<pre><code>| Catégorie |
|
||
|---|
|
||
| Haine et violence |
|
||
| Contenu sexuel |
|
||
| Illégalité |
|
||
| Droits d'auteur |
|
||
| Spam |
|
||
| Désinformation (fake news) |
|
||
| Autre |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux ajouter un commentaire optionnel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement est envoyé au flux de modération</p>
|
||
<hr />
|
||
<h2 id="8-feedback-visuel-pour-like-explicite">8. Feedback visuel pour like explicite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur le bouton cœur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le like est enregistré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une animation de cœur rouge se lance (0.5s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cœur reste rouge plein
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une vibration haptique courte est déclenchée (iOS: .light, Android: 50ms)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge "♥ Ajouté à vos favoris" s'affiche 2 secondes</p>
|
||
<hr />
|
||
<h2 id="9-feedback-visuel-pour-abonnement">9. Feedback visuel pour abonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "S'abonner"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'abonnement est enregistré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une animation d'étoile dorée se lance (0.8s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton devient "Abonné ✓" avec badge doré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification "Abonné à [Créateur]" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus du créateur seront boostés +30% dans l'algo</p>
|
||
<hr />
|
||
<h2 id="10-bouton-like-desactive-si-vitesse-10-kmh">10. Bouton like désactivé si vitesse >10 km/h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à 50 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder au bouton cœur dans l'app mobile</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton est grisé et non cliquable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message "Arrêtez-vous pour liker" s'affiche si clic tenté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les commandes au volant physiques fonctionnent</p>
|
||
<hr />
|
||
<h2 id="11-bouton-abonnement-desactive-en-conduite">11. Bouton abonnement désactivé en conduite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à 40 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder au profil créateur dans l'app</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "S'abonner" est désactivé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message "Arrêtez-vous pour vous abonner" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la navigation dans l'app est limitée aux fonctions lecture</p>
|
||
<hr />
|
||
<h2 id="12-signalement-possible-en-conduite-via-vocal">12. Signalement possible en conduite via vocal</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à 60 km/h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'utilise CarPlay avec Siri</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, signale ce contenu"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Siri demande "Quelle catégorie ?"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux répondre vocalement "Spam" ou autre catégorie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le signalement est enregistré sans toucher l'écran</p>
|
||
<hr />
|
||
<h2 id="13-actions-vocales-disponibles-avec-carplayandroid-auto">13. Actions vocales disponibles avec CarPlay/Android Auto</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis avec CarPlay activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, like ce podcast"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un like explicite (+2%) est enregistré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Siri confirme "J'ai ajouté ce contenu à vos favoris"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "OK Google, abonne-moi à ce créateur"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'abonnement est enregistré (+5% toutes jauges)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Google Assistant confirme "Vous êtes maintenant abonné"</p>
|
||
<hr />
|
||
<h2 id="14-menu-contextuel-accessible-a-larret-uniquement">14. Menu contextuel accessible à l'arrêt uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le véhicule est à l'arrêt</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le menu "⋮" (3 points verticaux)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les options disponibles sont:</p>
|
||
<pre><code>| Option |
|
||
|---|
|
||
| Like (cœur) |
|
||
| S'abonner au créateur |
|
||
| Signaler |
|
||
| Partager |
|
||
| Voir le profil du créateur |
|
||
| Télécharger (mode offline) |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> toutes les options sont cliquables</p>
|
||
<hr />
|
||
<h2 id="15-menu-contextuel-limite-en-conduite">15. Menu contextuel limité en conduite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à 30 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'ouvrir le menu "⋮"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules 2 options sont disponibles:</p>
|
||
<pre><code>| Option |
|
||
|---|
|
||
| Signaler (vocal possible) |
|
||
| Suivant |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> les actions complexes sont désactivées</p>
|
||
<hr />
|
||
<h2 id="16-persistance-des-likes-manuels-en-base-de-donnees">16. Persistance des likes manuels en base de données</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je like manuellement 5 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je ferme l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je me reconnecte plus tard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous mes likes manuels sont toujours présents
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les cœurs rouges sont affichés sur les contenus likés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes jauges reflètent toujours l'impact (+2% × 5 likes)</p>
|
||
<hr />
|
||
<h2 id="17-liste-mes-contenus-likes-accessible-dans-profil">17. Liste "Mes contenus likés" accessible dans profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai liké manuellement 10 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mon profil utilisateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une section "❤️ Mes favoris"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la liste affiche les 10 contenus likés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer pour réécouter
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux retirer un like (unlike) depuis cette liste</p>
|
||
<hr />
|
||
<h2 id="18-liste-mes-abonnements-accessible-dans-profil">18. Liste "Mes abonnements" accessible dans profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 5 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mon profil utilisateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une section "⭐ Mes abonnements"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la liste affiche les 5 créateurs avec leurs avatars
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accéder au profil de chaque créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux me désabonner depuis cette liste</p>
|
||
<hr />
|
||
<h2 id="19-impact-abonnement-sur-tous-les-tags-du-createur">19. Impact abonnement sur tous les tags du créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur a publié des contenus avec ces tags:</p>
|
||
<pre><code>| Contenu | Tags |
|
||
|---|---|
|
||
| C1 | Automobile, Voyage |
|
||
| C2 | Automobile, Technologie |
|
||
| C3 | Voyage, Famille |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges sont toutes à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je m'abonne à ce créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les jauges impactées sont:</p>
|
||
<pre><code>| Tag | Impact |
|
||
|---|---|
|
||
| Automobile | +5% |
|
||
| Voyage | +5% |
|
||
| Technologie | +5% |
|
||
| Famille | +5% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> toutes les autres jauges restent à 50%</p>
|
||
<hr />
|
||
<h2 id="20-limite-dabonnements-200-maximum">20. Limite d'abonnements (200 maximum)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à 200 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de m'abonner à un 201ème créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message "Limite de 200 abonnements atteinte" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois me désabonner d'un créateur existant pour en ajouter un nouveau</p>
|
||
<hr />
|
||
<h2 id="21-confirmation-avant-desabonnement">21. Confirmation avant désabonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné à un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Se désabonner"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup de confirmation s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois confirmer pour valider
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux annuler pour conserver l'abonnement</p>
|
||
<hr />
|
||
<h2 id="22-plan-cumul-like-automatique-like-manuel">22. 📋 Plan: Cumul like automatique + like manuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est tagué "Sport"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Sport" est à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute à <pourcentage>% (like auto <auto>)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je like manuellement (+2%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'impact total est <total>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma nouvelle jauge est <nouveau_niveau></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>pourcentage</th>
|
||
<th>auto</th>
|
||
<th>total</th>
|
||
<th>nouveau_niveau</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>0</td>
|
||
<td>+2%</td>
|
||
<td>52%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>30</td>
|
||
<td>+1%</td>
|
||
<td>+3%</td>
|
||
<td>53%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>+1%</td>
|
||
<td>+3%</td>
|
||
<td>53%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>80</td>
|
||
<td>+2%</td>
|
||
<td>+4%</td>
|
||
<td>54%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>95</td>
|
||
<td>+2%</td>
|
||
<td>+4%</td>
|
||
<td>54%</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="23-plan-actions-disponibles-selon-vitesse-gps">23. 📋 Plan: Actions disponibles selon vitesse GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je roule à <vitesse> km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder à <action></p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est <disponibilite></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>vitesse</th>
|
||
<th>action</th>
|
||
<th>disponibilite</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>Like manuel</td>
|
||
<td>disponible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>Abonnement</td>
|
||
<td>disponible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>Signalement</td>
|
||
<td>disponible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>Like manuel</td>
|
||
<td>disponible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>Abonnement</td>
|
||
<td>disponible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>Like manuel</td>
|
||
<td>désactivée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>Abonnement</td>
|
||
<td>désactivée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>Like manuel</td>
|
||
<td>désactivée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>Abonnement</td>
|
||
<td>désactivée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>Signalement vocal</td>
|
||
<td>disponible</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="commande-precedent">Commande "Précédent"</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur</em>
|
||
<em>Je veux que le bouton "Précédent" ait un comportement intelligent</em>
|
||
<em>Afin de rejouer le contenu actuel ou revenir au précédent selon la progression</em></p>
|
||
</blockquote>
|
||
<p><strong>19 scénarios</strong> (17 standards, 2 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-precedent-apres-10s-revient-au-contenu-precedent">1. Précédent après <10s revient au contenu précédent</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté le contenu "A" pendant 2 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute maintenant le contenu "B" depuis 5 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture revient au contenu "A"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la position de lecture est à 2 minutes (position exacte sauvegardée)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu "B" reste en historique</p>
|
||
<hr />
|
||
<h2 id="2-precedent-apres-10s-rejoue-le-contenu-actuel">2. Précédent après ≥10s rejoue le contenu actuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute le contenu "C" depuis 15 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "C" rejoue depuis le début (position 0:00)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture ne revient pas au contenu précédent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la progress bar revient à 0%</p>
|
||
<hr />
|
||
<h2 id="3-precedent-exactement-a-10s-rejoue-le-contenu-actuel">3. Précédent exactement à 10s rejoue le contenu actuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute le contenu "D" depuis exactement 10 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "D" rejoue depuis le début
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture ne revient pas au contenu précédent</p>
|
||
<hr />
|
||
<h2 id="4-precedent-sur-le-premier-contenu-de-session">4. Précédent sur le premier contenu de session</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de démarrer l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute le contenu "Premier" depuis 3 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "Premier" rejoue depuis le début
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun contenu précédent n'existe</p>
|
||
<hr />
|
||
<h2 id="5-historique-de-navigation-limite-a-10-contenus">5. Historique de navigation limité à 10 contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté 10 contenus [C1, C2, ..., C10]
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'historique Redis contient 10 entrées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je passe au contenu C11</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu C1 est supprimé de l'historique (FIFO)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique contient [C2, C3, ..., C10, C11]
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la taille reste à 10 contenus maximum</p>
|
||
<hr />
|
||
<h2 id="6-position-exacte-sauvegardee-dans-lhistorique">6. Position exacte sauvegardée dans l'historique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute le contenu "A" (durée 5 minutes)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'atteins 2 minutes 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique enregistre:</p>
|
||
<pre><code>| content_id | position_seconds | listened_at |
|
||
|---|---|---|
|
||
| A | 150 | 2026-01-21T10:30:00 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reviens au contenu "A" via "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture reprend exactement à 2 minutes 30 secondes</p>
|
||
<hr />
|
||
<h2 id="7-navigation-arriere-sur-plusieurs-contenus">7. Navigation arrière sur plusieurs contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté dans l'ordre: A (2min), B (30s), C (3min)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute maintenant D depuis 1 seconde</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent" (1ère fois)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reviens au contenu C à la position 3 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent" (<10s sur C)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reviens au contenu B à la position 30 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent" (<10s sur B)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reviens au contenu A à la position 2 minutes</p>
|
||
<hr />
|
||
<h2 id="8-precedent-apres-milieu-du-contenu-rejoue-depuis-debut">8. Précédent après milieu du contenu rejoue depuis début</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu de 5 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'atteins 2 minutes 30 secondes (milieu)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu actuel rejoue depuis 0:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne reviens pas au contenu précédent</p>
|
||
<hr />
|
||
<h2 id="9-enchainement-suivant-puis-precedent-rapide">9. Enchaînement Suivant puis Précédent rapide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute le contenu "A" depuis 1 minute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "B" démarre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie immédiatement sur "Précédent" (2s après)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reviens au contenu "A" à la position 1 minute
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu "B" reste dans l'historique</p>
|
||
<hr />
|
||
<h2 id="10-transition-fluide-avec-animation-03s">10. Transition fluide avec animation 0.3s</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le changement de contenu se produit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la transition audio utilise un fade out/in de 0.3 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la progress bar revient avec une animation fluide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'interface ne montre aucun message de confirmation</p>
|
||
<hr />
|
||
<h2 id="11-historique-survit-au-changement-de-reseau">11. Historique survit au changement de réseau</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un historique de 5 contenus en cache Redis</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je perds la connexion réseau temporairement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je reviens en ligne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique de navigation est toujours disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux toujours utiliser "Précédent"</p>
|
||
<hr />
|
||
<h2 id="12-historique-stocke-en-redis-avec-structure-complete">12. Historique stocké en Redis avec structure complète</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté 3 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le cache Redis</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la structure est:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'ordre est du plus récent au plus ancien</p>
|
||
<hr />
|
||
<h2 id="13-precedent-sur-contenu-en-cours-au-debut-10s-du-premier">13. Précédent sur contenu en cours au début (<10s) du premier</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je démarre une session avec le contenu "Initial"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute depuis 3 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "Initial" rejoue depuis le début
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune erreur n'est générée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique reste vide</p>
|
||
<hr />
|
||
<h2 id="14-compteur-de-temps-respecte-les-seuils-exacts">14. Compteur de temps respecte les seuils exacts</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le temps écoulé est de 9.9 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reviens au contenu précédent</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le temps écoulé est de 10.0 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu actuel rejoue depuis le début</p>
|
||
<hr />
|
||
<h2 id="15-progress-bar-visuelle-reflete-le-retour-exact">15. Progress bar visuelle reflète le retour exact</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté le contenu "A" jusqu'à 75% (3min45 sur 5min)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis passé au contenu "B"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reviens au contenu "A" via "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progress bar affiche 75%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'indicateur de temps affiche "3:45 / 5:00"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture reprend exactement à cet endroit</p>
|
||
<hr />
|
||
<h2 id="16-metadonnees-dhistorique-incluent-timestamp-precis">16. Métadonnées d'historique incluent timestamp précis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu "X" pendant 45 secondes à 10:30:15</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je passe au contenu suivant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique enregistre:</p>
|
||
<pre><code>| content_id | position_seconds | listened_at |
|
||
|---|---|---|
|
||
| X | 45 | 2026-01-21T10:30:15Z |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le timestamp précis permet l'analyse d'usage</p>
|
||
<hr />
|
||
<h2 id="17-suppression-fifo-respecte-lordre-chronologique">17. Suppression FIFO respecte l'ordre chronologique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un historique de [C1@10:00, C2@10:02, ..., C10@10:20]</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ajoute C11 à 10:22</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> C1 (le plus ancien) est supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique contient [C2@10:02, ..., C11@10:22]
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la taille reste exactement 10 entrées</p>
|
||
<hr />
|
||
<h2 id="18-plan-comportement-selon-temps-ecoute">18. 📋 Plan: Comportement selon temps écouté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu depuis <temps> secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est <comportement></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>temps</th>
|
||
<th>comportement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>revenir au contenu précédent</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>revenir au contenu précédent</td>
|
||
</tr>
|
||
<tr>
|
||
<td>9</td>
|
||
<td>revenir au contenu précédent</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>rejouer le contenu actuel depuis 0:00</td>
|
||
</tr>
|
||
<tr>
|
||
<td>11</td>
|
||
<td>rejouer le contenu actuel depuis 0:00</td>
|
||
</tr>
|
||
<tr>
|
||
<td>30</td>
|
||
<td>rejouer le contenu actuel depuis 0:00</td>
|
||
</tr>
|
||
<tr>
|
||
<td>180</td>
|
||
<td>rejouer le contenu actuel depuis 0:00</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="19-plan-positions-de-reprise-exactes">19. 📋 Plan: Positions de reprise exactes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu de 10 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'atteins <position> et passe au suivant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je reviens via "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture reprend exactement à <position></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>position</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0:15</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1:30</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3:45</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5:00</td>
|
||
</tr>
|
||
<tr>
|
||
<td>7:23</td>
|
||
</tr>
|
||
<tr>
|
||
<td>9:50</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="commandes-vocales-carplay-et-android-auto">Commandes vocales CarPlay et Android Auto</h1>
|
||
<blockquote>
|
||
<p><em>En tant que conducteur avec CarPlay ou Android Auto</em>
|
||
<em>Je veux utiliser des commandes vocales pour interagir avec l'application</em>
|
||
<em>Afin de garder les mains sur le volant et les yeux sur la route</em></p>
|
||
</blockquote>
|
||
<p><strong>25 scénarios</strong> (23 standards, 2 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que CarPlay ou Android Auto est activé</p>
|
||
</blockquote>
|
||
<h2 id="1-disponibilite-des-commandes-vocales-uniquement-avec-carplayandroid-auto">1. Disponibilité des commandes vocales uniquement avec CarPlay/Android Auto</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis avec CarPlay activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Siri est disponible pour les commandes RoadWave</p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis avec Android Auto activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "OK Google"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Google Assistant est disponible pour les commandes RoadWave</p>
|
||
<hr />
|
||
<h2 id="2-parc-automobile-compatible-avec-vocal-30-40-en-2026">2. Parc automobile compatible avec vocal (30-40% en 2026)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que nous sommes en 2026</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les statistiques du parc automobile EU</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> environ 30-40% des véhicules ont CarPlay ou Android Auto
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ces utilisateurs peuvent utiliser les commandes vocales
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 60-70% restants utilisent les commandes au volant uniquement</p>
|
||
<hr />
|
||
<h2 id="3-commande-vocale-like-ce-podcast-avec-siri">3. Commande vocale "Like ce podcast" avec Siri</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu tagué "Automobile"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma jauge "Automobile" est à 60%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, like ce podcast"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un like explicite (+2%) est enregistré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge "Automobile" passe à 62%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Siri confirme vocalement "J'ai ajouté ce contenu à vos favoris"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune interaction écran n'est requise</p>
|
||
<hr />
|
||
<h2 id="4-commande-vocale-like-ce-contenu-avec-google-assistant">4. Commande vocale "Like ce contenu" avec Google Assistant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu tagué "Voyage"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "OK Google, like ce contenu"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un like explicite est enregistré (+2%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Google Assistant confirme "J'ai liké ce contenu pour vous"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la commande fonctionne sans toucher l'écran</p>
|
||
<hr />
|
||
<h2 id="5-commande-vocale-abonne-moi-a-ce-createur">5. Commande vocale "Abonne-moi à ce créateur"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu d'un créateur tagué "Automobile" et "Technologie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes jauges sont à 50% et 45%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, abonne-moi à ce créateur"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'abonnement est enregistré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes jauges augmentent de 5% chacune (55% et 50%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Siri confirme "Vous êtes maintenant abonné à [Nom du créateur]"</p>
|
||
<hr />
|
||
<h2 id="6-commande-vocale-passe-au-contenu-suivant">6. Commande vocale "Passe au contenu suivant"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu "A"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, passe au contenu suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "B" démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la commande a le même effet que le bouton physique "Suivant"</p>
|
||
<hr />
|
||
<h2 id="7-commande-vocale-signale-ce-contenu">7. Commande vocale "Signale ce contenu"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu inapproprié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "OK Google, signale ce contenu"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Google Assistant demande "Quelle catégorie ?"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je réponds vocalement "Spam"
|
||
<span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est enregistré avec la catégorie "Spam"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Google Assistant confirme "J'ai signalé ce contenu"</p>
|
||
<hr />
|
||
<h2 id="8-commande-vocale-avec-categorie-de-signalement">8. Commande vocale avec catégorie de signalement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, signale ce contenu pour haine"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est enregistré avec la catégorie "Haine et violence"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Siri confirme "J'ai signalé ce contenu pour haine et violence"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le flux de modération reçoit le signalement</p>
|
||
<hr />
|
||
<h2 id="9-liste-des-categories-de-signalement-vocales-supportees">9. Liste des catégories de signalement vocales supportées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je dis "signale ce contenu pour [catégorie]"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la catégorie est:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est enregistré avec la bonne catégorie</p>
|
||
<hr />
|
||
<h2 id="10-commande-vocale-non-reconnue-fallback">10. Commande vocale non reconnue - fallback</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je dis "Hey Siri, super ce podcast"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Siri ne reconnaît pas l'intent RoadWave</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Siri répond "Je ne comprends pas cette commande RoadWave"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle suggère "Dites 'like ce podcast' ou 'passe au suivant'"</p>
|
||
<hr />
|
||
<h2 id="11-commandes-vocales-disponibles-en-conduite-uniquement">11. Commandes vocales disponibles en conduite uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je roule à 50 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise les commandes vocales</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les commandes sont disponibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="12-intent-ios-personnalise-pour-roadwave">12. Intent iOS personnalisé pour RoadWave</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'app iOS implémente les Intents</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je configure les Shortcuts iOS</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les intents suivants sont disponibles:</p>
|
||
<pre><code>| Intent Name | Action |
|
||
|---|---|
|
||
| LikeCurrentContentIntent | Like explicite |
|
||
| SubscribeToCreatorIntent | Abonnement |
|
||
| ReportContentIntent | Signalement |
|
||
| SkipToNextContentIntent | Suivant |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> Siri les reconnaît automatiquement</p>
|
||
<hr />
|
||
<h2 id="13-intent-android-personnalise-pour-roadwave">13. Intent Android personnalisé pour RoadWave</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'app Android implémente les Voice Actions</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je configure les actions Google Assistant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les actions suivantes sont disponibles:</p>
|
||
<pre><code>| Action Name | Action |
|
||
|---|---|
|
||
| com.roadwave.LIKE_CONTENT | Like explicite |
|
||
| com.roadwave.SUBSCRIBE_CREATOR | Abonnement |
|
||
| com.roadwave.REPORT_CONTENT | Signalement |
|
||
| com.roadwave.SKIP_NEXT | Suivant |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> Google Assistant les reconnaît</p>
|
||
<hr />
|
||
<h2 id="14-confirmation-vocale-apres-action-reussie">14. Confirmation vocale après action réussie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je dis "Hey Siri, like ce podcast"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'action est enregistrée avec succès</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Siri répond immédiatement avec confirmation:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la réponse est naturelle et concise
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle ne distrait pas de la conduite</p>
|
||
<hr />
|
||
<h2 id="15-gestion-derreur-vocale-si-action-echoue">15. Gestion d'erreur vocale si action échoue</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je dis "Hey Siri, abonne-moi à ce créateur"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai atteint la limite de 200 abonnements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Siri essaie d'enregistrer l'abonnement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Siri répond "Impossible de s'abonner, limite de 200 abonnements atteinte"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle suggère "Désabonnez-vous d'un créateur pour continuer"</p>
|
||
<hr />
|
||
<h2 id="16-commandes-vocales-multilingues-francais">16. Commandes vocales multilingues (français)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon Siri est configuré en français</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, j'aime ce podcast"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la commande est reconnue (variante de "like ce podcast")</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, mets une étoile"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la commande est reconnue (variante de "like")</p>
|
||
<hr />
|
||
<h2 id="17-implementation-post-mvp-sprint-5">17. Implémentation post-MVP (Sprint 5)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les commandes vocales sont une feature Sprint 5</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le MVP est lancé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules les commandes au volant physiques sont disponibles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le Sprint 5 est déployé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les intents iOS/Android sont activés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les commandes vocales deviennent disponibles</p>
|
||
<hr />
|
||
<h2 id="18-priorisation-commandes-vocales-vs-boutons-physiques">18. Priorisation commandes vocales vs boutons physiques</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis avec CarPlay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai accès aux boutons physiques ET aux commandes vocales</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je veux liker un contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux soit:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 3 méthodes sont valides</p>
|
||
<hr />
|
||
<h2 id="19-statistiques-dusage-des-commandes-vocales">19. Statistiques d'usage des commandes vocales</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 utilisateurs avec CarPlay utilisent RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les analytics</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux voir:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="20-feedback-haptique-desactive-pour-commandes-vocales">20. Feedback haptique désactivé pour commandes vocales</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je like un contenu via commande vocale</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'action est enregistrée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune vibration haptique n'est déclenchée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule la confirmation vocale est donnée</p>
|
||
<hr />
|
||
<h2 id="21-badge-visuel-mis-a-jour-apres-commande-vocale">21. Badge visuel mis à jour après commande vocale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je dis "Hey Siri, like ce podcast"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'action est enregistrée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge "♥ Ajouté à vos favoris" s'affiche sur l'écran CarPlay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cœur devient rouge plein dans l'interface
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la mise à jour est visible même sans toucher l'écran</p>
|
||
<hr />
|
||
<h2 id="22-commandes-vocales-avec-contenu-sans-createur">22. Commandes vocales avec contenu sans créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu anonyme (créateur supprimé)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis "Hey Siri, abonne-moi à ce créateur"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Siri répond "Ce créateur n'est plus disponible"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun abonnement n'est enregistré</p>
|
||
<hr />
|
||
<h2 id="23-limitation-temporelle-des-commandes-vocales">23. Limitation temporelle des commandes vocales</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je dis "Hey Siri, like ce podcast"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu change 1 seconde après</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Siri traite la commande 2 secondes plus tard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la commande s'applique au contenu qui était en lecture au moment de la commande
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> non au contenu actuel (système de timestamp)</p>
|
||
<hr />
|
||
<h2 id="24-plan-commandes-vocales-avec-differents-assistants">24. 📋 Plan: Commandes vocales avec différents assistants</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise <assistant></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je dis <commande></p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action <action> est exécutée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la confirmation est <confirmation></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>assistant</th>
|
||
<th>commande</th>
|
||
<th>action</th>
|
||
<th>confirmation</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Siri</td>
|
||
<td>"Like ce podcast"</td>
|
||
<td>Like +2%</td>
|
||
<td>"Ajouté à vos favoris"</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Google Assistant</td>
|
||
<td>"Like ce contenu"</td>
|
||
<td>Like +2%</td>
|
||
<td>"J'ai liké ce contenu"</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Siri</td>
|
||
<td>"Abonne-moi à ce créateur"</td>
|
||
<td>Abonnement +5%</td>
|
||
<td>"Vous êtes abonné"</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Google Assistant</td>
|
||
<td>"Abonne-moi à ce créateur"</td>
|
||
<td>Abonnement +5%</td>
|
||
<td>"Abonnement enregistré"</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Siri</td>
|
||
<td>"Signale ce contenu"</td>
|
||
<td>Signalement</td>
|
||
<td>"J'ai signalé ce contenu"</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Google Assistant</td>
|
||
<td>"Signale ce contenu"</td>
|
||
<td>Signalement</td>
|
||
<td>"Contenu signalé"</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="25-plan-mapping-categories-signalement-vocal">25. 📋 Plan: Mapping catégories signalement vocal</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je dis "signale ce contenu pour <mot_cle>"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> <mot_cle> est reconnu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la catégorie mappée est <categorie></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>mot_cle</th>
|
||
<th>categorie</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>haine</td>
|
||
<td>Haine et violence</td>
|
||
</tr>
|
||
<tr>
|
||
<td>violence</td>
|
||
<td>Haine et violence</td>
|
||
</tr>
|
||
<tr>
|
||
<td>sexuel</td>
|
||
<td>Contenu sexuel</td>
|
||
</tr>
|
||
<tr>
|
||
<td>porno</td>
|
||
<td>Contenu sexuel</td>
|
||
</tr>
|
||
<tr>
|
||
<td>illégal</td>
|
||
<td>Illégalité</td>
|
||
</tr>
|
||
<tr>
|
||
<td>terrorisme</td>
|
||
<td>Illégalité</td>
|
||
</tr>
|
||
<tr>
|
||
<td>copyright</td>
|
||
<td>Droits d'auteur</td>
|
||
</tr>
|
||
<tr>
|
||
<td>droits auteur</td>
|
||
<td>Droits d'auteur</td>
|
||
</tr>
|
||
<tr>
|
||
<td>spam</td>
|
||
<td>Spam</td>
|
||
</tr>
|
||
<tr>
|
||
<td>fake news</td>
|
||
<td>Désinformation</td>
|
||
</tr>
|
||
<tr>
|
||
<td>fausse info</td>
|
||
<td>Désinformation</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="commandes-au-volant-et-interactions-simplifiees">Commandes au volant et interactions simplifiées</h1>
|
||
<blockquote>
|
||
<p><em>En tant que conducteur en sécurité</em>
|
||
<em>Je veux utiliser uniquement les commandes simplifiées au volant</em>
|
||
<em>Afin de naviguer sans distraction et en toute sécurité</em></p>
|
||
</blockquote>
|
||
<p><strong>21 scénarios</strong> (19 standards, 2 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'application est connectée via CarPlay ou Android Auto</p>
|
||
</blockquote>
|
||
<h2 id="1-trois-commandes-disponibles-au-volant-uniquement">1. Trois commandes disponibles au volant uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à 50 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les commandes physiques disponibles</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules 3 actions sont disponibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> aucune commande complexe n'est proposée</p>
|
||
<hr />
|
||
<h2 id="2-commande-suivant-au-volant">2. Commande "Suivant" au volant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu "A"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur le bouton physique "Suivant" au volant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "B" démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune action supplémentaire n'est requise
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'interface ne demande aucune confirmation</p>
|
||
<hr />
|
||
<h2 id="3-commande-precedent-au-volant-respecte-regle-10s">3. Commande "Précédent" au volant respecte règle 10s</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu depuis 5 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent" au volant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reviens au contenu précédent (règle <10s)</p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu depuis 15 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent" au volant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu actuel rejoue depuis le début (règle ≥10s)</p>
|
||
<hr />
|
||
<h2 id="4-commande-playpause-avec-fade-audio">4. Commande "Play/Pause" avec fade audio</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu est en lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Pause" au volant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture se met en pause avec un fade out de 0.3 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la position de lecture est sauvegardée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Play" au volant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture reprend avec un fade in de 0.3 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la reprise se fait à la position exacte</p>
|
||
<hr />
|
||
<h2 id="5-aucune-commande-complexe-supportee">5. Aucune commande complexe supportée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie un appui long sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action n'est pas détectée (non supporté iOS/Android)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie un double-appui sur "Pause"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action n'est pas détectée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les actions simples (clic simple) fonctionnent</p>
|
||
<hr />
|
||
<h2 id="6-compatibilite-100-tous-vehicules">6. Compatibilité 100% tous véhicules</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis une voiture avec commandes basiques
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon véhicule a seulement Suivant/Précédent/Pause</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise RoadWave</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les fonctions essentielles sont accessibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai pas besoin de boutons supplémentaires</p>
|
||
<hr />
|
||
<h2 id="7-feedback-visuel-discret-apres-action">7. Feedback visuel discret après action</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu change</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'interface CarPlay/Android Auto affiche le nouveau titre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune popup ne bloque la vue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le changement est fluide et immédiat</p>
|
||
<hr />
|
||
<h2 id="8-like-automatique-renforce-apres-ecoute-80">8. Like automatique renforcé après écoute ≥80%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu de 5 minutes tagué "Automobile"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 4 minutes 30 secondes (90%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un like automatique renforcé (+2 points) est enregistré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge discret "♥ Ajouté à vos favoris" s'affiche 2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune action manuelle n'est requise</p>
|
||
<hr />
|
||
<h2 id="9-like-automatique-standard-apres-ecoute-30-79">9. Like automatique standard après écoute 30-79%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu de 5 minutes tagué "Voyage"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 2 minutes (40%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un like automatique standard (+1 point) est enregistré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge discret s'affiche brièvement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer à conduire sans interruption</p>
|
||
<hr />
|
||
<h2 id="10-signal-negatif-apres-skip-rapide-10s">10. Signal négatif après skip rapide <10s</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu tagué "Politique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant" après seulement 5 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un signal négatif (-0.5 point) est enregistré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la jauge "Politique" diminue légèrement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun message n'est affiché (transparence)</p>
|
||
<hr />
|
||
<h2 id="11-pas-de-like-si-ecoute-30">11. Pas de like si écoute <30%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu de 10 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 2 minutes (20%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun like n'est enregistré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les jauges ne changent pas
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système considère l'écoute comme neutre</p>
|
||
<hr />
|
||
<h2 id="12-badge-de-feedback-visuel-disparait-apres-2-secondes">12. Badge de feedback visuel disparaît après 2 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je reçois un like automatique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le badge "♥ Ajouté à vos favoris" apparaît</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il reste visible 2 secondes en bas de l'écran
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il disparaît automatiquement sans action
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il ne bloque pas la vue de la route</p>
|
||
<hr />
|
||
<h2 id="13-tracking-du-temps-decoute-precis-cote-client">13. Tracking du temps d'écoute précis côté client</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je démarre la lecture d'un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le player audio iOS/Android enregistre le temps</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le startTime est enregistré à la milliseconde</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'arrête la lecture (Suivant, Pause, ou fin)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la durée exacte écoutée est calculée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le pourcentage (durée / durée_totale * 100) est envoyé à l'API</p>
|
||
<hr />
|
||
<h2 id="14-api-recoit-les-evenements-decoute-pour-calcul">14. API reçoit les événements d'écoute pour calcul</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu de 5 minutes à 80%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'événement est envoyé à l'API</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le backend reçoit:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le backend calcule le like automatique (+2 points)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les jauges sont mises à jour immédiatement (Redis + PostgreSQL)</p>
|
||
<hr />
|
||
<h2 id="15-actions-differentes-selon-arret-du-contenu">15. Actions différentes selon arrêt du contenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action envoyée est "skipped"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu se termine naturellement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action envoyée est "completed"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Pause"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action envoyée est "paused"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le backend traite chaque action différemment</p>
|
||
<hr />
|
||
<h2 id="16-calcul-immediat-cote-backend-sans-delai">16. Calcul immédiat côté backend sans délai</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API reçoit un événement d'écoute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le backend traite l'événement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les jauges sont mises à jour immédiatement (< 100ms)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les nouvelles recommandations utilisent les valeurs actualisées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a aucun batch différé</p>
|
||
<hr />
|
||
<h2 id="17-compatibilite-ios-avec-avplayer">17. Compatibilité iOS avec AVPlayer</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'app iOS utilise AVPlayer</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les commandes physiques sont interceptées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les événements MPRemoteCommandCenter sont capturés:</p>
|
||
<pre><code>| Commande | Événement iOS |
|
||
|---|---|
|
||
| Suivant | nextTrackCommand |
|
||
| Précédent | previousTrackCommand |
|
||
| Play/Pause | playCommand / pauseCommand |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le tracking du temps utilise CMTime</p>
|
||
<hr />
|
||
<h2 id="18-compatibilite-android-avec-mediasession">18. Compatibilité Android avec MediaSession</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'app Android utilise MediaPlayer</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les commandes physiques sont interceptées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les événements MediaSession sont capturés:</p>
|
||
<pre><code>| Commande | Action Android |
|
||
|---|---|
|
||
| Suivant | ACTION_SKIP_TO_NEXT |
|
||
| Précédent | ACTION_SKIP_TO_PREVIOUS |
|
||
| Play/Pause | ACTION_PLAY / ACTION_PAUSE |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le tracking du temps utilise SystemClock.elapsedRealtime()</p>
|
||
<hr />
|
||
<h2 id="19-securite-maximale-pas-de-distraction">19. Sécurité maximale - pas de distraction</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à 80 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise RoadWave avec les commandes au volant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je n'ai jamais besoin de regarder mon téléphone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai jamais besoin de toucher l'écran CarPlay/Android Auto
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les actions sont accessibles via boutons physiques
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les likes sont enregistrés automatiquement</p>
|
||
<hr />
|
||
<h2 id="20-plan-calcul-du-like-automatique-selon-pourcentage">20. 📋 Plan: Calcul du like automatique selon pourcentage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu tagué "Sport"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant <pourcentage>%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le like automatique est <type>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'impact sur la jauge est <points></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>pourcentage</th>
|
||
<th>type</th>
|
||
<th>points</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>aucun</td>
|
||
<td>0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>25</td>
|
||
<td>aucun</td>
|
||
<td>0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>29</td>
|
||
<td>aucun</td>
|
||
<td>0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>30</td>
|
||
<td>standard</td>
|
||
<td>+1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>standard</td>
|
||
<td>+1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>79</td>
|
||
<td>standard</td>
|
||
<td>+1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>80</td>
|
||
<td>renforcé</td>
|
||
<td>+2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>90</td>
|
||
<td>renforcé</td>
|
||
<td>+2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>100</td>
|
||
<td>renforcé</td>
|
||
<td>+2</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="21-plan-signal-negatif-uniquement-si-skip-tres-rapide">21. 📋 Plan: Signal négatif uniquement si skip très rapide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je skip après <secondes> secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signal est <type>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'impact est <points></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>secondes</th>
|
||
<th>type</th>
|
||
<th>points</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>négatif</td>
|
||
<td>-0.5</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>négatif</td>
|
||
<td>-0.5</td>
|
||
</tr>
|
||
<tr>
|
||
<td>9</td>
|
||
<td>négatif</td>
|
||
<td>-0.5</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>neutre</td>
|
||
<td>0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>15</td>
|
||
<td>neutre</td>
|
||
<td>0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>30</td>
|
||
<td>neutre</td>
|
||
<td>0</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="file-dattente-et-commande-suivant">File d'attente et commande "Suivant"</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur en déplacement</em>
|
||
<em>Je veux que l'application pré-calcule intelligemment les prochains contenus</em>
|
||
<em>Afin d'avoir une navigation fluide sans latence</em></p>
|
||
</blockquote>
|
||
<p><strong>20 scénarios</strong> (19 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la géolocalisation est activée</p>
|
||
</blockquote>
|
||
<h2 id="1-pre-calcul-initial-de-5-contenus-en-cache">1. Pré-calcul initial de 5 contenus en cache</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de démarrer l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis situé à Paris (48.8566, 2.3522)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis en mode voiture (vitesse ≥ 5 km/h)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application initialise la lecture</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une file d'attente de 5 contenus est pré-calculée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la file est stockée en cache Redis avec la clé "user:{user_id}:queue"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les métadonnées incluent ma position, le timestamp de calcul et le mode
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cache a un TTL de 15 minutes</p>
|
||
<hr />
|
||
<h2 id="2-commande-suivant-sans-latence">2. Commande "Suivant" sans latence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une file d'attente de 5 contenus est en cache
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute actuellement le contenu "A"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur le bouton "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu suivant démarre immédiatement (< 100ms)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est retiré de la file d'attente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il reste 4 contenus dans la file</p>
|
||
<hr />
|
||
<h2 id="3-recalcul-automatique-apres-deplacement-10km">3. Recalcul automatique après déplacement >10km</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la file a été calculée à Paris (48.8566, 2.3522)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai 5 contenus en cache</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me déplace à Versailles (48.8049, 2.1204) soit 12km</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la file d'attente est invalidée automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une nouvelle file de 5 contenus est recalculée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est basée sur ma nouvelle position</p>
|
||
<hr />
|
||
<h2 id="4-recalcul-automatique-toutes-les-10-minutes">4. Recalcul automatique toutes les 10 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une file a été calculée il y a 10 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma position n'a pas changé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le timer de rafraîchissement expire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une nouvelle file de 5 contenus est recalculée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les anciens contenus non écoutés sont remplacés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les nouveaux contenus publiés depuis sont inclus</p>
|
||
<hr />
|
||
<h2 id="5-recalcul-quand-il-reste-moins-de-3-contenus">5. Recalcul quand il reste moins de 3 contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'il reste 3 contenus dans ma file d'attente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il reste 2 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un recalcul asynchrone est déclenché en arrière-plan
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 3 nouveaux contenus sont ajoutés à la file
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la file contient maintenant 5 contenus</p>
|
||
<hr />
|
||
<h2 id="6-insertion-prioritaire-dun-contenu-geolocalise-en-mode-voiture">6. Insertion prioritaire d'un contenu géolocalisé en mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai une file de 5 contenus pré-calculée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis en mode voiture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je me déplace à 50 km/h vers un point avec contenu géolocalisé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis à 98m du point (ETA = 7 secondes)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification est envoyée (icône + compteur 7→1 + son)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois appuyer sur "Suivant" dans les 7 secondes pour valider</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un décompte de 5 secondes démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 5 secondes, le contenu géolocalisé s'insère et démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il remplace le contenu actuel dans la lecture</p>
|
||
<hr />
|
||
<h2 id="7-contenu-geolocalise-ignore-est-perdu-cooldown-active">7. Contenu géolocalisé ignoré est perdu (cooldown activé)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée est affichée (compteur 7→1)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je ne clique pas sur "Suivant" pendant les 7 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification disparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu géolocalisé est perdu (pas d'insertion dans la file)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un cooldown de 10 minutes est activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune nouvelle notification géolocalisée ne sera envoyée pendant 10 minutes</p>
|
||
<hr />
|
||
<h2 id="8-validation-dune-notification-geolocalisee">8. Validation d'une notification géolocalisée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée est affichée (compteur à 5)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute un podcast</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur bascule à "5" (décompte final)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le podcast actuel continue de jouer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 5 secondes, le contenu géolocalisé démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le podcast est mis en pause et sauvegardé dans l'historique</p>
|
||
<hr />
|
||
<h2 id="9-invalidation-immediate-apres-modification-des-preferences">9. Invalidation immédiate après modification des préférences</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai une file de 5 contenus en cache
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse GPS est de 5 km/h (piéton)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie mes curseurs de préférences (géo/découverte/politique)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la file d'attente est invalidée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une nouvelle file est recalculée avec les nouvelles préférences
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les anciens contenus en cache sont supprimés</p>
|
||
<hr />
|
||
<h2 id="10-blocage-modification-preferences-en-conduite-10-kmh">10. Blocage modification préférences en conduite (>10 km/h)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma vitesse GPS est de 50 km/h (en voiture)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder aux réglages de préférences</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'interface affiche "Paramètres verrouillés en conduite"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas modifier les curseurs géo/découverte/politique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message "Arrêtez-vous pour modifier vos préférences" s'affiche</p>
|
||
<hr />
|
||
<h2 id="11-invalidation-lors-du-demarrage-dun-live-suivi">11. Invalidation lors du démarrage d'un live suivi</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "RadioVoyage"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai une file de 5 contenus en cache
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis dans la zone géographique du créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur "RadioVoyage" démarre une radio live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu live s'insère en tête de la file d'attente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la file d'attente est recalculée</p>
|
||
<hr />
|
||
<h2 id="12-metadonnees-de-cache-redis">12. Métadonnées de cache Redis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une file d'attente est calculée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> elle est stockée dans Redis</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la clé est "user:{user_id}:queue"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les métadonnées incluent:</p>
|
||
<pre><code>| champ | valeur |
|
||
|---|---|
|
||
| last_lat | 48.8566 |
|
||
| last_lon | 2.3522 |
|
||
| computed_at | 2026-01-21T10:30:00Z |
|
||
| mode | voiture |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le TTL est de 15 minutes (900 secondes)</p>
|
||
<hr />
|
||
<h2 id="13-contenu-geolocalise-remplace-le-contenu-actuel-pas-dinsertion-en-file">13. Contenu géolocalisé remplace le contenu actuel (pas d'insertion en file)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute le contenu C2 de ma file [C1, C2, C3, C4, C5]
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une notification géolocalisée "Tour Eiffel" est déclenchée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je valide la notification
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le décompte de 5s se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "Tour Eiffel" remplace C2 et démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> C2 est sauvegardé dans l'historique de navigation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la file reste [C3, C4, C5] (pas de contenu retiré)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> quand "Tour Eiffel" se termine, C3 démarre</p>
|
||
<hr />
|
||
<h2 id="14-invalidation-apres-deplacement-exactement-10km">14. Invalidation après déplacement exactement 10km</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la file a été calculée à une position donnée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me déplace d'exactement 10.0 km</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la file d'attente n'est PAS invalidée (seuil strict >10km)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus en cache restent valides</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me déplace de 10.1 km supplémentaires (total 10.1km)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la file d'attente est invalidée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une nouvelle file est calculée</p>
|
||
<hr />
|
||
<h2 id="15-rafraichissement-exactement-apres-10-minutes">15. Rafraîchissement exactement après 10 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une file a été calculée à 10:00:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'heure actuelle est 10:10:00</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le timer de rafraîchissement expire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une nouvelle file de 5 contenus est recalculée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le timestamp "computed_at" est mis à jour</p>
|
||
<hr />
|
||
<h2 id="16-recalcul-asynchrone-non-bloquant">16. Recalcul asynchrone non-bloquant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'il reste 2 contenus dans la file
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le recalcul asynchrone démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture du contenu actuel n'est pas interrompue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le recalcul se fait en arrière-plan
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les nouveaux contenus sont ajoutés dès disponibles (< 500ms)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur ne perçoit aucune latence</p>
|
||
<hr />
|
||
<h2 id="17-notification-basee-sur-eta-pas-distance-fixe">17. Notification basée sur ETA (pas distance fixe)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu géolocalisé existe à un point GPS
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je roule à 130 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis à 252m du point (ETA = 7 secondes)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification est envoyée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis à 300m du point (ETA = 8 secondes)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification n'est envoyée (ETA >7s)</p>
|
||
<hr />
|
||
<h2 id="18-plan-differentes-distances-de-deplacement-et-invalidation">18. 📋 Plan: Différentes distances de déplacement et invalidation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une file a été calculée à une position donnée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me déplace de <distance> km</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la file est <action></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>distance</th>
|
||
<th>action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>conservée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>9.9</td>
|
||
<td>conservée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10.0</td>
|
||
<td>conservée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10.1</td>
|
||
<td>invalidée et recalculée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>15</td>
|
||
<td>invalidée et recalculée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>invalidée et recalculée</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="19-quota-de-6-contenus-geolocalises-par-heure">19. Quota de 6 contenus géolocalisés par heure</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai validé 6 notifications géolocalisées dans la dernière heure</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un 7ème contenu géolocalisé est détecté (ETA 7s)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification n'est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le quota horaire est respecté</p>
|
||
<hr />
|
||
<h2 id="20-mode-pieton-pas-de-notification-avec-compteur-7s">20. Mode piéton - pas de notification avec compteur 7s</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en mode piéton (vitesse <5 km/h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un audio-guide géolocalisé existe à 150m</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je passe dans le rayon de 200m</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification push système est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun compteur 7s n'est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux ouvrir l'app en tapant sur la notification</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="lecture-en-boucle-et-enchainement-automatique">Lecture en boucle et enchaînement automatique</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur</em>
|
||
<em>Je veux que les contenus s'enchaînent automatiquement avec un délai paramétrable</em>
|
||
<em>Afin d'avoir une expérience fluide sans interruption</em></p>
|
||
</blockquote>
|
||
<p><strong>27 scénarios</strong> (24 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-passage-automatique-apres-2-secondes-mode-standard">1. Passage automatique après 2 secondes (mode standard)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu "A" en mode standard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la lecture se termine naturellement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un timer de 2 secondes démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un overlay s'affiche: "Contenu suivant dans 2s..."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une barre de décompte visuelle s'affiche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le timer atteint 0</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "B" démarre automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'overlay disparaît</p>
|
||
<hr />
|
||
<h2 id="2-passage-automatique-apres-1-seconde-mode-kids">2. Passage automatique après 1 seconde (mode Kids)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en mode Kids
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute un contenu pour enfants</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la lecture se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un timer de 1 seconde démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message "Contenu suivant dans 1s..." s'affiche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le timer expire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu suivant démarre automatiquement</p>
|
||
<hr />
|
||
<h2 id="3-passage-immediat-apres-une-radio-live-0-seconde">3. Passage immédiat après une radio live (0 seconde)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute une radio live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur arrête la diffusion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le passage au contenu suivant est immédiat (0s de délai)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun overlay de décompte n'est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transition est fluide</p>
|
||
<hr />
|
||
<h2 id="4-annulation-du-passage-automatique">4. Annulation du passage automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu se termine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le timer de 2 secondes démarre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Rester sur ce contenu" pendant le décompte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le timer est annulé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu actuel reste en pause à la fin
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu suivant n'est pas lancé</p>
|
||
<hr />
|
||
<h2 id="5-insertion-de-publicite-pendant-le-delai-de-transition">5. Insertion de publicité pendant le délai de transition</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté 4 contenus sans publicité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le 5ème contenu se termine</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le délai de 2 secondes démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une publicité s'insère dans la file d'attente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message devient "Publicité (15s)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publicité démarre après les 2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle ne coupe jamais un contenu en cours</p>
|
||
<hr />
|
||
<h2 id="6-frequence-de-publicite-parametrable-admin">6. Fréquence de publicité paramétrable admin</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la fréquence pub est configurée à "1/5 contenus"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 10 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 2 publicités sont insérées (après les contenus 5 et 10)</p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'admin change la fréquence à "1/3 contenus"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 9 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 3 publicités sont insérées (après les contenus 3, 6 et 9)</p>
|
||
<hr />
|
||
<h2 id="7-publicite-skippable-apres-5-secondes-par-defaut">7. Publicité skippable après 5 secondes par défaut</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30 secondes démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le délai minimal de visionnage est configuré à 5 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 3 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" n'est pas encore visible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'atteins 5 secondes d'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" apparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer pour passer au contenu suivant</p>
|
||
<hr />
|
||
<h2 id="8-delai-minimal-de-publicite-parametrable-admin">8. Délai minimal de publicité paramétrable admin</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'admin a configuré le délai à 10 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 9 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" n'est pas visible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'atteins 10 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" apparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux skipper la publicité</p>
|
||
<hr />
|
||
<h2 id="9-like-et-abonnement-autorises-sur-une-publicite">9. Like et abonnement autorisés sur une publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité est en lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton cœur (véhicule arrêté)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité reçoit un like (+2% jauges tags pub)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "S'abonner" au créateur de la pub</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis abonné (+5% jauges tags créateur)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur de pub bénéficie de l'engagement</p>
|
||
<hr />
|
||
<h2 id="10-metriques-dengagement-publicite-trackees">10. Métriques d'engagement publicité trackées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30s est diffusée à 100 auditeurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 40 auditeurs écoutent entièrement (30s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 50 auditeurs skippent après 10s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 10 auditeurs skippent avant 5s</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métriques sont:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-message-aucun-contenu-disponible-si-file-vide">11. Message "Aucun contenu disponible" si file vide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la file d'attente est vide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun contenu n'est disponible dans ma zone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu actuel se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message s'affiche: "Aucun contenu disponible dans cette zone"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une proposition apparaît: "Élargir la zone de recherche ?"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Élargir" est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture se met en pause automatiquement</p>
|
||
<hr />
|
||
<h2 id="12-elargissement-automatique-de-la-zone-de-recherche">12. Élargissement automatique de la zone de recherche</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le message "Aucun contenu disponible" s'affiche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Élargir la zone"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'algorithme relance une recherche avec rayon +50km
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification "Recherche élargie à 50km" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la file d'attente est recalculée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture reprend automatiquement</p>
|
||
<hr />
|
||
<h2 id="13-refus-delargissement-laisse-en-pause">13. Refus d'élargissement laisse en pause</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le message "Aucun contenu disponible" s'affiche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Annuler"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture reste en pause
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'interface affiche "En attente de contenu"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux manuellement naviguer ou chercher du contenu</p>
|
||
<hr />
|
||
<h2 id="14-retry-avec-backoff-exponentiel-en-cas-dechec-reseau">14. Retry avec backoff exponentiel en cas d'échec réseau</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu suivant échoue au chargement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la première tentative échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système retente après 1 seconde (backoff 1s)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la 2ème tentative échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système retente après 2 secondes (backoff 2s)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la 3ème tentative échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système retente après 4 secondes (backoff 4s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 3 échecs totaux, le système bascule en mode offline</p>
|
||
<hr />
|
||
<h2 id="15-basculement-mode-offline-apres-3-echecs-reseau">15. Basculement mode offline après 3 échecs réseau</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai eu 3 échecs de chargement consécutifs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 3ème échec se produit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message "Connexion instable, basculement mode offline" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture continue avec les contenus téléchargés uniquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus en ligne sont temporairement désactivés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la connexion revient</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode en ligne est automatiquement rétabli</p>
|
||
<hr />
|
||
<h2 id="16-overlay-de-decompte-avec-barre-visuelle">16. Overlay de décompte avec barre visuelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu se termine</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le timer de 2 secondes démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un overlay semi-transparent s'affiche en bas de l'écran
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le texte "Contenu suivant dans 2s..." est visible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une barre de progression décroît de 100% à 0% en 2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la couleur de la barre passe de vert à orange
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'overlay disparaît automatiquement après le décompte</p>
|
||
<hr />
|
||
<h2 id="17-bouton-rester-sur-ce-contenu-pendant-decompte">17. Bouton "Rester sur ce contenu" pendant décompte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le décompte de 2 secondes est actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'overlay s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un bouton "Rester sur ce contenu" est visible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est cliquable pendant les 2 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique dessus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le timer est annulé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'overlay disparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu actuel reste affiché en pause</p>
|
||
<hr />
|
||
<h2 id="18-pas-dinterruption-dun-contenu-en-cours">18. Pas d'interruption d'un contenu en cours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu de 10 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis à 5 minutes de lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une publicité devrait s'insérer (fréquence 1/5)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité n'interrompt jamais le contenu en cours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle attend la fin du contenu actuel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle s'insère pendant le délai de transition (2s)</p>
|
||
<hr />
|
||
<h2 id="19-publicites-uniquement-pour-utilisateurs-gratuits">19. Publicités uniquement pour utilisateurs gratuits</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 5 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une publicité est insérée après le 5ème contenu</p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je passe en compte Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 100 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune publicité n'est insérée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'enchaînement est direct (2s de transition seulement)</p>
|
||
<hr />
|
||
<h2 id="20-message-clair-pour-lutilisateur-lors-de-la-publicite">20. Message clair pour l'utilisateur lors de la publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité va démarrer</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le délai de transition démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le message affiché est: "Publicité (15s)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la durée totale de la pub est indiquée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur sait qu'il s'agit d'une pub
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transparence est maximale</p>
|
||
<hr />
|
||
<h2 id="21-transition-fluide-entre-contenus-sans-coupure">21. Transition fluide entre contenus sans coupure</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu se termine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le suivant est pré-chargé en cache</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le timer de 2s expire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la transition audio utilise un crossfade de 0.3s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a aucun blanc ou coupure
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience est fluide</p>
|
||
<hr />
|
||
<h2 id="22-gestion-des-erreurs-de-chargement-avec-retry">22. Gestion des erreurs de chargement avec retry</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu suivant échoue au chargement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la 1ère tentative échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification "Chargement..." s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système retente automatiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la 2ème tentative réussit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture démarre normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune action utilisateur n'est requise</p>
|
||
<hr />
|
||
<h2 id="23-mode-offline-apres-echecs-multiples">23. Mode offline après échecs multiples</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 50 contenus téléchargés en mode offline
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai eu 3 échecs réseau consécutifs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le mode offline s'active</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus téléchargés sont disponibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge "Mode offline" s'affiche en haut de l'écran
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture continue sans interruption</p>
|
||
<hr />
|
||
<h2 id="24-compteur-de-contenus-avant-prochaine-publicite">24. Compteur de contenus avant prochaine publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la fréquence pub est 1/5 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai écouté 3 contenus depuis la dernière pub</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'interface</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un indicateur discret affiche "2 contenus avant pub"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur sait quand attendre la prochaine publicité</p>
|
||
<hr />
|
||
<h2 id="25-plan-delai-de-transition-selon-mode">25. 📋 Plan: Délai de transition selon mode</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en mode <mode></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un contenu se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le délai de transition est <delai> secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message affiché est <message></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>mode</th>
|
||
<th>delai</th>
|
||
<th>message</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Standard</td>
|
||
<td>2</td>
|
||
<td>"Contenu suivant dans 2s..."</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Kids</td>
|
||
<td>1</td>
|
||
<td>"Contenu suivant dans 1s..."</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Live</td>
|
||
<td>0</td>
|
||
<td>(aucun message)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="26-plan-frequence-dinsertion-des-publicites">26. 📋 Plan: Fréquence d'insertion des publicités</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la fréquence pub est configurée à <frequence></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute <contenus> contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> <pubs> publicités sont insérées</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>frequence</th>
|
||
<th>contenus</th>
|
||
<th>pubs</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1/3</td>
|
||
<td>6</td>
|
||
<td>2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/3</td>
|
||
<td>9</td>
|
||
<td>3</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/5</td>
|
||
<td>10</td>
|
||
<td>2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/5</td>
|
||
<td>15</td>
|
||
<td>3</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/7</td>
|
||
<td>14</td>
|
||
<td>2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/7</td>
|
||
<td>21</td>
|
||
<td>3</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="27-plan-backoff-exponentiel-retry">27. 📋 Plan: Backoff exponentiel retry</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le chargement échoue</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis à la tentative <tentative></p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le délai de retry est <delai> secondes</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>tentative</th>
|
||
<th>delai</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>1</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>4</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="partage-de-contenu">Partage de contenu</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur de RoadWave</em>
|
||
<em>Je veux pouvoir partager du contenu audio</em>
|
||
<em>Afin de faire découvrir l'application à d'autres personnes</em></p>
|
||
</blockquote>
|
||
<p><strong>22 scénarios</strong> (20 standards, 2 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur "jean@example.com" est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-bouton-partager-disponible-dans-le-player-en-lecture">1. Bouton partager disponible dans le player en lecture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu "Balade à Paris" est en cours de lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur consulte les contrôles du player</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Partager" ⬆️ est visible</p>
|
||
<hr />
|
||
<h2 id="2-bouton-partager-disponible-sur-la-page-profil-createur">2. Bouton partager disponible sur la page profil créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur consulte le profil de "@paris_stories"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur consulte un contenu dans la liste</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Partager" est disponible pour chaque contenu</p>
|
||
<hr />
|
||
<h2 id="3-bouton-partager-dans-la-liste-de-recherche">3. Bouton partager dans la liste de recherche</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur effectue une recherche "voyage paris"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur ouvre le menu contextuel d'un résultat</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'option "Partager" est disponible</p>
|
||
<hr />
|
||
<h2 id="4-bouton-partager-dans-lhistorique-personnel">4. Bouton partager dans l'historique personnel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur consulte son historique d'écoute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur sélectionne un contenu de l'historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Partager" est accessible</p>
|
||
<hr />
|
||
<h2 id="5-plan-menu-de-partage-avec-options-multiples">5. 📋 Plan: Menu de partage avec options multiples</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu "<contenu>" est disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur le bouton "Partager"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le menu natif OS s'ouvre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les options suivantes sont disponibles:</p>
|
||
<pre><code>| option |
|
||
|---|
|
||
| Copier le lien |
|
||
| WhatsApp |
|
||
| Email |
|
||
| SMS |
|
||
| Plus... |
|
||
</code></pre>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>contenu</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Balade à Paris</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Secrets de Montmartre</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="6-generation-du-lien-de-partage">6. Génération du lien de partage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu avec l'ID "content_12345"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur copie le lien de partage</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le lien généré est "https://roadwave.fr/share/c/content_12345"</p>
|
||
<hr />
|
||
<h2 id="7-ouverture-du-lien-partage-avec-lapplication-installee-deep-link">7. Ouverture du lien partagé avec l'application installée (Deep link)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est installée sur l'appareil
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur le lien</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'application RoadWave s'ouvre automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu "content_12345" commence à jouer</p>
|
||
<hr />
|
||
<h2 id="8-ouverture-du-lien-partage-sans-lapplication-installee-web-player">8. Ouverture du lien partagé sans l'application installée (Web player)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave n'est pas installée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un lien "https://roadwave.fr/share/c/content_12345" est partagé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur le lien</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une page web responsive s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le web player HTML5 est visible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les boutons de téléchargement App Store et Google Play sont affichés</p>
|
||
<hr />
|
||
<h2 id="9-contenu-de-la-page-web-de-partage">9. Contenu de la page web de partage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu public avec les métadonnées suivantes:</p>
|
||
<pre><code>| champ | valeur |
|
||
|---|---|
|
||
| titre | Balade à Paris |
|
||
| créateur | @paris_stories |
|
||
| durée | 12 min |
|
||
| écoutes | 2300 |
|
||
| localisation | Paris 5e |
|
||
| type_geo | Ancré |
|
||
| tags | Voyage, Histoire |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page de partage est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la page contient:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="10-metadonnees-open-graph-pour-partage-social">10. Métadonnées Open Graph pour partage social</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu "Balade à Paris" par "@paris_stories"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page de partage est générée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métadonnées Open Graph incluent:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> l'aperçu s'affiche correctement sur WhatsApp
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'aperçu s'affiche correctement sur Facebook
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'aperçu s'affiche correctement sur Twitter</p>
|
||
<hr />
|
||
<h2 id="11-plan-deep-linking-par-plateforme">11. 📋 Plan: Deep linking par plateforme</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est installée sur <plateforme>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un lien de partage est ouvert</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'application s'ouvre via <mécanisme></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>plateforme</th>
|
||
<th>mécanisme</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>iOS</td>
|
||
<td>Universal Links</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Android</td>
|
||
<td>App Links</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="12-fallback-url-scheme-pour-deep-linking">12. Fallback URL scheme pour deep linking</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les App Links ne fonctionnent pas</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système tente d'ouvrir le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'URL scheme "roadwave://content/content_12345" est utilisé</p>
|
||
<hr />
|
||
<h2 id="13-badge-premium-visible-sur-le-lien-partage">13. Badge Premium visible sur le lien partagé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu Premium "Visite VIP Louvre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur non-premium clique sur le lien partagé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la page web affiche le badge "👑 Contenu Premium"</p>
|
||
<hr />
|
||
<h2 id="14-preview-30-secondes-dun-contenu-premium-partage">14. Preview 30 secondes d'un contenu Premium partagé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu Premium "Visite VIP Louvre" de 15 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur non-premium ouvre le lien partagé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le player démarre automatiquement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio joue pendant 30 secondes exactement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un fade out de 2 secondes est appliqué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un overlay "Contenu réservé Premium" s'affiche après 32 secondes</p>
|
||
<hr />
|
||
<h2 id="15-contenu-de-loverlay-paywall-premium">15. Contenu de l'overlay paywall Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu Premium a atteint la limite de 30 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'overlay paywall s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le texte suivant est visible:</p>
|
||
<hr />
|
||
<h2 id="16-actions-disponibles-sur-loverlay-premium">16. Actions disponibles sur l'overlay Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'overlay paywall Premium est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur consulte les options</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les actions suivantes sont disponibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-relecture-illimitee-du-preview-premium">17. Relecture illimitée du preview Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu Premium partagé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur a écouté les 30 premières secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "Rejouer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 30 premières secondes sont rejouées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette action est possible de manière illimitée</p>
|
||
<hr />
|
||
<h2 id="18-tracking-des-partages-premium">18. Tracking des partages Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur "@guide_louvre" avec un contenu Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> son contenu est partagé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métriques suivantes sont enregistrées:</p>
|
||
<pre><code>| métrique | valeur |
|
||
|---|---|
|
||
| Partages Premium | +1 |
|
||
| Ouvertures lien | compteur |
|
||
| Conversions Premium | si souscription |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="19-remuneration-createur-sur-conversion-premium-via-partage">19. Rémunération créateur sur conversion Premium via partage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu Premium partagé par "@guide_louvre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur s'abonne via le lien partagé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le créateur reçoit 70% des revenus de cet abonnement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la conversion est trackée dans son dashboard</p>
|
||
<hr />
|
||
<h2 id="20-partage-dun-contenu-supprime">20. Partage d'un contenu supprimé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un lien de partage "https://roadwave.fr/share/c/deleted_content" est ouvert
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu n'existe plus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page web se charge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message "Ce contenu n'est plus disponible" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les boutons de téléchargement de l'app sont affichés</p>
|
||
<hr />
|
||
<h2 id="21-partage-dun-contenu-en-attente-de-moderation">21. Partage d'un contenu en attente de modération</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu en cours de validation modération</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un lien de partage est ouvert</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le message "Ce contenu est en cours de validation" s'affiche</p>
|
||
<hr />
|
||
<h2 id="22-generation-du-lien-hors-connexion">22. Génération du lien hors connexion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur n'a pas de connexion réseau</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur tente de partager un contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le lien est copié dans le presse-papiers
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message "Lien copié (nécessite connexion pour ouvrir)" s'affiche</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="avantages-premium">Avantages Premium</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'abonné Premium</em>
|
||
<em>Je veux bénéficier d'avantages exclusifs</em>
|
||
<em>Afin de profiter d'une expérience audio améliorée sans publicité</em></p>
|
||
</blockquote>
|
||
<p><strong>37 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté à l'application RoadWave</p>
|
||
</blockquote>
|
||
<h2 id="1-utilisateur-gratuit-voit-1-publicite-tous-les-5-contenus">1. Utilisateur gratuit voit 1 publicité tous les 5 contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute ma file de contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une publicité tous les 5 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publicité dure 30 secondes en moyenne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas la skip</p>
|
||
<hr />
|
||
<h2 id="2-utilisateur-premium-ne-voit-aucune-publicite">2. Utilisateur Premium ne voit aucune publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute mes contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune publicité n'est diffusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je passe directement d'un contenu à l'autre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience d'écoute est fluide et ininterrompue</p>
|
||
<hr />
|
||
<h2 id="3-badge-0-publicite-sur-page-premium">3. Badge "0 publicité" sur page Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page des avantages Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lis la liste des avantages</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois en premier:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> c'est l'argument principal mis en avant</p>
|
||
<hr />
|
||
<h2 id="4-utilisateur-gratuit-voit-contenus-premium-bloques">4. Utilisateur gratuit voit contenus Premium bloqués</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les contenus d'un créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les contenus marqués Premium avec badge 👑
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je ne peux pas les lire (overlay bloquant)</p>
|
||
<hr />
|
||
<h2 id="5-utilisateur-premium-accede-a-tous-les-contenus-exclusifs">5. Utilisateur Premium accède à tous les contenus exclusifs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les contenus d'un créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les contenus Premium sont accessibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux les lire sans restriction
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> j'ai accès à 100% du catalogue (gratuit + Premium)</p>
|
||
<hr />
|
||
<h2 id="6-nombre-de-contenus-premium-disponibles">6. Nombre de contenus Premium disponibles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois combien de contenus Premium sont disponibles sur la plateforme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> par exemple: "8,547 contenus Premium exclusifs disponibles"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela justifie la valeur de l'abonnement</p>
|
||
<hr />
|
||
<h2 id="7-utilisateur-gratuit-ecoute-en-48-kbps-opus">7. Utilisateur gratuit écoute en 48 kbps Opus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance un contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio est streamé en 48 kbps Opus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela consomme environ 20 MB/heure
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité est très correcte pour de la voix</p>
|
||
<hr />
|
||
<h2 id="8-utilisateur-premium-ecoute-en-64-kbps-opus">8. Utilisateur Premium écoute en 64 kbps Opus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance un contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio est streamé en 64 kbps Opus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela consomme environ 30 MB/heure
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité est excellente (détails audio supérieurs)</p>
|
||
<hr />
|
||
<h2 id="9-comparaison-qualite-48-kbps-vs-64-kbps">9. Comparaison qualité 48 kbps vs 64 kbps</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lis la section qualité audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'explication:</p>
|
||
<hr />
|
||
<h2 id="10-justification-48-kbps-suffisant-pour-gratuit">10. Justification 48 kbps suffisant pour gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu RoadWave est principalement de la voix</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la qualité est fixée à 48 kbps pour gratuit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> c'est largement suffisant pour comprendre clairement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> équivalent à la qualité radio FM
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs gratuits ne sont pas frustrés</p>
|
||
<hr />
|
||
<h2 id="11-justification-64-kbps-avantage-tangible-premium">11. Justification 64 kbps avantage tangible Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les audiophiles et créateurs audio sont exigeants</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la qualité Premium est à 64 kbps</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la différence est perceptible à l'oreille
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les ambiances, musiques de fond, nuances de voix sont mieux rendues
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela justifie l'abonnement Premium</p>
|
||
<hr />
|
||
<h2 id="12-switch-automatique-qualite-selon-abonnement">12. Switch automatique qualité selon abonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis gratuit et j'écoute en 48 kbps</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je souscris à Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> dès le contenu suivant, je passe automatiquement en 64 kbps
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux entendre la différence de qualité immédiatement</p>
|
||
<hr />
|
||
<h2 id="13-consommation-data-premium-vs-gratuit">13. Consommation data Premium vs Gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je roule 1 heure par jour</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule la consommation mensuelle</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> en gratuit: 20 MB/h × 1h × 22 jours = 440 MB/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> en Premium: 30 MB/h × 1h × 22 jours = 660 MB/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la différence est de 220 MB/mois (acceptable pour 4G/5G illimitée)</p>
|
||
<hr />
|
||
<h2 id="14-utilisateur-gratuit-limite-a-50-contenus-telecharges">14. Utilisateur gratuit limité à 50 contenus téléchargés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède au mode offline</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux télécharger jusqu'à 50 contenus maximum
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si j'essaie de télécharger un 51ème, je vois:</p>
|
||
<hr />
|
||
<h2 id="15-utilisateur-premium-telechargements-illimites">15. Utilisateur Premium téléchargements illimités</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède au mode offline</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux télécharger autant de contenus que je veux
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la seule limite est l'espace de stockage de mon device
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> par exemple 500 contenus × 10 MB = 5 GB</p>
|
||
<hr />
|
||
<h2 id="16-justification-limite-50-contenus-gratuit">16. Justification limite 50 contenus gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 50 contenus de 10 minutes = ~8 heures d'écoute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur gratuit prépare un road trip</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 8 heures couvrent largement une journée de trajet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela permet un usage offline raisonnable sans abuser</p>
|
||
<hr />
|
||
<h2 id="17-justification-illimite-premium-pour-longs-road-trips">17. Justification illimité Premium pour longs road trips</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un road trip de plusieurs jours nécessite 20-50h de contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur Premium télécharge 200 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut partir serein sans connexion internet pendant 1 semaine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela justifie pleinement l'abonnement Premium</p>
|
||
<hr />
|
||
<h2 id="18-affichage-compteur-telechargements-gratuit">18. Affichage compteur téléchargements gratuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis gratuit et j'ai téléchargé 37 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à la page Téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="19-pas-de-compteur-pour-premium">19. Pas de compteur pour Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis Premium et j'ai téléchargé 187 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à la page Téléchargements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois simplement:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune limite n'est affichée</p>
|
||
<hr />
|
||
<h2 id="20-utilisateur-gratuit-historique-limite-a-100-derniers">20. Utilisateur gratuit historique limité à 100 derniers</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mon historique d'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les 100 derniers contenus écoutés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus plus anciens ne sont pas affichés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois un message "Historique limité à 100 contenus. Passez Premium pour un historique illimité."</p>
|
||
<hr />
|
||
<h2 id="21-utilisateur-premium-historique-illimite">21. Utilisateur Premium historique illimité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mon historique d'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois tous les contenus que j'ai écoutés depuis mon inscription
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux scroller jusqu'au premier contenu jamais écouté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique est complet et permanent</p>
|
||
<hr />
|
||
<h2 id="22-recherche-dans-historique-premium">22. Recherche dans historique Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis Premium et j'ai 2 000 contenus dans mon historique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je recherche "Tesla" dans mon historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les contenus écoutés contenant "Tesla" sont affichés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux retrouver facilement un contenu écouté il y a 6 mois</p>
|
||
<hr />
|
||
<h2 id="23-justification-limite-100-gratuit-suffisante">23. Justification limite 100 gratuit suffisante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 contenus de 10 min = ~16 heures d'écoute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur gratuit écoute 1h/jour</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique couvre les 16 derniers jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela suffit pour retrouver un contenu récent</p>
|
||
<hr />
|
||
<h2 id="24-justification-illimite-premium-pour-power-users">24. Justification illimité Premium pour power users</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un power user écoute 3h/jour depuis 2 ans</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il veut retrouver un contenu spécifique écouté il y a 1 an</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique illimité Premium lui permet de retrouver ce contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela apporte une vraie valeur ajoutée</p>
|
||
<hr />
|
||
<h2 id="25-export-historique-complet-premium-uniquement">25. Export historique complet (Premium uniquement)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande l'export de mes données</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique complet est inclus dans l'export:</p>
|
||
<hr />
|
||
<h2 id="26-affichage-tableau-comparatif-gratuit-vs-premium">26. Affichage tableau comparatif Gratuit vs Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vois le tableau comparatif</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il affiche:</p>
|
||
<hr />
|
||
<h2 id="27-justification-0-pub-argument-principal">27. Justification 0 pub = argument principal</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30s tous les 5 contenus = 6 min/h de pub</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur écoute 1h/jour</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il subit 180 min de pub/mois (3 heures !)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> payer 4.99€ pour éviter 3h de pub/mois est très rentable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> c'est l'argument de conversion n°1</p>
|
||
<hr />
|
||
<h2 id="28-justification-qualite-audio-avantage-tangible">28. Justification qualité audio avantage tangible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la différence 48 kbps → 64 kbps est audible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un audiophile compare les deux</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il entend clairement la différence sur un bon système audio voiture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela justifie l'abonnement pour les exigeants</p>
|
||
<hr />
|
||
<h2 id="29-justification-offline-illimite-pour-road-trips">29. Justification offline illimité pour road trips</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un road trip de 2 semaines nécessite 50-100h de contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur Premium télécharge 300 contenus avant de partir</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut partir en zone sans réseau sereinement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela apporte une vraie valeur pratique</p>
|
||
<hr />
|
||
<h2 id="30-justification-pas-dover-engineering">30. Justification pas d'over-engineering</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave se concentre sur l'essentiel</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les avantages Premium sont définis</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il n'y a pas de:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela réduit la complexité et le coût de développement</p>
|
||
<hr />
|
||
<h2 id="31-cta-premium-apres-5eme-publicite">31. CTA Premium après 5ème publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis gratuit et je viens d'entendre ma 5ème pub</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la publicité se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un message:</p>
|
||
<hr />
|
||
<h2 id="32-cta-premium-quand-limite-50-telechargements-atteinte">32. CTA Premium quand limite 50 téléchargements atteinte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis gratuit et j'ai atteint 50 téléchargements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de télécharger un 51ème contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une popup:</p>
|
||
<hr />
|
||
<h2 id="33-cta-premium-quand-contenu-exclusif-bloque">33. CTA Premium quand contenu exclusif bloqué</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis gratuit et je clique sur un contenu Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'overlay bloquant apparaît</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="34-statistiques-conversion-quel-avantage-convertit-le-mieux">34. Statistiques conversion - Quel avantage convertit le mieux ?</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin consulte les statistiques de conversion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il analyse les sources de conversion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela aide à optimiser le placement des CTA</p>
|
||
<hr />
|
||
<h2 id="35-ab-test-message-cta">35. A/B test message CTA</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave veut optimiser les conversions</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un A/B test est lancé sur le CTA après pub</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> groupe A voit "Marre des pubs ?" (focus négatif)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> groupe B voit "Profitez de 0 publicité" (focus positif)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le taux de conversion est mesuré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message le plus performant est déployé</p>
|
||
<hr />
|
||
<h2 id="36-notification-premium-apres-30-jours-dutilisation-gratuite">36. Notification Premium après 30 jours d'utilisation gratuite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis utilisateur gratuit depuis 30 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute régulièrement (15h cumulées)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 30ème jour arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification:</p>
|
||
<hr />
|
||
<h2 id="37-trial-gratuit-refuse-mais-onboarding-ameliore">37. Trial gratuit refusé mais onboarding amélioré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'il n'y a pas de trial gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un nouvel utilisateur s'inscrit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un onboarding explique clairement les avantages Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut comparer gratuit vs Premium dès le premier lancement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela l'aide à décider rapidement s'il veut payer</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="gestion-abonnement-premium">Gestion abonnement Premium</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux gérer facilement mon abonnement Premium</em>
|
||
<em>Afin de souscrire, renouveler ou annuler en toute transparence</em></p>
|
||
</blockquote>
|
||
<p><strong>41 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté à l'application RoadWave</p>
|
||
</blockquote>
|
||
<h2 id="1-souscription-via-web-desktopmobile-avec-mangopay">1. Souscription via Web (desktop/mobile) avec Mangopay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page Premium sur le site web</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "S'abonner - Mensuel 4.99€"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers le formulaire de paiement Mangopay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je saisis mes informations de carte bancaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le paiement de 4.99€ est prélevé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la commission Mangopay est de 1.8% + 0.18€ = 0.27€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave reçoit 4.72€ net</p>
|
||
<hr />
|
||
<h2 id="2-calcul-commission-mangopay">2. Calcul commission Mangopay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur paie 4.99€ via Mangopay</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la commission est calculée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la commission est : 4.99€ × 1.8% + 0.18€ = 0.09€ + 0.18€ = 0.27€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave reçoit : 4.99€ - 0.27€ = 4.72€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la commission représente 5.4% du prix</p>
|
||
<hr />
|
||
<h2 id="3-souscription-via-ios-app-avec-apple-iap">3. Souscription via iOS App avec Apple IAP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise l'app iOS</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "S'abonner - Mensuel 5.99€"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers l'interface Apple In-App Purchase
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le prix affiché est 5.99€ (majoré de 20%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le paiement est effectué via mon compte Apple
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Apple prend 30% de commission = 1.80€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave reçoit 4.19€ net</p>
|
||
<hr />
|
||
<h2 id="4-souscription-via-android-app-avec-google-play-billing">4. Souscription via Android App avec Google Play Billing</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise l'app Android</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "S'abonner - Mensuel 5.99€"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je suis redirigé vers l'interface Google Play Billing
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le prix affiché est 5.99€ (majoré de 20%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le paiement est effectué via mon compte Google
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Google prend 30% de commission = 1.80€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave reçoit 4.19€ net</p>
|
||
<hr />
|
||
<h2 id="5-majoration-20-sur-mobile-pour-compenser-commission-30">5. Majoration 20% sur mobile pour compenser commission 30%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Apple/Google prennent 30% de commission</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe le prix mobile</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le prix web est 4.99€ (commission Mangopay 5.4%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le prix mobile est 5.99€ (commission Apple/Google 30%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la majoration est de 1€ (+20%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela compense partiellement la commission excessive</p>
|
||
<hr />
|
||
<h2 id="6-email-incitation-souscription-web-moins-chere">6. Email incitation souscription web moins chère</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte Premium depuis l'app mobile</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vois le prix 5.99€</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois aussi un message:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un lien vers le site web est fourni</p>
|
||
<hr />
|
||
<h2 id="7-calcul-economie-souscription-web-vs-mobile">7. Calcul économie souscription web vs mobile</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le prix web est 4.99€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le prix mobile est 5.99€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule l'économie annuelle</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> web : 4.99€ × 12 = 59.88€/an
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mobile : 5.99€ × 12 = 71.88€/an
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> économie : 12€/an (soit 20% d'économie)</p>
|
||
<hr />
|
||
<h2 id="8-activation-immediate-apres-paiement-reussi">8. Activation immédiate après paiement réussi</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens de payer mon abonnement Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le paiement est confirmé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon statut passe immédiatement à "Premium"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accéder aux avantages Premium dès maintenant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de confirmation</p>
|
||
<hr />
|
||
<h2 id="9-email-confirmation-souscription">9. Email confirmation souscription</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai souscrit à Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la souscription est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="10-email-rappel-7-jours-avant-renouvellement">10. Email rappel 7 jours avant renouvellement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon abonnement mensuel se renouvelle le 15 juillet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 8 juillet arrive (7 jours avant)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email de rappel:</p>
|
||
<hr />
|
||
<h2 id="11-renouvellement-automatique-reussi">11. Renouvellement automatique réussi</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon abonnement mensuel arrive à échéance le 15 juillet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 15 juillet arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay/Apple/Google prélève automatiquement 4.99€ (ou 5.99€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon abonnement est renouvelé pour 1 mois supplémentaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de confirmation</p>
|
||
<hr />
|
||
<h2 id="12-email-confirmation-renouvellement">12. Email confirmation renouvellement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon abonnement vient d'être renouvelé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le paiement est confirmé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="13-echec-paiement-renouvellement-tentative-1">13. Échec paiement renouvellement - Tentative 1</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon abonnement doit se renouveler le 15 juillet
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que ma carte bancaire est expirée ou sans fonds</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le prélèvement échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="14-retry-automatique-paiement-apres-3-jours">14. Retry automatique paiement après 3 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le paiement a échoué le 15 juillet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 18 juillet arrive (J+3)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Mangopay/Apple/Google tente automatiquement un nouveau prélèvement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le paiement réussit, l'abonnement est renouvelé normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le paiement échoue encore, un 2ème retry est programmé</p>
|
||
<hr />
|
||
<h2 id="15-retry-automatique-paiement-apres-7-jours">15. Retry automatique paiement après 7 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 2 tentatives ont échoué (15 juillet et 18 juillet)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 22 juillet arrive (J+7)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une 3ème et dernière tentative est effectuée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le paiement réussit, l'abonnement est sauvé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le paiement échoue, l'abonnement est annulé automatiquement</p>
|
||
<hr />
|
||
<h2 id="16-annulation-automatique-apres-3-echecs-paiement">16. Annulation automatique après 3 échecs paiement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les 3 tentatives de renouvellement ont échoué (J+0, J+3, J+7)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la 3ème tentative échoue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon abonnement Premium est annulé automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon statut repasse à "Gratuit"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je perds accès aux avantages Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email d'annulation</p>
|
||
<hr />
|
||
<h2 id="17-email-annulation-automatique-pour-impaye">17. Email annulation automatique pour impayé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon abonnement a été annulé pour échec paiement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'annulation devient effective</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="18-annulation-self-service-dans-settings">18. Annulation self-service dans Settings</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux annuler mon abonnement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à "Paramètres > Abonnement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un bouton "Annuler l'abonnement"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux annuler en 2 clics sans contacter le support</p>
|
||
<hr />
|
||
<h2 id="19-confirmation-avant-annulation">19. Confirmation avant annulation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Annuler l'abonnement"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une popup de confirmation apparaît</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="20-acces-premium-maintenu-jusqua-fin-periode-payee">20. Accès Premium maintenu jusqu'à fin période payée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai annulé mon abonnement le 1er juillet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon abonnement mensuel était valable jusqu'au 15 juillet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'annulation est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je garde l'accès Premium jusqu'au 15 juillet
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> à partir du 16 juillet, je repasse en gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne suis pas remboursé pour les 14 jours restants</p>
|
||
<hr />
|
||
<h2 id="21-justification-pas-de-remboursement-prorata">21. Justification pas de remboursement prorata</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'industrie (Spotify, Netflix, YouTube) ne rembourse pas prorata</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave applique la même règle</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> c'est le standard accepté par les utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela simplifie la gestion comptable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> évite les abus (souscription puis annulation immédiate pour remboursement)</p>
|
||
<hr />
|
||
<h2 id="22-email-confirmation-annulation">22. Email confirmation annulation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai annulé mon abonnement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'annulation est enregistrée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="23-pas-de-renouvellement-apres-annulation">23. Pas de renouvellement après annulation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai annulé mon abonnement le 1er juillet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 15 juillet arrive (date de renouvellement prévue)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun prélèvement n'est effectué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon statut passe automatiquement à "Gratuit"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne reçois pas d'email de renouvellement</p>
|
||
<hr />
|
||
<h2 id="24-reabonnement-possible-immediatement">24. Réabonnement possible immédiatement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai annulé mon abonnement il y a 5 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à la page Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux me réabonner immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le processus de paiement est le même que la première fois</p>
|
||
<hr />
|
||
<h2 id="25-pas-de-nouvelle-periode-dessai-au-reabonnement">25. Pas de nouvelle période d'essai au réabonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai annulé mon abonnement il y a 3 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me réabonne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je paie immédiatement 4.99€ (pas d'essai gratuit)</p>
|
||
<hr />
|
||
<h2 id="26-offre-win-back-pour-utilisateurs-ayant-annule">26. Offre win-back pour utilisateurs ayant annulé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai annulé mon abonnement il y a 1 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois un email de win-back</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une offre spéciale:</p>
|
||
<hr />
|
||
<h2 id="27-table-subscriptions-en-base-postgresql">27. Table subscriptions en base PostgreSQL</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur souscrit à Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les données sont enregistrées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la table subscriptions contient:</p>
|
||
<hr />
|
||
<h2 id="28-statuts-possibles-dans-subscriptionstatus">28. Statuts possibles dans subscription.status</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un abonnement peut avoir différents statuts</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le statut est stocké en base</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les valeurs possibles sont:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="29-cache-redis-pour-verification-premium-temps-reel">29. Cache Redis pour vérification Premium temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur lance un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'app vérifie s'il est Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une clé Redis est consultée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si la clé n'existe pas, elle est recalculée depuis PostgreSQL
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela garantit des performances <10ms</p>
|
||
<hr />
|
||
<h2 id="30-refresh-cache-redis-via-webhooks">30. Refresh cache Redis via webhooks</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un paiement est confirmé par Mangopay/Apple/Google</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un webhook est reçu par RoadWave</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le cache Redis premium:{user_id} est mis à jour immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur voit son statut Premium activé sans délai</p>
|
||
<hr />
|
||
<h2 id="31-webhooks-mangopay-payin_normal_succeeded">31. Webhooks Mangopay - PAYIN_NORMAL_SUCCEEDED</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un paiement Mangopay réussit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay envoie le webhook PAYIN_NORMAL_SUCCEEDED</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave met à jour subscriptions.status = 'active'
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> met à jour current_period_end = NOW() + 1 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> refresh le cache Redis premium:{user_id} = true</p>
|
||
<hr />
|
||
<h2 id="32-webhooks-mangopay-payin_normal_failed">32. Webhooks Mangopay - PAYIN_NORMAL_FAILED</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un paiement Mangopay échoue</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Mangopay envoie le webhook PAYIN_NORMAL_FAILED</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave met à jour subscriptions.status = 'past_due'
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> programme un retry automatique dans 3 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> envoie un email à l'utilisateur</p>
|
||
<hr />
|
||
<h2 id="33-webhooks-apple-app-store-server-notifications">33. Webhooks Apple - App Store Server Notifications</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un paiement Apple IAP change de statut</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Apple envoie une notification serveur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave parse la notification (JSON)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> met à jour la subscription en conséquence
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> refresh le cache Redis</p>
|
||
<hr />
|
||
<h2 id="34-webhooks-google-real-time-developer-notifications">34. Webhooks Google - Real-time Developer Notifications</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un paiement Google Play change de statut</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Google envoie une notification temps réel</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave parse la notification (JSON)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> met à jour la subscription en conséquence
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> refresh le cache Redis</p>
|
||
<hr />
|
||
<h2 id="35-dashboard-admin-metriques-abonnements">35. Dashboard admin - Métriques abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin consulte les métriques Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède au dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="36-calcul-churn-rate-mensuel">36. Calcul churn rate mensuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 287 utilisateurs ont annulé ce mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il y avait 12,547 abonnés au début du mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le churn rate est calculé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> churn = 287 / 12,547 = 2.3%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un churn <5% est considéré comme excellent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave surveille cette métrique de près</p>
|
||
<hr />
|
||
<h2 id="37-alerte-si-churn-rate-5">37. Alerte si churn rate >5%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le churn rate mensuel dépasse 5%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette anomalie</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte est envoyée à l'équipe:</p>
|
||
<hr />
|
||
<h2 id="38-enquete-satisfaction-a-lannulation">38. Enquête satisfaction à l'annulation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je viens d'annuler mon abonnement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'annulation est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un questionnaire rapide:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les réponses aident à améliorer l'offre Premium</p>
|
||
<hr />
|
||
<h2 id="39-repartition-canaux-souscription">39. Répartition canaux souscription</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin analyse les canaux de souscription</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte les statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela aide à orienter les efforts marketing (inciter web = moins de commission)</p>
|
||
<hr />
|
||
<h2 id="40-performance-verification-premium-10ms">40. Performance vérification Premium <10ms</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 000 utilisateurs consultent des contenus simultanément</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> chaque requête vérifie le statut Premium via Redis</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le temps de réponse moyen est <10ms
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Redis gère facilement 100 000 requêtes/seconde
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience utilisateur est fluide</p>
|
||
<hr />
|
||
<h2 id="41-backup-donnees-abonnements">41. Backup données abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les données d'abonnements sont critiques</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un backup est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> PostgreSQL est répliqué en temps réel sur un replica
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un snapshot quotidien est stocké sur S3
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> en cas de crash, les données peuvent être restaurées <5 minutes</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="multi-devices-et-detection-simultanee">Multi-devices et détection simultanée</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'abonné Premium</em>
|
||
<em>Je veux utiliser mon compte sur plusieurs appareils</em>
|
||
<em>Mais limité à 1 seul stream actif à la fois pour éviter le partage abusif</em></p>
|
||
</blockquote>
|
||
<p><strong>30 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium actif
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon compte est valide</p>
|
||
</blockquote>
|
||
<h2 id="1-1-seul-stream-actif-autorise-par-compte">1. 1 seul stream actif autorisé par compte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'écoute rien actuellement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance un contenu sur mon iPhone</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le stream démarre normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Redis enregistre: active_streams:{user_id} = {device_id: "iPhone", started_at: timestamp}</p>
|
||
<hr />
|
||
<h2 id="2-detection-connexion-simultanee-arret-premier-device">2. Détection connexion simultanée - Arrêt premier device</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu sur mon iPhone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance un contenu sur mon iPad</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système détecte une session active sur iPhone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture sur iPhone est arrêtée immédiatement (WebSocket close)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois sur iPhone: "Lecture interrompue : votre compte est utilisé sur un autre appareil"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture démarre sur iPad normalement</p>
|
||
<hr />
|
||
<h2 id="3-message-explicite-sur-device-interrompu">3. Message explicite sur device interrompu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma lecture sur iPhone vient d'être interrompue</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je regarde l'écran de mon iPhone</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une overlay avec le message:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Reprendre ici" est disponible</p>
|
||
<hr />
|
||
<h2 id="4-reprendre-lecture-sur-device-interrompu">4. Reprendre lecture sur device interrompu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma lecture sur iPhone a été interrompue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je veux reprendre sur iPhone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Reprendre ici"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture démarre sur iPhone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'iPad est à son tour interrompu avec le même message
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le "ping-pong" entre devices est possible (mais pénible)</p>
|
||
<hr />
|
||
<h2 id="5-enregistrement-session-active-dans-redis">5. Enregistrement session active dans Redis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je lance un contenu sur mon iPhone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la lecture démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une entrée Redis est créée:</p>
|
||
<hr />
|
||
<h2 id="6-heartbeat-toutes-les-30-secondes-pour-maintenir-session">6. Heartbeat toutes les 30 secondes pour maintenir session</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu sur mon iPhone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 30 secondes s'écoulent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app envoie un heartbeat au serveur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le serveur refresh le TTL Redis à 300 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la session reste active</p>
|
||
<hr />
|
||
<h2 id="7-session-consideree-morte-apres-5-minutes-sans-heartbeat">7. Session considérée morte après 5 minutes sans heartbeat</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu sur mon iPhone
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que l'app crash ou que le réseau coupe</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 5 minutes s'écoulent sans heartbeat</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'entrée Redis expire automatiquement (TTL atteint)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux relancer sur n'importe quel device sans conflit</p>
|
||
<hr />
|
||
<h2 id="8-verification-session-avant-demarrage-lecture">8. Vérification session avant démarrage lecture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux lancer un contenu sur mon iPad</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur Play</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le serveur vérifie Redis: active_streams:{user_id}
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si une session existe sur un autre device, elle est tuée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la nouvelle session iPad est enregistrée dans Redis</p>
|
||
<hr />
|
||
<h2 id="9-gestion-multi-utilisateurs-simultanes">9. Gestion multi-utilisateurs simultanés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 000 utilisateurs Premium écoutent simultanément</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Redis stocke 100 000 entrées active_streams</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> chaque entrée a un TTL de 5 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Redis gère facilement cette charge (~10 MB de RAM)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les vérifications sont quasi-instantanées (O(1))</p>
|
||
<hr />
|
||
<h2 id="10-contenus-telecharges-offline-ne-comptent-pas-comme-stream">10. Contenus téléchargés (offline) ne comptent pas comme stream</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai téléchargé 20 contenus en mode offline</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute un contenu téléchargé sur mon iPhone sans réseau</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune session active n'est enregistrée dans Redis
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux écouter offline pendant qu'un autre device stream online</p>
|
||
<hr />
|
||
<h2 id="11-transition-rapide-device-10s-toleree">11. Transition rapide device <10s tolérée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute dans ma voiture sur mon iPhone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'arrive chez moi</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance la lecture sur mon iPad dans les 10 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la transition est considérée comme un changement de device légitime
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun message d'erreur n'est affiché sur iPhone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture reprend exactement où j'étais sur iPad</p>
|
||
<hr />
|
||
<h2 id="12-detection-transition-rapide-via-timestamps">12. Détection transition rapide via timestamps</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la session iPhone a started_at = 14:30:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance sur iPad à 14:30:05 (5 secondes après)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le serveur détecte: diff = 5s < 10s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> applique une "graceful transition" (pas de message d'erreur iPhone)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Redis met à jour: active_streams:{user_id} = {device_id: "iPad", ...}</p>
|
||
<hr />
|
||
<h2 id="13-plusieurs-devices-disponibles-mais-1-seul-actif">13. Plusieurs devices disponibles mais 1 seul actif</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je possède:</p>
|
||
<pre><code>| device | status |
|
||
|---|---|
|
||
| iPhone | Installé |
|
||
| iPad | Installé |
|
||
| MacBook (web) | Connecté |
|
||
| Android (conjoint) | Installé |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance un stream sur n'importe quel device</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seulement 1 peut être actif à la fois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les autres devices sont en "standby"</p>
|
||
<hr />
|
||
<h2 id="14-justification-anti-partage-compte">14. Justification anti-partage compte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium partage son compte avec un ami</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les 2 personnes essaient d'écouter simultanément</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture est constamment interrompue sur l'un ou l'autre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience devient inutilisable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela décourage fortement le partage de compte</p>
|
||
<hr />
|
||
<h2 id="15-justification-protection-revenus-createurs">15. Justification protection revenus créateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 1 abonnement Premium = 4.99€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 70% sont reversés aux créateurs (3.49€)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les créateurs sont rémunérés pour 1 personne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si 2 personnes utilisent le même compte simultanément, c'est injuste
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la limite 1 stream protège l'équité du système</p>
|
||
<hr />
|
||
<h2 id="16-justification-ux-claire">16. Justification UX claire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un stream est interrompu sur un device</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur voit le message explicite</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il comprend immédiatement pourquoi (autre device actif)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut choisir de reprendre sur le device actuel ou l'autre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de confusion ou frustration</p>
|
||
<hr />
|
||
<h2 id="17-comparaison-avec-spotify-limite-1-stream">17. Comparaison avec Spotify (limite 1 stream)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Spotify Premium limite aussi à 1 stream actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave applique la même règle</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les utilisateurs connaissent déjà ce comportement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela paraît normal et accepté par l'industrie</p>
|
||
<hr />
|
||
<h2 id="18-comparaison-avec-netflix-plusieurs-streams-selon-formule">18. Comparaison avec Netflix (plusieurs streams selon formule)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Netflix permet 1-4 streams selon la formule</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave limite à 1 stream pour tous</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> c'est plus strict que Netflix
|
||
<span style="color: #F44336"><strong>Mais</strong></span> Netflix cible le foyer familial (TV partagée)
|
||
<span style="color: #4CAF50"><strong>Alors</strong></span> que RoadWave cible l'individu conducteur (usage personnel)</p>
|
||
<hr />
|
||
<h2 id="19-detection-pattern-suspect-changements-devices-frequents">19. Détection pattern suspect - Changements devices fréquents</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur change de device 50 fois en 1 heure</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte ce pattern anormal</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte est générée pour l'équipe modération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compte peut être marqué pour surveillance
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si abus confirmé, suspension possible</p>
|
||
<hr />
|
||
<h2 id="20-logs-des-changements-de-device">20. Logs des changements de device</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je change de device plusieurs fois par jour</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les changements sont loggés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> chaque événement est enregistré:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces logs aident à détecter les partages de compte</p>
|
||
<hr />
|
||
<h2 id="21-metriques-admin-changements-devices-par-utilisateur">21. Métriques admin - Changements devices par utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin consulte les métriques de streaming</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède au dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="22-email-davertissement-si-changements-excessifs">22. Email d'avertissement si changements excessifs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je change de device 30 fois par jour pendant 3 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte ce pattern</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email d'avertissement:</p>
|
||
<hr />
|
||
<h2 id="23-suspension-compte-apres-avertissement-ignore">23. Suspension compte après avertissement ignoré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai reçu un email d'avertissement il y a 7 jours
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que je continue à changer de device 30 fois par jour</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe modération examine le compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte Premium peut être suspendu pour partage abusif
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de suspension avec justification</p>
|
||
<hr />
|
||
<h2 id="24-faq-pourquoi-ma-lecture-sarrete-quand-jutilise-un-autre-device">24. FAQ - Pourquoi ma lecture s'arrête quand j'utilise un autre device ?</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la FAQ Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je cherche "lecture interrompue"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je trouve la réponse:</p>
|
||
<hr />
|
||
<h2 id="25-support-utilisateur-pense-etre-pirate">25. Support - Utilisateur pense être piraté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur voit constamment "Lecture interrompue"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il pense que son compte est piraté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il contacte le support</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le support vérifie les logs de changements de device
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> peut identifier les devices (iPhone, iPad perso vs iPhone inconnu)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> conseille de changer le mot de passe si device inconnu détecté</p>
|
||
<hr />
|
||
<h2 id="26-changement-mot-de-passe-deconnecte-tous-les-devices">26. Changement mot de passe déconnecte tous les devices</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je pense que mon compte est compromis</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je change mon mot de passe</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous mes devices sont déconnectés immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les sessions actives dans Redis sont supprimées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois me reconnecter sur chaque device
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela sécurise mon compte</p>
|
||
<hr />
|
||
<h2 id="27-test-charge-100-000-verificationsseconde">27. Test charge - 100 000 vérifications/seconde</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 000 utilisateurs Premium lancent des contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> chaque lancement vérifie Redis (GET active_streams:{user_id})</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Redis peut gérer facilement 100 000 requêtes/seconde
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le temps de réponse moyen est <1ms
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun ralentissement n'est constaté</p>
|
||
<hr />
|
||
<h2 id="28-test-failover-redis">28. Test failover Redis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le serveur Redis principal tombe en panne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le failover automatique vers le replica Redis s'active</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les sessions actives peuvent être perdues temporairement (max 5 min)
|
||
<span style="color: #F44336"><strong>Mais</strong></span> les utilisateurs peuvent relancer immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'impact est minimal (pas de perte de données critiques)</p>
|
||
<hr />
|
||
<h2 id="29-test-concurrence-lancement-simultane-2-devices">29. Test concurrence - Lancement simultané 2 devices</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je lance exactement au même instant sur iPhone et iPad</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les 2 requêtes arrivent en parallèle au serveur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Redis utilise un lock (SETNX) pour atomicité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 1 seul device gagne (par exemple iPhone)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'autre device (iPad) reçoit immédiatement une erreur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut retry sur iPad si souhaité</p>
|
||
<hr />
|
||
<h2 id="30-nettoyage-automatique-sessions-expirees">30. Nettoyage automatique sessions expirées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 1000 sessions Redis ont expiré (TTL atteint)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Redis supprime automatiquement ces entrées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la mémoire est libérée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les nouveaux streams peuvent démarrer sans conflit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune intervention manuelle n'est nécessaire</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="offre-et-tarification-premium">Offre et tarification Premium</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux pouvoir souscrire à un abonnement Premium</em>
|
||
<em>Afin de profiter d'une expérience sans publicité avec des avantages exclusifs</em></p>
|
||
</blockquote>
|
||
<p><strong>31 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant qu'utilisateur</p>
|
||
</blockquote>
|
||
<h2 id="1-formule-mensuelle-a-499mois">1. Formule mensuelle à 4.99€/mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte les offres Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vois la formule mensuelle</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le prix affiché est 4.99€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a aucune réduction
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le prix effectif par mois est 4.99€</p>
|
||
<hr />
|
||
<h2 id="2-formule-annuelle-a-4999an-2-mois-offerts">2. Formule annuelle à 49.99€/an (2 mois offerts)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte les offres Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vois la formule annuelle</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le prix affiché est 49.99€/an
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'économie affichée est "2 mois offerts"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le prix effectif par mois est 4.16€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le badge "Meilleure offre"</p>
|
||
<hr />
|
||
<h2 id="3-calcul-economie-formule-annuelle">3. Calcul économie formule annuelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la formule mensuelle coûte 4.99€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule le coût annuel en mensuel</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 12 mois × 4.99€ = 59.88€/an
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la formule annuelle coûte 49.99€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'économie est de 9.89€ (≈ 2 mois gratuits)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la réduction est de 16.5%</p>
|
||
<hr />
|
||
<h2 id="4-pas-dessai-gratuit-disponible">4. Pas d'essai gratuit disponible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte les offres Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je recherche une option "Essai gratuit"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune option d'essai gratuit n'est proposée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois payer dès le premier jour pour accéder au Premium</p>
|
||
<hr />
|
||
<h2 id="5-justification-absence-essai-gratuit-anti-abus-vacances">5. Justification absence essai gratuit - Anti-abus vacances</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave ne propose pas d'essai gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur envisage un road trip de 14 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il ne peut pas s'abonner pour l'essai gratuit puis annuler
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela évite les inscriptions opportunistes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> protège les revenus des créateurs</p>
|
||
<hr />
|
||
<h2 id="6-justification-absence-essai-gratuit-protection-revenus-createurs">6. Justification absence essai gratuit - Protection revenus créateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur Premium écoute des contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il génère des écoutes dès le jour 1</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les créateurs sont rémunérés immédiatement (70% de 4.99€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de "période gratuite" sans rémunération créateurs</p>
|
||
<hr />
|
||
<h2 id="7-justification-absence-essai-gratuit-simplicite">7. Justification absence essai gratuit - Simplicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave gère les abonnements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il n'y a pas d'essai gratuit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> pas de gestion complexe de période trial
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> pas de workflow de conversion trial → payant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela réduit la complexité technique</p>
|
||
<hr />
|
||
<h2 id="8-justification-absence-essai-gratuit-engagement">8. Justification absence essai gratuit - Engagement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur paie dès le début</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il souscrit à Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est plus engagé qu'un utilisateur en essai gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le taux de churn est généralement plus faible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lifetime value (LTV) est plus élevée</p>
|
||
<hr />
|
||
<h2 id="9-pas-de-partage-familial-au-mvp">9. Pas de partage familial au MVP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte les offres Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je recherche une option "Famille" ou "Partage"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune option de partage familial n'est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les abonnements individuels sont proposés</p>
|
||
<hr />
|
||
<h2 id="10-justification-absence-partage-familial-complexite-technique">10. Justification absence partage familial - Complexité technique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le partage familial nécessite:</p>
|
||
<pre><code>| fonctionnalité | complexité |
|
||
|---|---|
|
||
| Gestion invitations | Moyenne |
|
||
| Validation liens famille | Moyenne |
|
||
| Limite devices par membre | Élevée |
|
||
| Dashboard admin famille | Élevée |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave évalue le ROI</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût dev/support est trop élevé pour le MVP
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la fonctionnalité est reportée post-MVP</p>
|
||
<hr />
|
||
<h2 id="11-justification-absence-partage-familial-risque-abus">11. Justification absence partage familial - Risque abus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une offre famille permet 5-6 membres</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il n'y a pas de vérification stricte de lien familial</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> des "familles" de 6 inconnus pourraient se former
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela réduirait fortement les revenus (6 personnes pour 1 abonnement)</p>
|
||
<hr />
|
||
<h2 id="12-justification-absence-partage-familial-cible-individuelle">12. Justification absence partage familial - Cible individuelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave cible principalement les conducteurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> chaque conducteur utilise l'app individuellement en voiture</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le besoin de partage familial est limité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la plupart des utilisateurs sont des individus (pas des familles)</p>
|
||
<hr />
|
||
<h2 id="13-post-mvp-offre-famille-a-999mois-pour-5-comptes">13. Post-MVP - Offre Famille à 9.99€/mois pour 5 comptes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave envisage une offre Famille post-MVP</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la fonctionnalité est spécifiée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le prix serait 9.99€/mois pour 5 comptes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela représente 2€/mois/personne
|
||
<span style="color: #F44336"><strong>Mais</strong></span> cette offre n'est pas disponible au MVP</p>
|
||
<hr />
|
||
<h2 id="14-comparaison-tarif-spotify-a-1099mois">14. Comparaison tarif - Spotify à 10.99€/mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Spotify Premium coûte 10.99€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe son prix à 4.99€/mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave est 54.5% moins cher que Spotify
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela positionne RoadWave comme très accessible</p>
|
||
<hr />
|
||
<h2 id="15-comparaison-tarif-youtube-premium-a-1199mois">15. Comparaison tarif - YouTube Premium à 11.99€/mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que YouTube Premium coûte 11.99€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe son prix à 4.99€/mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave est 58.4% moins cher que YouTube Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela est un argument commercial fort</p>
|
||
<hr />
|
||
<h2 id="16-comparaison-tarif-apple-music-a-1099mois">16. Comparaison tarif - Apple Music à 10.99€/mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'Apple Music coûte 10.99€/mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave fixe son prix à 4.99€/mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> RoadWave est 54.5% moins cher qu'Apple Music
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela attire les utilisateurs sensibles au prix</p>
|
||
<hr />
|
||
<h2 id="17-justification-tarif-bas-cible-conducteurs-quotidiens">17. Justification tarif bas - Cible conducteurs quotidiens</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave cible les trajets quotidiens domicile-travail</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le prix est fixé à 4.99€/mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> c'est un budget raisonnable pour un conducteur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> équivalent à ~1-2 cafés/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> psychologiquement acceptable pour un usage quotidien</p>
|
||
<hr />
|
||
<h2 id="18-justification-formule-annuelle-engagement-long-terme">18. Justification formule annuelle - Engagement long terme</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la formule annuelle offre 2 mois gratuits</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur souscrit pour 1 an</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il s'engage sur le long terme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> RoadWave sécurise 49.99€ de revenus immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cash flow est amélioré</p>
|
||
<hr />
|
||
<h2 id="19-justification-formule-annuelle-reduction-churn">19. Justification formule annuelle - Réduction churn</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur paie 49.99€ pour l'année</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il envisage d'arrêter après 3 mois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il a déjà payé pour 12 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il continuera probablement à utiliser l'app
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le taux de churn est réduit significativement</p>
|
||
<hr />
|
||
<h2 id="20-affichage-comparatif-des-deux-formules">20. Affichage comparatif des deux formules</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vois les deux formules côte à côte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<hr />
|
||
<h2 id="21-mise-en-avant-formule-annuelle">21. Mise en avant formule annuelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je vois les deux formules</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la formule annuelle a un badge "Meilleure offre" ⭐
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est visuellement mise en avant (bordure colorée, taille plus grande)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'économie de 2 mois est affichée en gros
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela incite à choisir la formule annuelle</p>
|
||
<hr />
|
||
<h2 id="22-lien-pourquoi-pas-dessai-gratuit-en-faq">22. Lien "Pourquoi pas d'essai gratuit ?" en FAQ</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte la page Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "FAQ"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une question "Pourquoi pas d'essai gratuit ?"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la réponse explique:</p>
|
||
<hr />
|
||
<h2 id="23-ab-test-formule-annuelle-post-mvp">23. A/B test formule annuelle (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave veut optimiser la conversion annuelle</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un A/B test est lancé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> groupe A voit "2 mois offerts" (économie en durée)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> groupe B voit "Économisez 9.89€" (économie en argent)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les taux de souscription sont mesurés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message le plus performant est déployé</p>
|
||
<hr />
|
||
<h2 id="24-promo-temporaire-exceptionnelle-black-friday-etc">24. Promo temporaire exceptionnelle (Black Friday, etc.)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que c'est le Black Friday</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une promo temporaire est activée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la formule annuelle peut passer à 39.99€/an (au lieu de 49.99€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'économie affichée est "4 mois offerts !"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la promo dure 3 jours uniquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela génère un pic de souscriptions</p>
|
||
<hr />
|
||
<h2 id="25-code-promo-partenariat-influenceur">25. Code promo partenariat influenceur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un influenceur promeut RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il partage un code promo "INFLUENCEUR20"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les utilisateurs obtiennent -20% sur le premier mois (3.99€ au lieu de 4.99€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le code est valable 1 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les conversions sont trackées par code promo</p>
|
||
<hr />
|
||
<h2 id="26-statistiques-admin-repartition-formules">26. Statistiques admin - Répartition formules</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin consulte les métriques d'abonnements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède au dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces données aident à piloter la stratégie tarifaire</p>
|
||
<hr />
|
||
<h2 id="27-calcul-revenus-mensuels-recurrents-mrr">27. Calcul revenus mensuels récurrents (MRR)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave a:</p>
|
||
<pre><code>| formule | nombre abonnés | prix |
|
||
|---|---|---|
|
||
| Mensuel | 7,234 | 4.99€/mois |
|
||
| Annuel | 5,313 | 49.99€/an |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le MRR est calculé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> MRR mensuel = 7,234 × 4.99€ = 36,098€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> MRR annuel ramené au mois = 5,313 × 49.99€ / 12 = 22,139€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> MRR total = 58,237€/mois</p>
|
||
<hr />
|
||
<h2 id="28-projection-revenus-annuels-arr">28. Projection revenus annuels (ARR)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le MRR est de 58,237€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'ARR est calculé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ARR = 58,237€ × 12 = 698,844€/an
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela aide à évaluer la valorisation de l'entreprise</p>
|
||
<hr />
|
||
<h2 id="29-affichage-prix-ttc-tva-incluse">29. Affichage prix TTC (TVA incluse)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave est une plateforme française</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les prix sont affichés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les prix sont TTC (TVA 20% incluse)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le prix 4.99€ inclut déjà la TVA
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela respecte la réglementation française</p>
|
||
<hr />
|
||
<h2 id="30-performance-page-premium-avec-cache">30. Performance page Premium avec cache</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la page Premium est consultée fréquemment</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur charge la page</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les prix et avantages sont servis depuis un cache CDN
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le temps de chargement est <200ms
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela garantit une expérience fluide</p>
|
||
<hr />
|
||
<h2 id="31-localisation-prix-selon-pays-post-mvp">31. Localisation prix selon pays (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave se lance à l'international post-MVP</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur se connecte depuis l'Allemagne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les prix peuvent être ajustés (ex: 4.99€ en France, 4.49€ en Pologne)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela respecte le pouvoir d'achat local
|
||
<span style="color: #F44336"><strong>Mais</strong></span> cette fonctionnalité n'est pas au MVP (France uniquement)</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="profil-createur">Profil créateur</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur de RoadWave</em>
|
||
<em>Je veux consulter les profils des créateurs</em>
|
||
<em>Afin de découvrir leur contenu et décider de m'abonner</em></p>
|
||
</blockquote>
|
||
<p><strong>31 scénarios</strong> (28 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée</p>
|
||
</blockquote>
|
||
<h2 id="1-url-du-profil-createur">1. URL du profil créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec le pseudo "paris_stories"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur accède au profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'URL est "https://roadwave.fr/@paris_stories"</p>
|
||
<hr />
|
||
<h2 id="2-informations-principales-du-profil">2. Informations principales du profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur "@paris_stories" avec les informations suivantes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le profil est affiché</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les éléments suivants sont visibles:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="3-plan-arrondi-des-statistiques-publiques">3. 📋 Plan: Arrondi des statistiques publiques</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec <valeur_exacte> <métrique></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le profil est affiché</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la valeur affichée est "<valeur_affichée>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>métrique</th>
|
||
<th>valeur_exacte</th>
|
||
<th>valeur_affichée</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>abonnés</td>
|
||
<td>342</td>
|
||
<td>342</td>
|
||
</tr>
|
||
<tr>
|
||
<td>abonnés</td>
|
||
<td>1200</td>
|
||
<td>1.2K</td>
|
||
</tr>
|
||
<tr>
|
||
<td>abonnés</td>
|
||
<td>54000</td>
|
||
<td>54K</td>
|
||
</tr>
|
||
<tr>
|
||
<td>abonnés</td>
|
||
<td>1200000</td>
|
||
<td>1.2M</td>
|
||
</tr>
|
||
<tr>
|
||
<td>écoutes</td>
|
||
<td>842</td>
|
||
<td>842</td>
|
||
</tr>
|
||
<tr>
|
||
<td>écoutes</td>
|
||
<td>5400</td>
|
||
<td>5.4K</td>
|
||
</tr>
|
||
<tr>
|
||
<td>écoutes</td>
|
||
<td>142000</td>
|
||
<td>142K</td>
|
||
</tr>
|
||
<tr>
|
||
<td>écoutes</td>
|
||
<td>2100000</td>
|
||
<td>2.1M</td>
|
||
</tr>
|
||
<tr>
|
||
<td>durée (heures)</td>
|
||
<td>18</td>
|
||
<td>18h</td>
|
||
</tr>
|
||
<tr>
|
||
<td>durée (heures)</td>
|
||
<td>142</td>
|
||
<td>142h</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="4-bio-avec-markdown-basique">4. Bio avec markdown basique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec la bio suivante en markdown:</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le profil est affiché</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le texte en gras "Histoires de Paris" est formaté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le texte en italique "Nouveau contenu chaque semaine" est formaté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le lien "https://paris-stories.fr" est cliquable</p>
|
||
<hr />
|
||
<h2 id="5-limitation-de-la-bio-a-300-caracteres">5. Limitation de la bio à 300 caractères</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur qui entre une bio de 350 caractères</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la bio est sauvegardée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les 300 premiers caractères sont conservés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message "Maximum 300 caractères" s'affiche</p>
|
||
<hr />
|
||
<h2 id="6-boutons-daction-principaux">6. Boutons d'action principaux</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur consulte un profil créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page est chargée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les boutons suivants sont visibles:</p>
|
||
<pre><code>| bouton | action |
|
||
|---|---|
|
||
| S'abonner | Abonnement au créateur |
|
||
| Partager profil | Menu de partage |
|
||
| ••• | Menu contextuel |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="7-menu-contextuel-du-profil">7. Menu contextuel du profil [•••]</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur clique sur le bouton [•••]</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le menu s'ouvre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les options suivantes sont disponibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="8-liste-des-contenus-du-createur">8. Liste des contenus du créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec 3 contenus publiés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le profil est affiché</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> chaque contenu affiche:</p>
|
||
<pre><code>| élément | exemple |
|
||
|---|---|
|
||
| Cover image | Image 16:9 |
|
||
| Titre | Balade à Paris |
|
||
| Durée et écoutes | 12 min · 🎧 2.3K |
|
||
| Localisation | 📍 Paris |
|
||
| Bouton lecture | ▶️ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-plan-options-de-tri-des-contenus">9. 📋 Plan: Options de tri des contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec 10 contenus publiés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur sélectionne le tri "<option_tri>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus sont triés par <critère></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>option_tri</th>
|
||
<th>critère</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Plus récents</td>
|
||
<td>Date publication DESC (défaut)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Plus populaires</td>
|
||
<td>Écoutes × facteur temporel (90 jours)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Plus anciens</td>
|
||
<td>Date publication ASC</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="10-filtrage-des-contenus-par-tag">10. Filtrage des contenus par tag</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec des contenus taggés "Voyage", "Histoire", "Gastronomie"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur filtre par tags "Voyage, Histoire"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus avec ces tags sont affichés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le nombre de résultats est indiqué "12 contenus"</p>
|
||
<hr />
|
||
<h2 id="11-recherche-locale-dans-le-profil">11. Recherche locale dans le profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur consulte le profil de "@paris_stories"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le créateur a publié 50 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur entre "Montmartre" dans la barre de recherche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la recherche s'effectue sur les titres et descriptions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les contenus correspondants sont affichés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le placeholder indique "Rechercher dans les contenus de @paris_stories"</p>
|
||
<hr />
|
||
<h2 id="12-chargement-pagine-des-contenus">12. Chargement paginé des contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec 100 contenus publiés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le profil est affiché</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 20 contenus sont chargés initialement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Charger plus" est visible en bas de page</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "Charger plus"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 20 contenus supplémentaires sont chargés</p>
|
||
<hr />
|
||
<h2 id="13-informations-publiques-visibles-par-tous">13. Informations publiques visibles par tous</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur consulte un profil créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les informations suivantes sont publiques:</p>
|
||
<pre><code>| information | visible |
|
||
|---|---|
|
||
| Photo et pseudo | ✅ |
|
||
| Badge vérifié | ✅ |
|
||
| Bio | ✅ |
|
||
| Nombre abonnés | ✅ |
|
||
| Nombre contenus | ✅ |
|
||
| Durée totale créée | ✅ |
|
||
| Écoutes totales | ✅ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="14-informations-privees-non-visibles">14. Informations privées non visibles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur consulte un profil créateur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les informations suivantes sont privées:</p>
|
||
<pre><code>| information | visible |
|
||
|---|---|
|
||
| Liste des abonnés | ❌ |
|
||
| Revenus | ❌ |
|
||
| Localisation précise | ❌ |
|
||
| Email | ❌ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="15-dashboard-createur-avec-metriques-privees">15. Dashboard créateur avec métriques privées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur "@paris_stories" consulte son propre dashboard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page statistiques est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métriques suivantes sont accessibles:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="16-graphique-devolution-des-abonnes">16. Graphique d'évolution des abonnés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le créateur consulte son dashboard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il sélectionne la période "30 jours"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un graphique d'évolution des abonnés est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les périodes disponibles sont:</p>
|
||
<pre><code>| période |
|
||
|---|
|
||
| 30j |
|
||
| 90j |
|
||
| 1 an |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-tableau-detaille-des-ecoutes-par-contenu">17. Tableau détaillé des écoutes par contenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec 10 contenus publiés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte le tableau des performances</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> chaque contenu affiche:</p>
|
||
<pre><code>| métrique | exemple |
|
||
|---|---|
|
||
| Titre | Balade |
|
||
| Écoutes totales | 2300 |
|
||
| Écoutes complètes >80% | 1840 |
|
||
| Taux complétion | 80% |
|
||
| Likes | 420 |
|
||
| Partages | 56 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="18-affichage-du-badge-verifie">18. Affichage du badge vérifié</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur vérifié "@paris_stories"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> son profil est affiché</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge bleu "✓" est accolé au pseudo
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un tooltip "Compte vérifié" s'affiche au survol</p>
|
||
<hr />
|
||
<h2 id="19-badge-verifie-visible-partout">19. Badge vérifié visible partout</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur vérifié "@paris_stories"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge "✓" est affiché dans:</p>
|
||
<pre><code>| emplacement |
|
||
|---|
|
||
| Page profil |
|
||
| Player en lecture |
|
||
| Résultats de recherche |
|
||
| Notifications |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="20-plan-attribution-automatique-du-badge-selon-criteres">20. 📋 Plan: Attribution automatique du badge selon critères</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec <critère></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les conditions sont validées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge vérifié est attribué <automatique></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>critère</th>
|
||
<th>automatique</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>KYC Mangopay validé</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>≥10K abonnés + compte >6 mois</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Célébrité / Média officiel</td>
|
||
<td>Manuel</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="21-attribution-automatique-via-kyc">21. Attribution automatique via KYC</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur qui complète son KYC Mangopay</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les documents sont validés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge vérifié est attribué automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification "Votre compte est maintenant vérifié ✓" est envoyée</p>
|
||
<hr />
|
||
<h2 id="22-attribution-automatique-a-10k-abonnes">22. Attribution automatique à 10K abonnés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur avec 9999 abonnés et un compte de 7 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il atteint 10000 abonnés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge vérifié est attribué automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification de félicitations est envoyée</p>
|
||
<hr />
|
||
<h2 id="23-demande-manuelle-de-verification-celebrite">23. Demande manuelle de vérification (célébrité)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur reconnu publiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il soumet le formulaire de demande de vérification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une requête est créée pour l'équipe RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'équipe vérifie l'identité sous 48-72h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le badge est attribué si validation réussie</p>
|
||
<hr />
|
||
<h2 id="24-retrait-du-badge-en-cas-de-suspension">24. Retrait du badge en cas de suspension</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur vérifié avec le badge "✓"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> sa monétisation est suspendue</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge vérifié est retiré temporairement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le badge est restauré après levée de la suspension</p>
|
||
<hr />
|
||
<h2 id="25-retrait-definitif-du-badge-pour-strikes-multiples">25. Retrait définitif du badge pour strikes multiples</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur vérifié avec 3 strikes actifs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un 4ème strike est appliqué (ban)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge vérifié est retiré définitivement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compte est banni</p>
|
||
<hr />
|
||
<h2 id="26-retrait-du-badge-pour-usurpation-didentite">26. Retrait du badge pour usurpation d'identité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un créateur vérifié qui usurpe l'identité d'une célébrité</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la fraude est détectée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le badge est retiré immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compte est banni
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une enquête est ouverte</p>
|
||
<hr />
|
||
<h2 id="27-profil-createur-supprime">27. Profil créateur supprimé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur tente d'accéder à "@deleted_user"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page est chargée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message "Ce profil n'existe pas ou a été supprimé" s'affiche</p>
|
||
<hr />
|
||
<h2 id="28-blocage-dun-createur">28. Blocage d'un créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur bloque le créateur "@spam_account"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur consulte son flux de recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun contenu de "@spam_account" n'est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur n'apparaît plus dans les recherches</p>
|
||
<hr />
|
||
<h2 id="29-deblocage-dun-createur">29. Déblocage d'un créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a bloqué "@paris_stories"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il accède à ses paramètres "Comptes bloqués"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il débloque "@paris_stories"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus du créateur réapparaissent dans les recommandations</p>
|
||
<hr />
|
||
<h2 id="30-signalement-dun-profil-pour-spam">30. Signalement d'un profil pour spam</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur signale le profil "@spam_account"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il sélectionne la raison "Spam"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est envoyé à la modération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message de confirmation s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le profil reste visible jusqu'à décision de modération</p>
|
||
<hr />
|
||
<h2 id="31-signalement-pour-usurpation-didentite">31. Signalement pour usurpation d'identité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur signale le profil "@fake_celebrity"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il sélectionne "Usurpation d'identité"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il fournit une preuve</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est priorisé (priorité HAUTE)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'équipe modération traite sous 24h</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="creation-de-campagnes-publicitaires">Création de campagnes publicitaires</h1>
|
||
<blockquote>
|
||
<p><em>En tant que publicitaire</em>
|
||
<em>Je veux créer des campagnes avec ciblage précis et maîtrise du budget</em>
|
||
<em>Afin d'optimiser mes investissements publicitaires</em></p>
|
||
</blockquote>
|
||
<p><strong>30 scénarios</strong> (27 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un compte publicitaire est créé et vérifié</p>
|
||
</blockquote>
|
||
<h2 id="1-creation-dune-campagne-publicitaire-complete">1. Création d'une campagne publicitaire complète</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté en tant que publicitaire</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée une nouvelle campagne avec les paramètres:</p>
|
||
<pre><code>| 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+ |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne est créée avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget quotidien calculé est de 21.43€/jour
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les diffusions estimées sont de ~430 écoutes complètes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un statut "En attente de validation" est assigné</p>
|
||
<hr />
|
||
<h2 id="2-budget-minimum-50-requis">2. Budget minimum 50€ requis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une nouvelle campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis un budget de 40€</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Budget minimum requis: 50€"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne n'est pas créée</p>
|
||
<hr />
|
||
<h2 id="3-budget-de-50-exactement-accepte">3. Budget de 50€ exactement accepté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une nouvelle campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis un budget de 50€</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne est créée avec succès
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune erreur n'est affichée</p>
|
||
<hr />
|
||
<h2 id="4-calcul-automatique-du-budget-quotidien">4. Calcul automatique du budget quotidien</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une campagne avec:</p>
|
||
<pre><code>| Budget total | 300€ |
|
||
|---|---|
|
||
| Durée | 14 j |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule le budget quotidien</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le budget/jour est de 21.43€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le nombre estimé de diffusions/jour est de 430 (à 0.05€/écoute)</p>
|
||
<hr />
|
||
<h2 id="5-ciblage-geographique-point-gps-precis">5. Ciblage géographique point GPS précis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Point GPS" avec coordonnées (43.1234, 5.9234)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je définis un rayon de 5km</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne cible uniquement les utilisateurs dans ce rayon
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la zone est représentée par un cercle sur la carte</p>
|
||
<hr />
|
||
<h2 id="6-ciblage-geographique-ville">6. Ciblage géographique ville</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Ville" et choisis "Marseille"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne cible tous les utilisateurs dans la commune de Marseille
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les limites administratives sont affichées sur la carte</p>
|
||
<hr />
|
||
<h2 id="7-ciblage-geographique-departement">7. Ciblage géographique département</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Département" et choisis "Var (83)"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne cible tout le département du Var
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une estimation de population cible est affichée</p>
|
||
<hr />
|
||
<h2 id="8-ciblage-geographique-region">8. Ciblage géographique région</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Région" et choisis "Provence-Alpes-Côte d'Azur"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne cible toute la région PACA
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'estimation de population cible est mise à jour</p>
|
||
<hr />
|
||
<h2 id="9-ciblage-geographique-national">9. Ciblage géographique national</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "National"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne cible tous les utilisateurs en France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune limite géographique n'est appliquée</p>
|
||
<hr />
|
||
<h2 id="10-ciblage-horaire-plages-multiples">10. Ciblage horaire plages multiples</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis les plages horaires:</p>
|
||
<pre><code>| Plage |
|
||
|---|
|
||
| 7h-9h |
|
||
| 12h-14h |
|
||
| 17h-19h |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité est diffusée uniquement pendant ces plages
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle n'est jamais diffusée en dehors (ex: 10h, 15h, 20h)</p>
|
||
<hr />
|
||
<h2 id="11-ciblage-horaire-toute-la-journee">11. Ciblage horaire toute la journée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je ne définis aucune plage horaire spécifique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité est diffusée 24h/24
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune restriction horaire n'est appliquée</p>
|
||
<hr />
|
||
<h2 id="12-ciblage-par-centres-dinteret">12. Ciblage par centres d'intérêt</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne pour un garage automobile</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne les tags:</p>
|
||
<pre><code>| Tag |
|
||
|---|
|
||
| Automobile |
|
||
| Mécanique |
|
||
| Sport |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité est prioritaire pour les utilisateurs avec jauges élevées sur ces tags
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle peut quand même être diffusée à d'autres utilisateurs (ciblage non exclusif)</p>
|
||
<hr />
|
||
<h2 id="13-classification-dage-obligatoire">13. Classification d'âge obligatoire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de valider sans sélectionner une tranche d'âge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Classification d'âge obligatoire"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les options proposées sont:</p>
|
||
<pre><code>| Option |
|
||
|---|
|
||
| Tout public |
|
||
| 13+ |
|
||
| 16+ |
|
||
| 18+ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="14-upload-audio-publicitaire-formats-acceptes">14. Upload audio publicitaire formats acceptés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'upload un fichier audio format MP3</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est accepté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'upload un fichier audio format AAC (.aac ou .m4a)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est accepté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'upload un fichier audio format WAV</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Format non supporté. Utilisez MP3 ou AAC"</p>
|
||
<hr />
|
||
<h2 id="15-duree-audio-publicitaire-validee">15. Durée audio publicitaire validée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'upload un audio de 8 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Durée minimale: 10 secondes"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'upload un audio de 65 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Durée maximale: 60 secondes"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'upload un audio de 30 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est accepté</p>
|
||
<hr />
|
||
<h2 id="16-prepaiement-obligatoire-via-mangopay">16. Prépaiement obligatoire via Mangopay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai configuré une campagne à 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'arrive à l'étape de paiement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois payer les 300€ avant validation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le paiement est traité via Mangopay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule la carte bancaire est acceptée</p>
|
||
<hr />
|
||
<h2 id="17-recharge-automatique-optionnelle">17. Recharge automatique optionnelle</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai une campagne active</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je configure la recharge automatique à 10% du budget</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> si le budget restant passe sous 30€ (10% de 300€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la campagne recharge automatiquement 100€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma carte bancaire est débitée de 100€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget total passe à 130€</p>
|
||
<hr />
|
||
<h2 id="18-desactivation-recharge-automatique">18. Désactivation recharge automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai activé la recharge automatique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive cette option</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune recharge ne se produit automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne s'arrête quand le budget atteint 0€</p>
|
||
<hr />
|
||
<h2 id="19-etalement-budget-sur-periode-longue">19. Étalement budget sur période longue</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une campagne avec:</p>
|
||
<pre><code>| Budget total | 1000€ |
|
||
|---|---|
|
||
| Durée | 30 j |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule l'étalement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le budget/jour est de 33.33€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le budget se consomme plus vite (ex: 50€/jour)
|
||
<span style="color: #4CAF50"><strong>Alors</strong></span> une alerte "Budget épuisé dans 10 jours" est envoyée</p>
|
||
<hr />
|
||
<h2 id="20-estimation-population-cible-selon-zone">20. Estimation population cible selon zone</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je sélectionne la zone "Marseille"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule la population cible</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'estimation affichée est "~15 000 utilisateurs potentiels"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message "Estimation basée sur utilisateurs actifs dans la zone" s'affiche</p>
|
||
<hr />
|
||
<h2 id="21-campagne-avec-date-de-debut-differee">21. Campagne avec date de début différée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis la date de début au 2026-03-01 (dans 1 mois)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne a le statut "Programmée"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle démarre automatiquement le 2026-03-01 à 00h00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget n'est pas consommé avant cette date</p>
|
||
<hr />
|
||
<h2 id="22-interface-self-service-accessible">22. Interface self-service accessible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un publicitaire</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à l'interface publicitaire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux créer une campagne sans contact commercial RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les options sont configurables en autonomie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un tutoriel guidé est disponible (première utilisation)</p>
|
||
<hr />
|
||
<h2 id="23-apercu-zone-ciblee-sur-carte-interactive">23. Aperçu zone ciblée sur carte interactive</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je configure une zone géographique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne "Département du Var"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une carte Leaflet affiche les limites du département en surbrillance
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un compteur "~50 000 utilisateurs actifs" est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux zoomer/dézoomer pour visualiser la zone</p>
|
||
<hr />
|
||
<h2 id="24-tags-multiples-pour-ciblage-affine">24. Tags multiples pour ciblage affiné</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne pour un restaurant</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sélectionne les tags:</p>
|
||
<pre><code>| Tag |
|
||
|---|
|
||
| Gastronomie |
|
||
| Tourisme |
|
||
| Famille |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité est prioritaire pour utilisateurs intéressés par ces 3 thèmes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le score de ciblage combine les 3 jauges d'intérêt</p>
|
||
<hr />
|
||
<h2 id="25-validation-des-dates-de-campagne">25. Validation des dates de campagne</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis une date de début postérieure à la date de fin</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Date de fin doit être après date de début"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne n'est pas créée</p>
|
||
<hr />
|
||
<h2 id="26-duree-minimale-de-campagne">26. Durée minimale de campagne</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis une durée de moins de 24 heures</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Durée minimale: 1 jour"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois ajuster les dates</p>
|
||
<hr />
|
||
<h2 id="27-duree-maximale-de-campagne">27. Durée maximale de campagne</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis une durée de plus de 90 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Durée maximale: 90 jours"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois ajuster les dates ou créer plusieurs campagnes</p>
|
||
<hr />
|
||
<h2 id="28-plan-calcul-budget-quotidien-selon-duree">28. 📋 Plan: Calcul budget quotidien selon durée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une campagne avec un budget de <budget>€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la durée est de <duree> jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le budget quotidien est de <budget_jour>€/jour</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>budget</th>
|
||
<th>duree</th>
|
||
<th>budget_jour</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>100</td>
|
||
<td>10</td>
|
||
<td>10.00</td>
|
||
</tr>
|
||
<tr>
|
||
<td>300</td>
|
||
<td>14</td>
|
||
<td>21.43</td>
|
||
</tr>
|
||
<tr>
|
||
<td>500</td>
|
||
<td>30</td>
|
||
<td>16.67</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1000</td>
|
||
<td>60</td>
|
||
<td>16.67</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="29-plan-estimation-diffusions-selon-budget">29. 📋 Plan: Estimation diffusions selon budget</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un budget quotidien de <budget_jour>€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le coût par écoute complète est 0.05€</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le nombre estimé de diffusions/jour est <diffusions></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>budget_jour</th>
|
||
<th>diffusions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>10.00</td>
|
||
<td>200</td>
|
||
</tr>
|
||
<tr>
|
||
<td>21.43</td>
|
||
<td>429</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50.00</td>
|
||
<td>1000</td>
|
||
</tr>
|
||
<tr>
|
||
<td>100.00</td>
|
||
<td>2000</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="30-plan-formats-audio-acceptesrejetes">30. 📋 Plan: Formats audio acceptés/rejetés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'upload un fichier <fichier></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le format est <format></p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le résultat est <resultat></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>fichier</th>
|
||
<th>format</th>
|
||
<th>resultat</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>pub.mp3</td>
|
||
<td>MP3</td>
|
||
<td>accepté</td>
|
||
</tr>
|
||
<tr>
|
||
<td>pub.aac</td>
|
||
<td>AAC</td>
|
||
<td>accepté</td>
|
||
</tr>
|
||
<tr>
|
||
<td>pub.m4a</td>
|
||
<td>AAC</td>
|
||
<td>accepté</td>
|
||
</tr>
|
||
<tr>
|
||
<td>pub.wav</td>
|
||
<td>WAV</td>
|
||
<td>rejeté</td>
|
||
</tr>
|
||
<tr>
|
||
<td>pub.ogg</td>
|
||
<td>OGG</td>
|
||
<td>rejeté</td>
|
||
</tr>
|
||
<tr>
|
||
<td>pub.flac</td>
|
||
<td>FLAC</td>
|
||
<td>rejeté</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="caracteristiques-et-facturation-des-publicites">Caractéristiques et facturation des publicités</h1>
|
||
<blockquote>
|
||
<p><em>En tant que système RoadWave</em>
|
||
<em>Je veux appliquer des règles précises de durée, skippabilité et facturation</em>
|
||
<em>Afin d'équilibrer expérience utilisateur et rentabilité publicitaire</em></p>
|
||
</blockquote>
|
||
<p><strong>32 scénarios</strong> (29 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur gratuit écoute du contenu</p>
|
||
</blockquote>
|
||
<h2 id="1-duree-minimale-10-secondes">1. Durée minimale 10 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un publicitaire uploade une publicité de 8 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système valide la durée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Durée minimale: 10 secondes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'upload est rejeté</p>
|
||
<hr />
|
||
<h2 id="2-duree-maximale-60-secondes">2. Durée maximale 60 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un publicitaire uploade une publicité de 65 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système valide la durée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Durée maximale: 60 secondes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'upload est rejeté</p>
|
||
<hr />
|
||
<h2 id="3-duree-recommandee-15-30-secondes">3. Durée recommandée 15-30 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un publicitaire crée une campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il voit les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message s'affiche:</p>
|
||
<hr />
|
||
<h2 id="4-publicite-de-10-secondes-acceptee">4. Publicité de 10 secondes acceptée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un publicitaire uploade une publicité de 10 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système valide la durée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est accepté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune erreur n'est affichée</p>
|
||
<hr />
|
||
<h2 id="5-publicite-de-60-secondes-acceptee">5. Publicité de 60 secondes acceptée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un publicitaire uploade une publicité de 60 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système valide la durée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier est accepté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un avertissement s'affiche: "⚠️ Durée longue: taux de skip potentiellement élevé"</p>
|
||
<hr />
|
||
<h2 id="6-delai-minimum-skippable-5-secondes-par-defaut">6. Délai minimum skippable 5 secondes par défaut</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30 secondes démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le délai minimal est configuré à 5 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 3 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" n'est pas visible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois attendre 2 secondes supplémentaires</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'atteins 5 secondes d'écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" apparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer pour passer au contenu suivant</p>
|
||
<hr />
|
||
<h2 id="7-delai-minimum-parametrable-admin-3-secondes">7. Délai minimum paramétrable admin (3 secondes)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'admin configure le délai à 3 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une publicité démarre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 3 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" apparaît immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux skipper</p>
|
||
<hr />
|
||
<h2 id="8-delai-minimum-parametrable-admin-10-secondes">8. Délai minimum paramétrable admin (10 secondes)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'admin configure le délai à 10 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une publicité démarre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 9 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" n'est toujours pas visible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'atteins 10 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" apparaît</p>
|
||
<hr />
|
||
<h2 id="9-facturation-ecoute-complete-80-005">9. Facturation écoute complète (>80%) - 0.05€</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30 secondes est diffusée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 25 secondes (83%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute est considérée comme "complète"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire est facturé 0.05€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur "écoutes complètes" s'incrémente</p>
|
||
<hr />
|
||
<h2 id="10-facturation-ecoute-complete-exactement-80">10. Facturation écoute complète exactement 80%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30 secondes est diffusée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant exactement 24 secondes (80%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute est considérée comme "complète"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire est facturé 0.05€</p>
|
||
<hr />
|
||
<h2 id="11-facturation-skip-apres-delai-minimal-002">11. Facturation skip après délai minimal - 0.02€</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30 secondes est diffusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le délai minimal est 5 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 10 secondes (33%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Passer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute est considérée comme "partielle"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire est facturé 0.02€</p>
|
||
<hr />
|
||
<h2 id="12-facturation-skip-immediat-5s-0">12. Facturation skip immédiat (<5s) - 0€</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30 secondes est diffusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le délai minimal est 5 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 3 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Suivant" (pas de bouton skip encore)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute est considérée comme "non engagée"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire n'est PAS facturé (0€)</p>
|
||
<hr />
|
||
<h2 id="13-comptabilisation-ecoute-complete-a-79">13. Comptabilisation écoute complète à 79%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30 secondes est diffusée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 23 secondes (77%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute est considérée comme "partielle" (pas complète)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire est facturé 0.02€</p>
|
||
<hr />
|
||
<h2 id="14-comptabilisation-ecoute-complete-a-100">14. Comptabilisation écoute complète à 100%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30 secondes est diffusée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute les 30 secondes complètes (100%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute est considérée comme "complète"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire est facturé 0.05€</p>
|
||
<hr />
|
||
<h2 id="15-budget-consomme-selon-mix-ecoutes">15. Budget consommé selon mix écoutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne à 300€ a généré:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule le budget consommé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le total est 240€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il reste 60€ de budget disponible</p>
|
||
<hr />
|
||
<h2 id="16-affichage-compteur-secondes-restantes">16. Affichage compteur secondes restantes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30s démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le délai minimal est 5s</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant 2 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un compteur s'affiche: "Passer dans 3s..."</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'atteins 5 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur disparaît
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton "Passer la publicité" s'affiche</p>
|
||
<hr />
|
||
<h2 id="17-progress-bar-publicite-visible">17. Progress bar publicité visible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30s est en lecture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 10 secondes se sont écoulées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la progress bar affiche 33% (10/30)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'indicateur temporel affiche "0:10 / 0:30"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur visualise la progression</p>
|
||
<hr />
|
||
<h2 id="18-message-publicite-clairement-affiche">18. Message "Publicité" clairement affiché</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité démarre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'audio commence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un badge "Publicité" est affiché en haut de l'écran
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la durée totale est indiquée: "Publicité (30s)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transparence est maximale (utilisateur sait que c'est une pub)</p>
|
||
<hr />
|
||
<h2 id="19-transition-fluide-apres-publicite">19. Transition fluide après publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30s se termine</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la lecture atteint 30 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le délai de transition de 2s démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu normal suivant est annoncé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'enchaînement est naturel (même UX que entre contenus)</p>
|
||
<hr />
|
||
<h2 id="20-like-autorise-sur-publicite">20. Like autorisé sur publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité est en lecture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le véhicule est à l'arrêt</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton cœur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un like explicite (+2%) est enregistré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes jauges d'intérêt sont mises à jour selon les tags de la pub
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire voit un compteur "Likes" incrémenté</p>
|
||
<hr />
|
||
<h2 id="21-abonnement-autorise-sur-publicite">21. Abonnement autorisé sur publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité est diffusée par un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le véhicule est à l'arrêt</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "S'abonner"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'abonnement est enregistré (+5% jauges)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire bénéficie de l'engagement fort
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela compte comme une conversion majeure</p>
|
||
<hr />
|
||
<h2 id="22-bouton-skip-visible-et-accessible">22. Bouton skip visible et accessible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité a dépassé le délai minimal</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le bouton "Passer" s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est positionné en bas à droite de l'écran
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il a une taille de clic confortable (44×44px minimum iOS)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est clairement visible (contraste élevé)</p>
|
||
<hr />
|
||
<h2 id="23-analytics-tracking-precis-par-type">23. Analytics tracking précis par type</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité est diffusée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un événement se produit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est tracké en temps réel:</p>
|
||
<pre><code>| É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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="24-recommandation-sweet-spot-15-30s">24. Recommandation sweet spot 15-30s</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> les statistiques RoadWave globales:</p>
|
||
<pre><code>| Durée pub | Taux complétion moyen |
|
||
|---|---|
|
||
| 10s | 65% |
|
||
| 15s | 55% |
|
||
| 30s | 45% |
|
||
| 45s | 30% |
|
||
| 60s | 20% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un publicitaire consulte les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le sweet spot affiché est "15-30 secondes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'explication est "Meilleur compromis engagement/message"</p>
|
||
<hr />
|
||
<h2 id="25-optimisation-duree-selon-taux-de-skip-campagne">25. Optimisation durée selon taux de skip campagne</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne de 60s a un taux de skip de 85%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le publicitaire consulte les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système suggère:</p>
|
||
<hr />
|
||
<h2 id="26-cout-effectif-moyen-cem-calcule">26. Coût effectif moyen (CEM) calculé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une campagne avec:</p>
|
||
<pre><code>| Type écoute | Nombre | Coût unitaire | Total |
|
||
|---|---|---|---|
|
||
| Complète | 2000 | 0.05€ | 100€ |
|
||
| Partielle | 3000 | 0.02€ | 60€ |
|
||
| Skip immédiat | 1000 | 0€ | 0€ |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule le coût effectif moyen</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> CEM = 160€ / 6000 impressions = 0.027€/impression
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette métrique aide à comparer avec CPM industrie</p>
|
||
<hr />
|
||
<h2 id="27-publicite-non-skippable-interdite">27. Publicité non skippable interdite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un publicitaire demande "Publicité non skippable"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il configure sa campagne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cette option n'existe pas
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les publicités sont obligatoirement skippables après 5s minimum</p>
|
||
<hr />
|
||
<h2 id="28-delai-minimal-jamais-3-secondes">28. Délai minimal jamais <3 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin essaie de configurer le délai à 2 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il valide le paramètre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Délai minimal: 3 secondes minimum"</p>
|
||
<hr />
|
||
<h2 id="29-delai-minimal-jamais-10-secondes">29. Délai minimal jamais >10 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un admin essaie de configurer le délai à 15 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il valide le paramètre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche: "Délai maximal: 10 secondes maximum"</p>
|
||
<hr />
|
||
<h2 id="30-plan-facturation-selon-duree-ecoutee">30. 📋 Plan: Facturation selon durée écoutée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité de 30s est diffusée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant <duree>s (<pourcentage>%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le type d'écoute est <type>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût facturé est <cout>€</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>duree</th>
|
||
<th>pourcentage</th>
|
||
<th>type</th>
|
||
<th>cout</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>10</td>
|
||
<td>skip immédiat</td>
|
||
<td>0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>17</td>
|
||
<td>partielle</td>
|
||
<td>0.02</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>33</td>
|
||
<td>partielle</td>
|
||
<td>0.02</td>
|
||
</tr>
|
||
<tr>
|
||
<td>20</td>
|
||
<td>67</td>
|
||
<td>partielle</td>
|
||
<td>0.02</td>
|
||
</tr>
|
||
<tr>
|
||
<td>24</td>
|
||
<td>80</td>
|
||
<td>complète</td>
|
||
<td>0.05</td>
|
||
</tr>
|
||
<tr>
|
||
<td>27</td>
|
||
<td>90</td>
|
||
<td>complète</td>
|
||
<td>0.05</td>
|
||
</tr>
|
||
<tr>
|
||
<td>30</td>
|
||
<td>100</td>
|
||
<td>complète</td>
|
||
<td>0.05</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="31-plan-budget-consomme-selon-distribution-ecoutes">31. 📋 Plan: Budget consommé selon distribution écoutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> <completes> écoutes complètes à 0.05€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> <partielles> écoutes partielles à 0.02€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> <skips> skips immédiats à 0€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule le budget total consommé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le résultat est <budget_total>€</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>completes</th>
|
||
<th>partielles</th>
|
||
<th>skips</th>
|
||
<th>budget_total</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1000</td>
|
||
<td>500</td>
|
||
<td>100</td>
|
||
<td>60</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2000</td>
|
||
<td>1000</td>
|
||
<td>500</td>
|
||
<td>120</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5000</td>
|
||
<td>2000</td>
|
||
<td>1000</td>
|
||
<td>290</td>
|
||
</tr>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>1000</td>
|
||
<td>0</td>
|
||
<td>20</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1000</td>
|
||
<td>0</td>
|
||
<td>0</td>
|
||
<td>50</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="32-plan-apparition-bouton-skip-selon-delai-configure">32. 📋 Plan: Apparition bouton skip selon délai configuré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le délai minimal est configuré à <delai>s</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute pendant <temps_ecoute>s</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le bouton "Passer" est <visible></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>delai</th>
|
||
<th>temps_ecoute</th>
|
||
<th>visible</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>3</td>
|
||
<td>non visible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>5</td>
|
||
<td>visible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>10</td>
|
||
<td>visible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>8</td>
|
||
<td>non visible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>10</td>
|
||
<td>visible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>2</td>
|
||
<td>non visible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>3</td>
|
||
<td>visible</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="gestion-du-budget-et-alertes-publicitaires">Gestion du budget et alertes publicitaires</h1>
|
||
<blockquote>
|
||
<p><em>En tant que publicitaire</em>
|
||
<em>Je veux suivre en temps réel mon budget et recevoir des alertes</em>
|
||
<em>Afin de maîtriser mes dépenses et optimiser mes campagnes</em></p>
|
||
</blockquote>
|
||
<p><strong>30 scénarios</strong> (27 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un compte publicitaire est connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une campagne active est en cours</p>
|
||
</blockquote>
|
||
<h2 id="1-dashboard-budget-temps-reel">1. Dashboard budget temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne a un budget de 300€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai consommé 220€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard budget</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| Métrique | Valeur |
|
||
|---|---|
|
||
| Budget total | 300€ |
|
||
| Budget consommé | 220€ |
|
||
| Budget restant | 80€ |
|
||
| Pourcentage | 73% consommé |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="2-jauge-visuelle-budget-consomme">2. Jauge visuelle budget consommé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai consommé 220€ sur 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une jauge de progression affiche 73%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la couleur est orange (seuil 50-80%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un indicateur "80€ restants" est affiché clairement</p>
|
||
<hr />
|
||
<h2 id="3-couleur-jauge-selon-seuil">3. Couleur jauge selon seuil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un budget de 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ai consommé 150€ (50%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la jauge est verte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ai consommé 240€ (80%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la jauge est orange</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ai consommé 285€ (95%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la jauge est rouge
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message "Budget presque épuisé" s'affiche</p>
|
||
<hr />
|
||
<h2 id="4-projection-epuisement-budget">4. Projection épuisement budget</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai consommé 220€ en 10 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il reste 4 jours de campagne</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule la projection</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la consommation quotidienne moyenne est 22€/jour
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la projection affiche "Budget épuisé dans 3.6 jours"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un avertissement "Campagne s'arrêtera avant la fin prévue" s'affiche</p>
|
||
<hr />
|
||
<h2 id="5-projection-avec-budget-suffisant">5. Projection avec budget suffisant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai consommé 100€ en 10 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il reste 4 jours de campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le budget total est 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule la projection</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la consommation quotidienne moyenne est 10€/jour
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la projection affiche "Budget suffisant pour toute la campagne"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget restant estimé à la fin est 160€</p>
|
||
<hr />
|
||
<h2 id="6-alerte-80-budget-consomme">6. Alerte 80% budget consommé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon budget est de 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consomme 240€ (80%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois immédiatement un email:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification push est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification in-app s'affiche</p>
|
||
<hr />
|
||
<h2 id="7-alerte-90-budget-consomme">7. Alerte 90% budget consommé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon budget est de 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consomme 270€ (90%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois immédiatement un email:</p>
|
||
<hr />
|
||
<h2 id="8-alerte-budget-epuise-100">8. Alerte budget épuisé (100%)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon budget est de 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consomme les 300€ (100%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois immédiatement un email:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne est automatiquement mise en pause
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> plus aucune diffusion ne se produit</p>
|
||
<hr />
|
||
<h2 id="9-pause-manuelle-de-campagne">9. Pause manuelle de campagne</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne est active
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il reste 150€ de budget</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Mettre en pause"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le statut passe à "En pause"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les diffusions s'arrêtent immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget de 150€ est conservé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux réactiver la campagne plus tard</p>
|
||
<hr />
|
||
<h2 id="10-reprise-campagne-pausee">10. Reprise campagne pausée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne est en pause
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il reste 150€ de budget</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Reprendre la campagne"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le statut passe à "Active"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les diffusions reprennent immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget restant de 150€ continue de se consommer</p>
|
||
<hr />
|
||
<h2 id="11-prolongation-campagne-avec-recharge">11. Prolongation campagne avec recharge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne se termine dans 2 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il reste 20€ de budget</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Prolonger la campagne"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ajoute 200€ supplémentaires</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le budget total passe à 220€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la date de fin peut être prolongée de 10 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un nouveau paiement Mangopay de 200€ est traité</p>
|
||
<hr />
|
||
<h2 id="12-recharge-automatique-activee">12. Recharge automatique activée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai configuré la recharge automatique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le seuil est fixé à 10% (30€ sur budget 300€)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le montant de recharge est 100€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le budget restant passe sous 30€</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une recharge automatique de 100€ est déclenchée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma carte bancaire est débitée via Mangopay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget total passe à budget_restant + 100€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de confirmation</p>
|
||
<hr />
|
||
<h2 id="13-echec-recharge-automatique-carte-expiree">13. Échec recharge automatique (carte expirée)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la recharge automatique est activée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma carte bancaire a expiré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le budget passe sous le seuil de 10%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la recharge automatique échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email urgent:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne continue jusqu'à épuisement du budget restant</p>
|
||
<hr />
|
||
<h2 id="14-modification-ciblage-si-budget-50-consomme">14. Modification ciblage si budget <50% consommé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai consommé 120€ sur 300€ (40%)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier le ciblage géographique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est autorisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le ciblage est mis à jour immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les nouvelles diffusions utilisent le nouveau ciblage</p>
|
||
<hr />
|
||
<h2 id="15-blocage-modification-ciblage-si-budget-50-consomme">15. Blocage modification ciblage si budget >50% consommé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai consommé 180€ sur 300€ (60%)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier le ciblage géographique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une erreur s'affiche:</p>
|
||
<hr />
|
||
<h2 id="16-modification-audio-necessite-nouvelle-validation">16. Modification audio nécessite nouvelle validation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne est active</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je veux modifier le fichier audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message s'affiche:</p>
|
||
<hr />
|
||
<h2 id="17-modification-plages-horaires-autorisee">17. Modification plages horaires autorisée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne cible 7h-9h et 17h-19h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie pour cibler 12h-14h aussi</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est appliquée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les diffusions suivantes incluent la nouvelle plage
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune re-validation n'est nécessaire</p>
|
||
<hr />
|
||
<h2 id="18-historique-consommation-budget-jour-par-jour">18. Historique consommation budget jour par jour</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne a duré 10 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un graphique avec:</p>
|
||
<pre><code>| Jour | Consommation | Cumulé |
|
||
|---|---|---|
|
||
| 1 | 22€ | 22€ |
|
||
| 2 | 25€ | 47€ |
|
||
| 3 | 20€ | 67€ |
|
||
| ... | ... | ... |
|
||
| 10 | 18€ | 220€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux identifier les pics de consommation</p>
|
||
<hr />
|
||
<h2 id="19-notification-fin-de-campagne-programmee">19. Notification fin de campagne programmée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne se termine le 14/02</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la date de fin est atteinte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email:</p>
|
||
<hr />
|
||
<h2 id="20-remboursement-budget-non-utilise">20. Remboursement budget non utilisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne avait 300€ de budget
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'elle s'est terminée avec 280€ consommés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la campagne se termine (date ou épuisement)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un remboursement de 20€ est initié via Mangopay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le délai est de 5-7 jours ouvrés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification de confirmation</p>
|
||
<hr />
|
||
<h2 id="21-aucun-remboursement-si-budget-entierement-consomme">21. Aucun remboursement si budget entièrement consommé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne avait 300€ de budget
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'elle s'est terminée avec 300€ consommés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la campagne se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun remboursement n'est initié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message final indique "Budget entièrement utilisé"</p>
|
||
<hr />
|
||
<h2 id="22-statistiques-comparatives-budget-vs-objectif">22. Statistiques comparatives budget vs objectif</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'avais défini un objectif de 5000 impressions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon budget était 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les statistiques finales</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| Métrique | Objectif | Réalisé | Écart |
|
||
|---|---|---|---|
|
||
| Impressions | 5000 | 6000 | +20% |
|
||
| Budget | 300€ | 280€ | -7% |
|
||
| Coût/impression | 0.06€ | 0.047€ | -22% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> une analyse "✅ Objectifs dépassés avec budget optimisé"</p>
|
||
<hr />
|
||
<h2 id="23-export-rapport-financier-detaille">23. Export rapport financier détaillé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux analyser mes dépenses</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Exporter rapport financier"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je télécharge un CSV avec:</p>
|
||
<pre><code>| Colonne |
|
||
|---|
|
||
| Date/Heure |
|
||
| Type écoute |
|
||
| Coût unitaire |
|
||
| Zone géographique |
|
||
| Utilisateur (anonyme) |
|
||
| Durée écoutée |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux l'importer dans Excel pour analyses</p>
|
||
<hr />
|
||
<h2 id="24-tableau-de-bord-multi-campagnes">24. Tableau de bord multi-campagnes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 3 campagnes actives</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la vue d'ensemble</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un tableau récapitulatif:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> un badge alerte rouge sur la campagne B</p>
|
||
<hr />
|
||
<h2 id="25-alerte-consolidee-multi-campagnes">25. Alerte consolidée multi-campagnes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 5 campagnes actives
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 2 campagnes ont >80% budget consommé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois les notifications</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un email consolidé unique est envoyé:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne reçois pas 2 emails séparés (évite spam)</p>
|
||
<hr />
|
||
<h2 id="26-configuration-seuils-alertes-personnalises">26. Configuration seuils alertes personnalisés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je configure mes préférences d'alerte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je définis les seuils:</p>
|
||
<pre><code>| Seuil | Valeur |
|
||
|---|---|
|
||
| Alerte 1 | 70% |
|
||
| Alerte 2 | 85% |
|
||
| Alerte 3 | 95% |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois des alertes à 70%, 85% et 95%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> non aux seuils par défaut 80%, 90%, 100%</p>
|
||
<hr />
|
||
<h2 id="27-desactivation-alertes-email">27. Désactivation alertes email</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je préfère uniquement les notifications in-app</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive les alertes email dans mes préférences</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois plus d'emails d'alerte budget
|
||
<span style="color: #F44336"><strong>Mais</strong></span> les notifications in-app continuent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les alertes critiques (échec paiement) sont toujours envoyées par email</p>
|
||
<hr />
|
||
<h2 id="28-plan-couleur-jauge-selon-pourcentage-consomme">28. 📋 Plan: Couleur jauge selon pourcentage consommé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un budget de 300€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ai consommé <montant>€ (<pourcentage>%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la couleur de la jauge est <couleur></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>montant</th>
|
||
<th>pourcentage</th>
|
||
<th>couleur</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>100</td>
|
||
<td>33</td>
|
||
<td>verte</td>
|
||
</tr>
|
||
<tr>
|
||
<td>150</td>
|
||
<td>50</td>
|
||
<td>verte</td>
|
||
</tr>
|
||
<tr>
|
||
<td>180</td>
|
||
<td>60</td>
|
||
<td>orange</td>
|
||
</tr>
|
||
<tr>
|
||
<td>240</td>
|
||
<td>80</td>
|
||
<td>orange</td>
|
||
</tr>
|
||
<tr>
|
||
<td>270</td>
|
||
<td>90</td>
|
||
<td>rouge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>285</td>
|
||
<td>95</td>
|
||
<td>rouge</td>
|
||
</tr>
|
||
<tr>
|
||
<td>300</td>
|
||
<td>100</td>
|
||
<td>rouge</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="29-plan-projection-epuisement-selon-consommation">29. 📋 Plan: Projection épuisement selon consommation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un budget de 300€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une consommation actuelle de <consomme>€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une durée écoulée de <jours_ecoules> jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule la consommation quotidienne moyenne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle est de <conso_jour>€/jour
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget sera épuisé dans <jours_restants> jours</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>consomme</th>
|
||
<th>jours_ecoules</th>
|
||
<th>conso_jour</th>
|
||
<th>jours_restants</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>100</td>
|
||
<td>5</td>
|
||
<td>20</td>
|
||
<td>10</td>
|
||
</tr>
|
||
<tr>
|
||
<td>200</td>
|
||
<td>10</td>
|
||
<td>20</td>
|
||
<td>5</td>
|
||
</tr>
|
||
<tr>
|
||
<td>150</td>
|
||
<td>10</td>
|
||
<td>15</td>
|
||
<td>10</td>
|
||
</tr>
|
||
<tr>
|
||
<td>270</td>
|
||
<td>12</td>
|
||
<td>22.5</td>
|
||
<td>1.3</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="30-plan-alertes-envoyees-selon-seuils">30. 📋 Plan: Alertes envoyées selon seuils</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un budget de 500€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consomme <montant>€ (<pourcentage>%)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une alerte <niveau></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>montant</th>
|
||
<th>pourcentage</th>
|
||
<th>niveau</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>350</td>
|
||
<td>70</td>
|
||
<td>aucune</td>
|
||
</tr>
|
||
<tr>
|
||
<td>400</td>
|
||
<td>80</td>
|
||
<td>alerte 80%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>450</td>
|
||
<td>90</td>
|
||
<td>alerte 90%</td>
|
||
</tr>
|
||
<tr>
|
||
<td>500</td>
|
||
<td>100</td>
|
||
<td>budget épuisé</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="insertion-et-frequence-des-publicites">Insertion et fréquence des publicités</h1>
|
||
<blockquote>
|
||
<p><em>En tant que système RoadWave</em>
|
||
<em>Je veux insérer les publicités de manière équilibrée et non intrusive</em>
|
||
<em>Afin de préserver l'expérience utilisateur tout en monétisant</em></p>
|
||
</blockquote>
|
||
<p><strong>31 scénarios</strong> (28 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur gratuit est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-frequence-par-defaut-1-pub-5-contenus">1. Fréquence par défaut 1 pub / 5 contenus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la fréquence par défaut est configurée à 1/5
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 5 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 1 publicité est insérée après le 5ème contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 10 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 2 publicités sont insérées (après les contenus 5 et 10)</p>
|
||
<hr />
|
||
<h2 id="2-aucune-publicite-pour-utilisateurs-premium">2. Aucune publicité pour utilisateurs Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 100 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune publicité n'est insérée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je bénéficie d'une expérience sans interruption publicitaire</p>
|
||
<hr />
|
||
<h2 id="3-frequence-parametrable-par-admin-13">3. Fréquence paramétrable par admin (1/3)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'admin configure la fréquence à 1/3
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 6 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 2 publicités sont insérées (après contenus 3 et 6)</p>
|
||
<hr />
|
||
<h2 id="4-frequence-parametrable-par-admin-110">4. Fréquence paramétrable par admin (1/10)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'admin configure la fréquence à 1/10
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute 20 contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 2 publicités sont insérées (après contenus 10 et 20)</p>
|
||
<hr />
|
||
<h2 id="5-jamais-dinterruption-dun-contenu-en-cours">5. Jamais d'interruption d'un contenu en cours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu de 10 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis à 5 minutes de lecture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une publicité devrait être insérée selon la fréquence</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système vérifie l'insertion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité attend la fin du contenu actuel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle s'insère pendant le délai de transition (2s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu n'est jamais interrompu</p>
|
||
<hr />
|
||
<h2 id="6-insertion-entre-deux-contenus-uniquement">6. Insertion entre deux contenus uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le contenu "A" se termine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le délai de transition de 2s démarre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte qu'une publicité doit être insérée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le message "Publicité (30s)" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publicité démarre après les 2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'enchaînement est naturel et fluide</p>
|
||
<hr />
|
||
<h2 id="7-rotation-limite-3-foisjour-par-utilisateur">7. Rotation limite 3 fois/jour par utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a entendu la publicité "A" 3 fois aujourd'hui</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne une nouvelle publicité à diffuser</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité "A" n'est plus éligible pour cet utilisateur aujourd'hui
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une autre publicité "B" est sélectionnée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela évite la saturation publicitaire</p>
|
||
<hr />
|
||
<h2 id="8-compteur-de-diffusions-par-pub-et-par-utilisateur">8. Compteur de diffusions par pub et par utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur écoute la pub "RestaurantX"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la diffusion se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un compteur Redis "pub:RestaurantX:user:123:count" s'incrémente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le TTL est de 24h (reset à minuit)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le compteur atteint 3</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la pub "RestaurantX" est exclue des prochaines sélections aujourd'hui</p>
|
||
<hr />
|
||
<h2 id="9-limite-max-6-pubsheure-par-utilisateur">9. Limite max 6 pubs/heure par utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a entendu 6 publicités dans la dernière heure</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système devrait insérer une 7ème pub</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'insertion est reportée à l'heure suivante
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un compteur horaire Redis "pub:user:123:hourly" est vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela évite le spam publicitaire</p>
|
||
<hr />
|
||
<h2 id="10-ciblage-geographique-prioritaire-point-gps">10. Ciblage géographique prioritaire - Point GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité cible un point GPS à 2km de ma position
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une autre publicité cible ma ville entière</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne une publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité point GPS est priorisée (score géo plus élevé)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le ciblage précis est favorisé</p>
|
||
<hr />
|
||
<h2 id="11-ciblage-geographique-prioritaire-hierarchie">11. Ciblage géographique prioritaire - Hiérarchie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 4 publicités sont éligibles:</p>
|
||
<pre><code>| Publicité | Zone | Distance |
|
||
|---|---|---|
|
||
| A | Point GPS | 1km |
|
||
| B | Ville | 0km |
|
||
| C | Département | 0km |
|
||
| D | National | N/A |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne selon priorité géographique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'ordre de priorité est: A > B > C > D
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publicité A (Point GPS, la plus précise) est diffusée</p>
|
||
<hr />
|
||
<h2 id="12-ciblage-centres-dinteret-secondaire">12. Ciblage centres d'intérêt secondaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 2 publicités ciblent ma zone géographique:</p>
|
||
<pre><code>| Publicité | Tags | Mes jauges |
|
||
|---|---|---|
|
||
| A | Automobile | 80% |
|
||
| B | Voyage | 40% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système applique le score centres d'intérêt</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité A est favorisée (meilleur match jauges)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le ciblage thématique affine la sélection</p>
|
||
<hr />
|
||
<h2 id="13-ciblage-horaire-strict">13. Ciblage horaire strict</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne cible uniquement 7h-9h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il est 10h30</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne une publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cette campagne n'est PAS éligible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les campagnes "toute la journée" ou avec plage horaire actuelle sont considérées</p>
|
||
<hr />
|
||
<h2 id="14-ciblage-horaire-pendant-plage-active">14. Ciblage horaire pendant plage active</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne cible 7h-9h et 17h-19h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il est 8h15</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne une publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cette campagne est éligible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle peut être diffusée</p>
|
||
<hr />
|
||
<h2 id="15-normalisation-volume-audio-14-lufs">15. Normalisation volume audio -14 LUFS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité est uploadée avec volume trop élevé (-6 LUFS)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système encode l'audio via FFmpeg</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le volume est normalisé automatiquement à -14 LUFS
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire reçoit une notification "Volume audio ajusté pour conformité"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela évite l'effet "pub trop forte" frustrant</p>
|
||
<hr />
|
||
<h2 id="16-validation-volume-audio-lors-encodage">16. Validation volume audio lors encodage</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité est soumise</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> FFmpeg encode le fichier</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une commande loudnorm est appliquée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier final respecte le standard broadcast -14 LUFS</p>
|
||
<hr />
|
||
<h2 id="17-selection-aleatoire-si-criteres-equivalents">17. Sélection aléatoire si critères équivalents</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 3 publicités ont le même score géo
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'elles ont toutes des jauges centres d'intérêt équivalentes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucune n'a été diffusée 3 fois aujourd'hui</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne une publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une sélection aléatoire équitable est faite
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque campagne a 33% de chances d'être diffusée</p>
|
||
<hr />
|
||
<h2 id="18-exclusion-publicites-avec-budget-epuise">18. Exclusion publicités avec budget épuisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne "A" a épuisé son budget
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une campagne "B" a encore du budget disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne une publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seule la campagne "B" est éligible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne "A" est automatiquement exclue</p>
|
||
<hr />
|
||
<h2 id="19-exclusion-publicites-hors-dates-de-campagne">19. Exclusion publicités hors dates de campagne</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne "A" est programmée du 01/02 au 14/02
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que nous sommes le 20/01</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne une publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne "A" n'est pas éligible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les campagnes actives aujourd'hui sont considérées</p>
|
||
<hr />
|
||
<h2 id="20-publicite-visible-uniquement-dans-zone-geographique">20. Publicité visible uniquement dans zone géographique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité cible "Marseille uniquement"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis à Lyon</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système sélectionne une publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cette publicité n'est jamais éligible pour moi
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne la verrai jamais tant que je reste à Lyon</p>
|
||
<hr />
|
||
<h2 id="21-tracking-compteur-horaire-avec-ttl">21. Tracking compteur horaire avec TTL</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur entend une pub à 10h05</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le compteur horaire est incrémenté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la clé Redis "pub:user:123:hourly:2026012110" est créée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le TTL est de 1 heure (expire à 11h05)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système compte les pubs dans la fenêtre glissante d'1h</p>
|
||
<hr />
|
||
<h2 id="22-reset-compteur-quotidien-a-minuit">22. Reset compteur quotidien à minuit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a entendu la pub "A" 3 fois le 20/01</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> minuit passe et on est le 21/01</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur "pub:A:user:123:count" est expiré (TTL 24h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut à nouveau entendre la pub "A" jusqu'à 3 fois</p>
|
||
<hr />
|
||
<h2 id="23-aucune-pub-si-aucune-campagne-eligible">23. Aucune pub si aucune campagne éligible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'aucune campagne n'a de budget disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système devrait insérer une publicité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune pub n'est insérée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'enchaînement de contenus continue normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le prochain contenu démarre directement</p>
|
||
<hr />
|
||
<h2 id="24-priorisation-campagnes-avec-budget-important-restant">24. Priorisation campagnes avec budget important restant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 2 campagnes sont éligibles:</p>
|
||
<pre><code>| Campagne | Budget restant | Jours restants |
|
||
|---|---|---|
|
||
| A | 500€ | 2j |
|
||
| B | 50€ | 10j |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système applique la priorisation budgétaire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne A est légèrement favorisée (urgence dépense)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela aide à épuiser les budgets avant fin de campagne</p>
|
||
<hr />
|
||
<h2 id="25-log-des-selections-pour-analytics">25. Log des sélections pour analytics</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité "RestaurantX" est sélectionnée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> elle est diffusée à l'utilisateur "123"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un événement est loggé en base:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> cela permet l'analytics publicitaire</p>
|
||
<hr />
|
||
<h2 id="26-detection-changement-statut-utilisateur-gratuit-premium">26. Détection changement statut utilisateur (gratuit → premium)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'entends des publicités</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je souscris à Premium</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système détecte le changement de statut immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> plus aucune publicité n'est insérée dès le prochain contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon expérience devient sans pub instantanément</p>
|
||
<hr />
|
||
<h2 id="27-interface-admin-pour-ajuster-frequence-globale">27. Interface admin pour ajuster fréquence globale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis admin RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède aux paramètres publicitaires</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux ajuster le curseur de fréquence:</p>
|
||
<pre><code>| Option | Fréquence |
|
||
|---|---|
|
||
| 1/3 | Haute (agressif) |
|
||
| 1/5 | Standard (défaut) |
|
||
| 1/7 | Modérée |
|
||
| 1/10 | Faible |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le changement s'applique en temps réel à tous les utilisateurs</p>
|
||
<hr />
|
||
<h2 id="28-ab-testing-frequence-sur-cohortes-utilisateurs">28. A/B testing fréquence sur cohortes utilisateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'admin active un test A/B</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 50% des utilisateurs ont fréquence 1/5
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 50% des utilisateurs ont fréquence 1/7</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métriques sont trackées séparément:</p>
|
||
<pre><code>| Cohorte | Fréquence | Taux désabonnement | Revenus/user |
|
||
|---|---|---|---|
|
||
| A | 1/5 | 2.5% | 0.50€ |
|
||
| B | 1/7 | 1.8% | 0.40€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> l'admin peut identifier la fréquence optimale</p>
|
||
<hr />
|
||
<h2 id="29-plan-insertion-publicite-selon-frequence">29. 📋 Plan: Insertion publicité selon fréquence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la fréquence est <frequence></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute <contenus> contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> <pubs> publicités sont insérées</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>frequence</th>
|
||
<th>contenus</th>
|
||
<th>pubs</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1/3</td>
|
||
<td>9</td>
|
||
<td>3</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/5</td>
|
||
<td>10</td>
|
||
<td>2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/5</td>
|
||
<td>25</td>
|
||
<td>5</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/7</td>
|
||
<td>14</td>
|
||
<td>2</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1/10</td>
|
||
<td>30</td>
|
||
<td>3</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="30-plan-priorite-geographique-selon-type-zone">30. 📋 Plan: Priorité géographique selon type zone</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité cible <type_zone></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule le score géographique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la priorité est <score></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>type_zone</th>
|
||
<th>score</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Point GPS</td>
|
||
<td>1.0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Ville</td>
|
||
<td>0.8</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Département</td>
|
||
<td>0.6</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Région</td>
|
||
<td>0.4</td>
|
||
</tr>
|
||
<tr>
|
||
<td>National</td>
|
||
<td>0.2</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="31-plan-exclusion-publicite-selon-compteur-quotidien">31. 📋 Plan: Exclusion publicité selon compteur quotidien</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité a été entendue <fois> fois aujourd'hui</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système vérifie l'éligibilité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publicité est <eligible></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>fois</th>
|
||
<th>eligible</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>éligible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1</td>
|
||
<td>éligible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2</td>
|
||
<td>éligible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>non éligible</td>
|
||
</tr>
|
||
<tr>
|
||
<td>4</td>
|
||
<td>non éligible</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="metriques-dengagement-et-dashboard-publicitaire">Métriques d'engagement et dashboard publicitaire</h1>
|
||
<blockquote>
|
||
<p><em>En tant que publicitaire</em>
|
||
<em>Je veux consulter des métriques détaillées en temps réel</em>
|
||
<em>Afin d'optimiser mes campagnes et mesurer leur ROI</em></p>
|
||
</blockquote>
|
||
<p><strong>27 scénarios</strong> (24 standards, 3 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un compte publicitaire est connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une campagne active est en cours</p>
|
||
</blockquote>
|
||
<h2 id="1-dashboard-temps-reel-avec-metriques-essentielles">1. Dashboard temps réel avec métriques essentielles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne a généré 1000 diffusions</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les métriques suivantes mises à jour en temps réel:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="2-calcul-impressions-totales">2. Calcul impressions totales</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma publicité a été diffusée 2500 fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur "Impressions" affiche 2500
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il s'incrémente en temps réel à chaque nouvelle diffusion</p>
|
||
<hr />
|
||
<h2 id="3-calcul-ecoutes-completes-80">3. Calcul écoutes complètes (>80%)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma publicité de 30s a été:</p>
|
||
<pre><code>| Durée écoutée | Nombre |
|
||
|---|---|
|
||
| 25s (83%) | 300 |
|
||
| 20s (67%) | 200 |
|
||
| 10s (33%) | 150 |
|
||
| 5s (17%) | 50 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les écoutes complètes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur affiche 300 (uniquement ≥80%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le taux d'écoute complète est de 43% (300/700)</p>
|
||
<hr />
|
||
<h2 id="4-calcul-taux-de-skip">4. Calcul taux de skip</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> 1000 diffusions totales
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 400 écoutes complètes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le taux de skip</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il affiche 60% ((1000-400)/1000)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est calculé comme (total - complètes) / total</p>
|
||
<hr />
|
||
<h2 id="5-duree-moyenne-decoute-calculee">5. Durée moyenne d'écoute calculée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma publicité de 30s a été écoutée:</p>
|
||
<pre><code>| Durée | Nombre d'utilisateurs |
|
||
|---|---|
|
||
| 30s | 400 |
|
||
| 20s | 300 |
|
||
| 10s | 200 |
|
||
| 5s | 100 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la durée moyenne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le calcul est: (30×400 + 20×300 + 10×200 + 5×100) / 1000
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le résultat affiché est 21s</p>
|
||
<hr />
|
||
<h2 id="6-metriques-de-likes-sur-publicite">6. Métriques de likes sur publicité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 50 utilisateurs ont liké ma publicité</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur "Likes" affiche 50
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un taux de like de 5% est calculé (50/1000 impressions)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela indique une forte appréciation du contenu</p>
|
||
<hr />
|
||
<h2 id="7-metriques-dabonnements-generes">7. Métriques d'abonnements générés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 10 utilisateurs se sont abonnés après avoir entendu ma pub</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur "Abonnements" affiche 10
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un taux de conversion de 1% est calculé (10/1000)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela représente un engagement très fort</p>
|
||
<hr />
|
||
<h2 id="8-calcul-cout-par-ecoute-cpe">8. Calcul coût par écoute (CPE)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai dépensé 200€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> obtenu 4000 écoutes complètes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le coût par écoute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le CPE affiché est 0.05€ (200/4000)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il correspond au tarif standard RoadWave</p>
|
||
<hr />
|
||
<h2 id="9-repartition-geographique-avec-heatmap">9. Répartition géographique avec heatmap</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne cible le département du Var
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai 1000 diffusions réparties:</p>
|
||
<pre><code>| Zone | Diffusions | Pourcentage |
|
||
|---|---|---|
|
||
| Toulon | 400 | 40% |
|
||
| Hyères | 250 | 25% |
|
||
| Fréjus | 200 | 20% |
|
||
| Autres | 150 | 15% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la heatmap géographique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une carte Leaflet affiche les zones avec intensité proportionnelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Toulon apparaît en rouge foncé (forte concentration)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les autres villes en dégradé orange/jaune</p>
|
||
<hr />
|
||
<h2 id="10-repartition-horaire-avec-graphique">10. Répartition horaire avec graphique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne cible les plages 7h-9h et 17h-19h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai 1000 diffusions:</p>
|
||
<pre><code>| Plage horaire | Diffusions |
|
||
|---|---|
|
||
| 7h-8h | 300 |
|
||
| 8h-9h | 250 |
|
||
| 17h-18h | 280 |
|
||
| 18h-19h | 170 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le graphique horaire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un histogramme Chart.js affiche les 4 barres
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux identifier que 7h-8h est le pic d'écoute
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> optimiser mes futures campagnes sur cette plage</p>
|
||
<hr />
|
||
<h2 id="11-taux-de-completion-par-tranche-dage">11. Taux de complétion par tranche d'âge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne est Tout Public
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai des écoutes sur différentes tranches:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte l'analyse par âge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois que les 25-34 ans ont le meilleur taux (50%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cibler cette tranche pour mes prochaines campagnes</p>
|
||
<hr />
|
||
<h2 id="12-comparatif-de-campagnes-ab-testing">12. Comparatif de campagnes A/B testing</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 2 campagnes actives:</p>
|
||
<pre><code>| Campagne | Budget | Écoutes complètes | Taux | CPE |
|
||
|---|---|---|---|---|
|
||
| A | 300€ | 4000 | 40% | 0.075€ |
|
||
| B | 300€ | 6000 | 60% | 0.05€ |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le comparatif</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois que la campagne B performe mieux
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le tableau recommande "Campagne B: +50% écoutes, -33% CPE"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux allouer plus de budget à la campagne B</p>
|
||
<hr />
|
||
<h2 id="13-export-donnees-csv-pour-analyse-externe">13. Export données CSV pour analyse externe</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux analyser mes données dans Excel</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Exporter CSV"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je télécharge un fichier avec les colonnes:</p>
|
||
<pre><code>| Colonne |
|
||
|---|
|
||
| Date |
|
||
| Heure |
|
||
| Zone géographique |
|
||
| Tranche d'âge |
|
||
| Durée écoute |
|
||
| Skip (Oui/Non) |
|
||
| Like (Oui/Non) |
|
||
| Abonnement (Oui/Non) |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux faire des analyses personnalisées</p>
|
||
<hr />
|
||
<h2 id="14-export-graphiques-interactifs">14. Export graphiques interactifs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte le dashboard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur un graphique Chart.js</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux zoomer/filtrer interactivement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux exporter le graphique en PNG
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'image est en haute résolution pour présentations</p>
|
||
<hr />
|
||
<h2 id="15-rapport-pdf-automatique-fin-de-campagne">15. Rapport PDF automatique fin de campagne</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne de 14 jours se termine</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la date de fin est atteinte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un rapport PDF est généré automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il contient:</p>
|
||
<pre><code>| Section |
|
||
|---|
|
||
| Résumé exécutif |
|
||
| Métriques clés |
|
||
| Graphiques de performance |
|
||
| Heatmap géographique |
|
||
| Répartition horaire |
|
||
| Analyse tranches d'âge |
|
||
| Recommandations optimisation |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email avec le PDF en pièce jointe</p>
|
||
<hr />
|
||
<h2 id="16-metriques-temps-reel-rafraichies-automatiquement">16. Métriques temps réel rafraîchies automatiquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte le dashboard à 10h00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une nouvelle diffusion se produit à 10h01</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métriques sont rafraîchies automatiquement (polling 30s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois les nouveaux chiffres sans recharger la page
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge "Mis à jour il y a 15s" s'affiche</p>
|
||
<hr />
|
||
<h2 id="17-alertes-performance-personnalisees">17. Alertes performance personnalisées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je configure une alerte "Taux de skip >70%"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma campagne atteint 72% de skip</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le seuil est dépassé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email d'alerte:</p>
|
||
<hr />
|
||
<h2 id="18-benchmark-vs-moyennes-roadwave">18. Benchmark vs moyennes RoadWave</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne a 45% d'écoutes complètes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le benchmark</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "Votre taux: 45% | Moyenne RoadWave: 40%"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge "📊 Performance: +12% vs moyenne" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je sais que ma campagne performe au-dessus de la moyenne</p>
|
||
<hr />
|
||
<h2 id="19-cout-total-consomme-vs-budget">19. Coût total consommé vs budget</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un budget de 300€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai consommé 220€</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une jauge "Budget consommé: 73%" (220/300)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le montant restant "80€ restants"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une projection "Épuisé dans 3 jours à ce rythme"</p>
|
||
<hr />
|
||
<h2 id="20-repartition-couts-par-type-decoute">20. Répartition coûts par type d'écoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai dépensé 200€ avec:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la répartition</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un graphique camembert affiche:</p>
|
||
<pre><code>| Segment | Pourcentage |
|
||
|---|---|
|
||
| Écoutes complètes | 75% (150€) |
|
||
| Skips partiels | 20% (40€) |
|
||
| Skips immédiats | 5% (0€) |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="21-evolution-performance-dans-le-temps">21. Évolution performance dans le temps</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une campagne de 30 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le graphique d'évolution</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une courbe Chart.js avec:</p>
|
||
<pre><code>| Axe | Donnée |
|
||
|---|---|
|
||
| X | Jours (1-30) |
|
||
| Y | Taux d'écoute complète (%) |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux identifier les tendances (amélioration/dégradation)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les jours avec pics d'engagement</p>
|
||
<hr />
|
||
<h2 id="22-metriques-avancees-taux-de-reecoute">22. Métriques avancées - Taux de réécoute</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a entendu ma pub 3 fois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il l'a écoutée complètement les 3 fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les métriques avancées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le "Taux de réécoute" affiche 100%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela indique que le contenu n'est pas perçu comme spam
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs tolèrent bien la répétition</p>
|
||
<hr />
|
||
<h2 id="23-recommandations-automatiques-doptimisation">23. Recommandations automatiques d'optimisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne a un taux de skip de 75%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la durée moyenne d'écoute est de 8s sur 30s</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système suggère:</p>
|
||
<hr />
|
||
<h2 id="24-suivi-multi-campagnes-avec-vue-consolidee">24. Suivi multi-campagnes avec vue consolidée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 3 campagnes actives simultanément</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la vue consolidée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un tableau récapitulatif:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux comparer les performances d'un coup d'œil</p>
|
||
<hr />
|
||
<h2 id="25-plan-calcul-taux-decoute-complete">25. 📋 Plan: Calcul taux d'écoute complète</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> <total> diffusions totales
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> <completes> écoutes complètes (≥80%)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule le taux</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le résultat est <taux>%</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>total</th>
|
||
<th>completes</th>
|
||
<th>taux</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>1000</td>
|
||
<td>400</td>
|
||
<td>40</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2000</td>
|
||
<td>1200</td>
|
||
<td>60</td>
|
||
</tr>
|
||
<tr>
|
||
<td>500</td>
|
||
<td>100</td>
|
||
<td>20</td>
|
||
</tr>
|
||
<tr>
|
||
<td>1000</td>
|
||
<td>850</td>
|
||
<td>85</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="26-plan-calcul-cout-par-ecoute-cpe">26. 📋 Plan: Calcul coût par écoute (CPE)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un budget dépensé de <depense>€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> <ecoutes> écoutes complètes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je calcule le CPE</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le résultat est <cpe>€</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>depense</th>
|
||
<th>ecoutes</th>
|
||
<th>cpe</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>100</td>
|
||
<td>2000</td>
|
||
<td>0.05</td>
|
||
</tr>
|
||
<tr>
|
||
<td>300</td>
|
||
<td>6000</td>
|
||
<td>0.05</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>1000</td>
|
||
<td>0.05</td>
|
||
</tr>
|
||
<tr>
|
||
<td>500</td>
|
||
<td>10000</td>
|
||
<td>0.05</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="27-plan-classification-performance-vs-benchmark">27. 📋 Plan: Classification performance vs benchmark</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un taux d'écoute complète de <taux>%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une moyenne RoadWave de 40%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je compare à la moyenne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la performance est <classification></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>taux</th>
|
||
<th>classification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>60</td>
|
||
<td>Excellente (+50%)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>Bonne (+25%)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>40</td>
|
||
<td>Moyenne</td>
|
||
</tr>
|
||
<tr>
|
||
<td>30</td>
|
||
<td>Faible (-25%)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>20</td>
|
||
<td>Très faible (-50%)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="validation-et-moderation-des-publicites">Validation et modération des publicités</h1>
|
||
<blockquote>
|
||
<p><em>En tant que modérateur RoadWave</em>
|
||
<em>Je veux valider manuellement toutes les publicités avant diffusion</em>
|
||
<em>Afin de garantir la qualité et la légalité des contenus publicitaires</em></p>
|
||
</blockquote>
|
||
<p><strong>29 scénarios</strong> (27 standards, 2 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un modérateur RoadWave est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-validation-manuelle-obligatoire-avant-diffusion">1. Validation manuelle obligatoire avant diffusion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un publicitaire a créé une campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le paiement de 300€ a été effectué</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la campagne est soumise</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle passe en statut "En attente de validation"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est ajoutée à la file d'attente des modérateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la diffusion ne démarre PAS avant validation manuelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire reçoit un email "Votre campagne est en cours de validation (24-48h)"</p>
|
||
<hr />
|
||
<h2 id="2-delai-de-validation-24-48h-ouvrees">2. Délai de validation 24-48h ouvrées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne est soumise le lundi 10h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur la valide le mardi 15h</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le délai est de 29h (dans les 48h ouvrées)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire reçoit une notification "Votre campagne est approuvée"</p>
|
||
<hr />
|
||
<h2 id="3-validation-depassant-48h-avec-notification">3. Validation dépassant 48h avec notification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne est soumise le lundi 10h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 48h ouvrées se sont écoulées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la campagne n'est toujours pas validée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le publicitaire reçoit un email automatique:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un modérateur senior est assigné automatiquement</p>
|
||
<hr />
|
||
<h2 id="4-acceptation-de-campagne-publicitaire">4. Acceptation de campagne publicitaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne est en attente de validation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'audio respecte toutes les règles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur clique sur "Approuver"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le statut passe à "Approuvée"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne démarre à la date programmée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire reçoit un email de confirmation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le budget commence à être consommé dès le début</p>
|
||
<hr />
|
||
<h2 id="5-refus-de-campagne-avec-motif-detaille">5. Refus de campagne avec motif détaillé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne contient du contenu alcool</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur clique sur "Refuser"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il sélectionne le motif "Contenu interdit: Alcool"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il ajoute le commentaire "La publicité pour l'alcool est interdite en France"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le statut passe à "Refusée"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire reçoit un email détaillé avec:</p>
|
||
<pre><code>| Champ | Valeur |
|
||
|---|---|
|
||
| Motif | Contenu interdit: Alcool |
|
||
| Commentaire | La publicité pour l'alcool est interdite en France |
|
||
| Action requise | Modifier votre contenu et soumettre à nouveau |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> un remboursement automatique de 300€ est déclenché</p>
|
||
<hr />
|
||
<h2 id="6-remboursement-automatique-apres-refus">6. Remboursement automatique après refus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne à 500€ est refusée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le statut passe à "Refusée"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un remboursement Mangopay de 500€ est initié automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le délai de remboursement est de 5-7 jours ouvrés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire reçoit un email "Remboursement en cours"</p>
|
||
<hr />
|
||
<h2 id="7-contenus-interdits-alcool">7. Contenus interdits - Alcool</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité mentionne "Whisky premium 40°"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit refuser la campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sélectionner le motif "Contenu interdit: Alcool"</p>
|
||
<hr />
|
||
<h2 id="8-contenus-interdits-tabac">8. Contenus interdits - Tabac</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité mentionne "Cigarettes électroniques"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit refuser la campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sélectionner le motif "Contenu interdit: Tabac/Vape"</p>
|
||
<hr />
|
||
<h2 id="9-contenus-interdits-jeux-dargent">9. Contenus interdits - Jeux d'argent</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité mentionne "Gagnez 10 000€ - Paris sportifs"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit refuser la campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sélectionner le motif "Contenu interdit: Jeux d'argent"</p>
|
||
<hr />
|
||
<h2 id="10-contenus-interdits-politique-pendant-campagne-electorale">10. Contenus interdits - Politique pendant campagne électorale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité politique est soumise
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que nous sommes en période de campagne électorale officielle</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit refuser la campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sélectionner le motif "Contenu interdit: Publicité politique (période électorale)"</p>
|
||
<hr />
|
||
<h2 id="11-contenus-interdits-contenu-sexuel">11. Contenus interdits - Contenu sexuel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité contient des propos sexuellement explicites</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit refuser la campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sélectionner le motif "Contenu interdit: Contenu sexuel"</p>
|
||
<hr />
|
||
<h2 id="12-contenus-interdits-violence">12. Contenus interdits - Violence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité contient des descriptions violentes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit refuser la campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sélectionner le motif "Contenu interdit: Violence"</p>
|
||
<hr />
|
||
<h2 id="13-contenu-legal-autorise-commerce-local">13. Contenu légal autorisé - Commerce local</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité pour un restaurant local dit "Découvrez notre menu du jour"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit approuver la campagne</p>
|
||
<hr />
|
||
<h2 id="14-contenu-legal-autorise-service-professionnel">14. Contenu légal autorisé - Service professionnel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité pour un garage dit "Révision complète à partir de 99€"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit approuver la campagne</p>
|
||
<hr />
|
||
<h2 id="15-criteres-de-validation-qualite-audio">15. Critères de validation - Qualité audio</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité a une qualité audio très basse (bruits, saturation)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut refuser avec le motif "Qualité audio insuffisante"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> recommander "Veuillez soumettre un fichier audio de meilleure qualité"</p>
|
||
<hr />
|
||
<h2 id="16-criteres-de-validation-classification-dage-correcte">16. Critères de validation - Classification d'âge correcte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité contient du langage familier
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'elle est classée "Tout public"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il peut refuser avec le motif "Classification d'âge incorrecte"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> recommander "Reclasser en 13+ minimum"</p>
|
||
<hr />
|
||
<h2 id="17-criteres-de-validation-respect-reglementation-francaise">17. Critères de validation - Respect réglementation française</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité fait des promesses mensongères "Perdez 10kg en 1 semaine"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur écoute l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il doit refuser avec le motif "Non-conformité réglementaire: Publicité mensongère"</p>
|
||
<hr />
|
||
<h2 id="18-file-dattente-moderation-priorisee">18. File d'attente modération priorisée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 10 campagnes sont en attente de validation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la campagne A a été soumise il y a 40h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la campagne B a été soumise il y a 2h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur consulte sa file</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la campagne A apparaît en premier (priorité temporelle)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un badge "Urgente - >40h" est affiché</p>
|
||
<hr />
|
||
<h2 id="19-dashboard-moderation-vue-densemble">19. Dashboard modération - Vue d'ensemble</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis modérateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède au dashboard modération publicités</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="20-transcription-automatique-pour-aide-moderation">20. Transcription automatique pour aide modération</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité audio est soumise</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système traite l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une transcription automatique est générée via Whisper
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est affichée au modérateur pour faciliter la revue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle permet une recherche par mots-clés (alcool, tabac, etc.)</p>
|
||
<hr />
|
||
<h2 id="21-detection-automatique-mots-cles-interdits">21. Détection automatique mots-clés interdits</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité audio est soumise</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la transcription contient "whisky" ou "vodka"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un flag automatique "⚠️ Alcool détecté" est ajouté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la campagne est priorisée pour validation manuelle rapide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le modérateur est alerté du contenu potentiellement interdit</p>
|
||
<hr />
|
||
<h2 id="22-historique-moderation-publicitaire">22. Historique modération publicitaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un publicitaire a eu 2 campagnes refusées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il soumet une 3ème campagne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le modérateur voit l'historique:</p>
|
||
<pre><code>| Date | Statut | Motif |
|
||
|---|---|---|
|
||
| 2026-01-15 | Refusée | Contenu interdit: Alcool |
|
||
| 2026-01-20 | Refusée | Qualité audio faible |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> il peut en tenir compte dans sa décision</p>
|
||
<hr />
|
||
<h2 id="23-appel-possible-apres-refus">23. Appel possible après refus</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma campagne a été refusée pour "Classification incorrecte"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je conteste la décision via le formulaire d'appel</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un modérateur senior revoit la campagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il peut approuver si la classification est en fait correcte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le délai de réponse est de 48-72h</p>
|
||
<hr />
|
||
<h2 id="24-notification-temps-reel-pour-moderateurs">24. Notification temps réel pour modérateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis modérateur connecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une nouvelle campagne est soumise</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification in-app
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur "Campagnes en attente" s'incrémente en temps réel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer pour consulter immédiatement</p>
|
||
<hr />
|
||
<h2 id="25-statistiques-conformite-par-categorie">25. Statistiques conformité par catégorie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis admin modération</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les statistiques mensuelles</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les motifs de refus:</p>
|
||
<pre><code>| Motif | Nombre | Pourcentage |
|
||
|---|---|---|
|
||
| Alcool | 15 | 30% |
|
||
| Qualité audio | 12 | 24% |
|
||
| Classification erronée | 10 | 20% |
|
||
| Publicité mensongère | 8 | 16% |
|
||
| Autres | 5 | 10% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="26-export-rapport-moderation">26. Export rapport modération</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis modérateur senior</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'exporte le rapport mensuel</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un fichier CSV avec:</p>
|
||
<pre><code>| Colonne |
|
||
|---|
|
||
| Campagne ID |
|
||
| Publicitaire |
|
||
| Date soumission |
|
||
| Date décision |
|
||
| Statut |
|
||
| Motif (si refus) |
|
||
| Modérateur |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je peux l'analyser dans Excel</p>
|
||
<hr />
|
||
<h2 id="27-validation-partielle-avec-demande-modification">27. Validation partielle avec demande modification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne a un contenu acceptable
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que la classification d'âge est incorrecte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur clique sur "Demander modification"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le publicitaire reçoit un email:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut devient "Modification requise"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le publicitaire peut modifier sans repayer</p>
|
||
<hr />
|
||
<h2 id="28-plan-contenus-interdits-automatiquement-detectes">28. 📋 Plan: Contenus interdits automatiquement détectés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une publicité contient le mot <mot_cle></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la transcription automatique est analysée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un flag <flag> est ajouté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le motif de refus suggéré est <motif></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>mot_cle</th>
|
||
<th>flag</th>
|
||
<th>motif</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>whisky</td>
|
||
<td>⚠️ Alcool</td>
|
||
<td>Contenu interdit: Alcool</td>
|
||
</tr>
|
||
<tr>
|
||
<td>vodka</td>
|
||
<td>⚠️ Alcool</td>
|
||
<td>Contenu interdit: Alcool</td>
|
||
</tr>
|
||
<tr>
|
||
<td>cigarette</td>
|
||
<td>⚠️ Tabac</td>
|
||
<td>Contenu interdit: Tabac</td>
|
||
</tr>
|
||
<tr>
|
||
<td>casino</td>
|
||
<td>⚠️ Jeux argent</td>
|
||
<td>Contenu interdit: Jeux</td>
|
||
</tr>
|
||
<tr>
|
||
<td>paris sportifs</td>
|
||
<td>⚠️ Jeux argent</td>
|
||
<td>Contenu interdit: Jeux</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="29-plan-delais-de-validation-selon-soumission">29. 📋 Plan: Délais de validation selon soumission</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une campagne est soumise <jour> à <heure></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> elle est validée <delai> heures plus tard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le statut est <conformite></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>jour</th>
|
||
<th>heure</th>
|
||
<th>delai</th>
|
||
<th>conformite</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Lundi</td>
|
||
<td>10h</td>
|
||
<td>24</td>
|
||
<td>Dans les délais (24h)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Lundi</td>
|
||
<td>10h</td>
|
||
<td>48</td>
|
||
<td>Dans les délais (48h)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Lundi</td>
|
||
<td>10h</td>
|
||
<td>50</td>
|
||
<td>Hors délais (>48h)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Vendredi</td>
|
||
<td>16h</td>
|
||
<td>72</td>
|
||
<td>Dans les délais (we)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="architecture-technique-radio-live">Architecture technique radio live</h1>
|
||
<blockquote>
|
||
<p><em>En tant que système</em>
|
||
<em>Je veux gérer efficacement les flux audio en temps réel</em>
|
||
<em>Afin d'assurer une diffusion stable et scalable des lives</em></p>
|
||
</blockquote>
|
||
<p><strong>24 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'infrastructure RoadWave est opérationnelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que les serveurs Go avec Pion WebRTC sont actifs</p>
|
||
</blockquote>
|
||
<h2 id="1-ingestion-webrtc-du-flux-createur">1. Ingestion WebRTC du flux créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur démarre un live depuis son application mobile</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le flux audio WebRTC (Opus 48 kbps) arrive sur le serveur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le serveur Go avec Pion WebRTC accepte la connexion
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le flux est traité en temps réel</p>
|
||
<hr />
|
||
<h2 id="2-conversion-temps-reel-opus-vers-segments-hls">2. Conversion temps réel Opus vers segments HLS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un flux WebRTC Opus est reçu par le serveur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le serveur traite le flux</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> FFmpeg convertit en segments HLS (.ts)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un fichier manifest .m3u8 est généré et mis à jour régulièrement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les segments ont une durée de 2 secondes chacun</p>
|
||
<hr />
|
||
<h2 id="3-distribution-via-bunny-cdn">3. Distribution via Bunny CDN</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les segments HLS sont générés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur demande à rejoindre le live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le manifest .m3u8 est servi via Bunny CDN
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les segments .ts sont cachés sur le CDN
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la distribution est globale avec latence minimale</p>
|
||
<hr />
|
||
<h2 id="4-lecture-hls-native-sur-mobile-ios">4. Lecture HLS native sur mobile iOS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un auditeur iOS rejoint un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application charge le flux HLS</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le player natif AVPlayer gère la lecture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer de 15 secondes est appliqué automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité s'adapte selon la connexion</p>
|
||
<hr />
|
||
<h2 id="5-lecture-hls-native-sur-mobile-android">5. Lecture HLS native sur mobile Android</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un auditeur Android rejoint un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application charge le flux HLS</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le player natif ExoPlayer gère la lecture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer de 15 secondes est configuré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité s'adapte selon la connexion</p>
|
||
<hr />
|
||
<h2 id="6-enregistrement-parallele-du-flux-pour-replay">6. Enregistrement parallèle du flux pour replay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live est en cours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un processus parallèle enregistre le flux Opus raw
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'enregistrement est stocké temporairement sur le serveur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'enregistrement est indépendant de la diffusion HLS</p>
|
||
<hr />
|
||
<h2 id="7-traitement-post-live-asynchrone">7. Traitement post-live asynchrone</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live vient de se terminer</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le processus post-live démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job asynchrone est créé dans la queue Redis
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un worker Go récupère le job
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le worker exécute FFmpeg pour les conversions</p>
|
||
<hr />
|
||
<h2 id="8-conversion-opus-raw-vers-mp3-256-kbps">8. Conversion Opus raw vers MP3 256 kbps</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un worker traite un job post-live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la conversion démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> FFmpeg convertit Opus raw en MP3 256 kbps
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la normalisation audio à -14 LUFS est appliquée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les silences prolongés (>3 secondes) sont détectés et nettoyés</p>
|
||
<hr />
|
||
<h2 id="9-generation-segments-hls-pour-le-replay">9. Génération segments HLS pour le replay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le MP3 256 kbps est généré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le worker crée les segments HLS</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> des segments .ts de 10 secondes sont créés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un manifest .m3u8 est généré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les segments sont uploadés vers le stockage Bunny</p>
|
||
<hr />
|
||
<h2 id="10-publication-automatique-du-replay">10. Publication automatique du replay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que tous les segments HLS sont uploadés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le worker finalise le job</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une entrée de contenu "replay" est créée en base PostgreSQL
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le titre est "[REPLAY] [Titre live original]"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le type géographique est "Géo-neutre"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le replay est immédiatement disponible pour les auditeurs</p>
|
||
<hr />
|
||
<h2 id="11-suppression-automatique-fichier-opus-raw-apres-7-jours">11. Suppression automatique fichier Opus raw après 7 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un replay est publié depuis 7 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de nettoyage quotidien s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier Opus raw est supprimé du stockage
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul le MP3 256 kbps et les segments HLS sont conservés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'espace de stockage est libéré</p>
|
||
<hr />
|
||
<h2 id="12-scalabilite-horizontale-des-workers-de-conversion">12. Scalabilité horizontale des workers de conversion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 50 lives se terminent simultanément</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les jobs post-live sont créés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les workers Go disponibles traitent les jobs en parallèle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si tous les workers sont occupés, les jobs attendent en queue Redis
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> de nouveaux workers peuvent être lancés automatiquement (Kubernetes)</p>
|
||
<hr />
|
||
<h2 id="13-limitation-du-nombre-de-lives-simultanes-mvp">13. Limitation du nombre de lives simultanés (MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'infrastructure MVP est configurée pour 100 lives simultanés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 100 lives sont actuellement en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un nouveau créateur essaie de démarrer un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la demande est refusée avec le code erreur 503
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes" est retourné
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la demande peut être mise en queue prioritaire si créateur Premium</p>
|
||
<hr />
|
||
<h2 id="14-monitoring-des-ressources-serveur-en-temps-reel">14. Monitoring des ressources serveur en temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que plusieurs lives sont en cours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système monitore en temps réel:</p>
|
||
<pre><code>| métrique | seuil alerte |
|
||
|---|---|
|
||
| CPU utilisation | >80% |
|
||
| Mémoire utilisation | >85% |
|
||
| Bande passante upload | >80% capacité |
|
||
| Nombre connexions WebRTC | >90 |
|
||
| Latence moyenne CDN | >200ms |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> si un seuil est dépassé, une alerte est envoyée à l'équipe technique</p>
|
||
<hr />
|
||
<h2 id="15-calcul-du-cout-de-bande-passante-cdn">15. Calcul du coût de bande passante CDN</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live a 100 auditeurs simultanés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la qualité est 48 kbps Opus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le live dure 1 heure</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la bande passante totale est d'environ 2.16 GB
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût Bunny CDN est d'environ 0.02€ (tarif ~0.01€/GB)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ces métriques sont enregistrées pour facturation créateur si nécessaire</p>
|
||
<hr />
|
||
<h2 id="16-cache-cdn-des-segments-hls">16. Cache CDN des segments HLS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live est diffusé via Bunny CDN</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un segment .ts est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le segment est uploadé vers Bunny origin
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> Bunny CDN cache le segment sur ses edge servers
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les auditeurs suivants récupèrent le segment depuis le cache
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la charge sur le serveur origin est réduite de ~90%</p>
|
||
<hr />
|
||
<h2 id="17-gestion-de-la-latence-webrtc-createur">17. Gestion de la latence WebRTC créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur diffuse avec une connexion 4G</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la latence réseau augmente ponctuellement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le buffer côté serveur absorbe les fluctuations
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité peut être réduite temporairement (48 kbps → 32 kbps)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un warning est affiché au créateur si la connexion est trop instable</p>
|
||
<hr />
|
||
<h2 id="18-detection-automatique-de-la-musique-protegee-post-mvp">18. Détection automatique de la musique protégée (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live contient de la musique en arrière-plan</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système d'audio fingerprint analyse le flux</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une empreinte audio est calculée toutes les 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'empreinte est comparée à une base de données de contenus protégés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si une correspondance est trouvée, un warning est envoyé au créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le créateur ne corrige pas sous 30 secondes, le live peut être arrêté</p>
|
||
<hr />
|
||
<h2 id="19-stockage-des-metadonnees-de-live-en-postgresql">19. Stockage des métadonnées de live en PostgreSQL</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un créateur démarre un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les métadonnées suivantes sont enregistrées:</p>
|
||
<pre><code>| 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" |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces données sont indexées pour recherche et analytics</p>
|
||
<hr />
|
||
<h2 id="20-cache-redis-pour-compteurs-temps-reel">20. Cache Redis pour compteurs temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live est en cours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Redis stocke les compteurs temps réel:</p>
|
||
<pre><code>| clé Redis | valeur exemple |
|
||
|---|---|
|
||
| live:[live_id]:listeners | 247 |
|
||
| live:[live_id]:likes | 89 |
|
||
| live:[live_id]:reports | 0 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces compteurs sont mis à jour toutes les 2 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les compteurs sont persistés en PostgreSQL toutes les 60 secondes</p>
|
||
<hr />
|
||
<h2 id="21-heartbeat-auditeurs-pour-compteur-precis">21. Heartbeat auditeurs pour compteur précis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un auditeur écoute un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'application envoie un heartbeat toutes les 10 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le heartbeat met à jour le timestamp dans Redis
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si aucun heartbeat n'est reçu pendant 30 secondes, l'auditeur est retiré du compteur</p>
|
||
<hr />
|
||
<h2 id="22-gestion-des-pannes-serveur-pendant-un-live">22. Gestion des pannes serveur pendant un live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live est en cours sur serveur A</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le serveur A tombe en panne</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Kubernetes redémarre automatiquement un pod
|
||
<span style="color: #F44336"><strong>Mais</strong></span> le live en cours est perdu (pas de failover temps réel en MVP)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur voit le message "Connexion perdue. Veuillez redémarrer le live"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les auditeurs voient "Le live est terminé suite à un problème technique"</p>
|
||
<hr />
|
||
<h2 id="23-backup-automatique-des-enregistrements-live">23. Backup automatique des enregistrements live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live est enregistré en Opus raw</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'enregistrement dépasse 10 minutes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un backup incrémental est créé toutes les 10 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le backup est stocké sur un stockage secondaire (S3-compatible)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> en cas de crash serveur, le live peut être récupéré jusqu'au dernier backup</p>
|
||
<hr />
|
||
<h2 id="24-logs-et-audit-trail-des-lives">24. Logs et audit trail des lives</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un live démarre, se déroule et se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les événements sont loggés:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces logs sont conservés 90 jours pour analytics et conformité RGPD</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="arret-du-live">Arrêt du live</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur</em>
|
||
<em>Je veux arrêter ma diffusion en direct de manière contrôlée</em>
|
||
<em>Afin de terminer proprement mon live et générer un replay automatiquement</em></p>
|
||
</blockquote>
|
||
<p><strong>19 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant que créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je diffuse actuellement un live</p>
|
||
</blockquote>
|
||
<h2 id="1-arret-manuel-avec-compte-a-rebours-5-secondes">1. Arrêt manuel avec compte à rebours 5 secondes</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur le bouton "Arrêter live"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un compte à rebours de 5 secondes démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Ce live se termine dans 5... 4... 3... 2... 1"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Annuler" est affiché pendant le décompte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'audio du compte à rebours est diffusé aux auditeurs</p>
|
||
<hr />
|
||
<h2 id="2-annulation-du-compte-a-rebours">2. Annulation du compte à rebours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai appuyé sur "Arrêter live"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le compte à rebours affiche "3 secondes"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Annuler"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compte à rebours s'arrête
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le live continue normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune notification n'est envoyée aux auditeurs</p>
|
||
<hr />
|
||
<h2 id="3-arret-effectif-apres-compte-a-rebours">3. Arrêt effectif après compte à rebours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le compte à rebours est à 0</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live s'arrête
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la diffusion aux auditeurs se termine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message "Live terminé" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le processus de traitement post-live démarre automatiquement</p>
|
||
<hr />
|
||
<h2 id="4-deconnexion-createur-courte-moins-de-60-secondes">4. Déconnexion créateur courte (moins de 60 secondes)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ma connexion est perdue pendant 30 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les auditeurs voient le message "Connexion créateur perdue, reconnexion en cours..."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le live continue de bufferer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> quand ma connexion revient, le live reprend normalement</p>
|
||
<hr />
|
||
<h2 id="5-deconnexion-createur-longue-60-secondes-ou-plus">5. Déconnexion créateur longue (60 secondes ou plus)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ma connexion est perdue pendant 60 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live s'arrête automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les auditeurs voient le message "Le live est terminé suite à une coupure de connexion"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le processus de traitement post-live démarre</p>
|
||
<hr />
|
||
<h2 id="6-enregistrement-automatique-pendant-le-live">6. Enregistrement automatique pendant le live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon flux audio est enregistré en continu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le format d'enregistrement est Opus raw
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'enregistrement est stocké temporairement sur le serveur</p>
|
||
<hr />
|
||
<h2 id="7-generation-automatique-du-replay-apres-arret">7. Génération automatique du replay après arrêt</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live vient de se terminer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'option "Publier replay automatiquement" est activée (par défaut)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le traitement post-live démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job asynchrone est créé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le job effectue les opérations suivantes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="8-publication-du-replay">8. Publication du replay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le traitement post-live est terminé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le replay est publié automatiquement sous 5 à 10 minutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le titre est "[REPLAY] [Titre live original]"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la zone de diffusion est la même que le live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les tags sont identiques au live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la classification d'âge est identique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le type géographique est "Géo-neutre" (contenu pérenne)</p>
|
||
<hr />
|
||
<h2 id="9-notification-de-disponibilite-du-replay-aux-auditeurs">9. Notification de disponibilité du replay aux auditeurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le replay de mon live est publié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur qui a écouté le live se reconnecte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit une notification in-app "Le replay de [Titre] est disponible"</p>
|
||
<hr />
|
||
<h2 id="10-option-desactivation-publication-automatique-replay">10. Option désactivation publication automatique replay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je configure un nouveau live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive l'option "Publier replay automatiquement"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je démarre puis arrête le live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live est enregistré
|
||
<span style="color: #F44336"><strong>Mais</strong></span> le replay n'est pas publié automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux décider manuellement de le publier plus tard</p>
|
||
<hr />
|
||
<h2 id="11-suppression-manuelle-du-replay-apres-publication">11. Suppression manuelle du replay après publication</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live a généré un replay publié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à mes contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le replay dans ma liste
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux le supprimer comme n'importe quel contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je supprime le replay</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier source Opus raw est supprimé immédiatement</p>
|
||
<hr />
|
||
<h2 id="12-conservation-fichier-source-opus-raw">12. Conservation fichier source Opus raw</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live est terminé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le replay est publié</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier Opus raw est conservé pendant 7 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 7 jours, le fichier raw est supprimé automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul le MP3 256 kbps est conservé</p>
|
||
<hr />
|
||
<h2 id="13-modification-du-replay-interdite">13. Modification du replay interdite</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live a généré un replay publié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier l'audio du replay</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'action est refusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></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"><strong>Et</strong></span> je peux uniquement modifier les métadonnées (titre, description)</p>
|
||
<hr />
|
||
<h2 id="14-statistiques-du-live-disponibles-apres-arret">14. Statistiques du live disponibles après arrêt</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live est terminé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède aux statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="15-live-termine-avec-signalements-en-cours">15. Live terminé avec signalements en cours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live a reçu 3 signalements pendant la diffusion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le live se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le replay n'est pas publié automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu est en attente de modération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Votre replay sera publié après vérification suite aux signalements reçus"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un modérateur doit valider ou refuser le replay sous 24h</p>
|
||
<hr />
|
||
<h2 id="16-arret-force-par-un-moderateur">16. Arrêt forcé par un modérateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un modérateur détecte du contenu interdit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur clique sur "Arrêter le live immédiatement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live s'arrête sans compte à rebours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Votre live a été interrompu par la modération"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification détaillant la raison
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le replay n'est pas publié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier source est conservé 30 jours pour appel</p>
|
||
<hr />
|
||
<h2 id="17-metriques-de-bande-passante-pendant-le-live">17. Métriques de bande passante pendant le live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 100 auditeurs écoutent simultanément</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la bande passante consommée est d'environ 4.8 Mbps via CDN
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût estimé Bunny CDN est d'environ 0.02€ par heure de diffusion
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux voir ces métriques en temps réel dans l'interface créateur</p>
|
||
<hr />
|
||
<h2 id="18-live-sans-auditeurs-pendant-5-minutes">18. Live sans auditeurs pendant 5 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun auditeur n'écoute depuis 5 minutes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un message d'information "Aucun auditeur actuellement connecté"
|
||
<span style="color: #F44336"><strong>Mais</strong></span> le live continue normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir de continuer ou d'arrêter</p>
|
||
<hr />
|
||
<h2 id="19-qualite-audio-du-replay-superieure-au-live">19. Qualité audio du replay supérieure au live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live était diffusé en Opus 48 kbps</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le replay est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le replay est encodé en MP3 256 kbps
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la qualité audio du replay est supérieure au live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la taille du fichier est optimisée pour le stockage long terme</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="comportement-auditeur-pendant-un-live">Comportement auditeur pendant un live</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'auditeur</em>
|
||
<em>Je veux écouter des lives de manière stable</em>
|
||
<em>Afin de profiter du contenu en temps réel sans coupures</em></p>
|
||
</blockquote>
|
||
<p><strong>27 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant qu'auditeur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un créateur diffuse actuellement un live</p>
|
||
</blockquote>
|
||
<h2 id="1-rejoindre-un-live-avec-buffer-de-synchronisation-15-secondes">1. Rejoindre un live avec buffer de synchronisation 15 secondes</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Rejoindre le live"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la connexion au flux HLS s'établit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je commence à écouter avec un décalage de 15 secondes par rapport au créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer de 15 secondes garantit une lecture stable</p>
|
||
<hr />
|
||
<h2 id="2-justification-du-buffer-15-secondes">2. Justification du buffer 15 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> les alternatives de buffer possibles:</p>
|
||
<pre><code>| 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 | ❌ |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le buffer optimal est 15 secondes</p>
|
||
<hr />
|
||
<h2 id="3-lecture-stable-sur-reseau-3g">3. Lecture stable sur réseau 3G</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis sur réseau 3G
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> des micro-coupures réseau surviennent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le buffer de 15 secondes absorbe les coupures
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la lecture continue sans interruption perceptible</p>
|
||
<hr />
|
||
<h2 id="4-lecture-stable-sur-reseau-4g">4. Lecture stable sur réseau 4G</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis sur réseau 4G
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la lecture est fluide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le buffer de 15 secondes prévient les coupures lors de changement de cellule</p>
|
||
<hr />
|
||
<h2 id="5-continuation-du-live-en-sortant-de-la-zone-geographique">5. Continuation du live en sortant de la zone géographique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live régional "Île-de-France"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis situé en Île-de-France</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me déplace et sors du département</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live continue de jouer normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux écouter jusqu'à la fin naturelle du live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après la fin du live, l'algorithme propose du contenu correspondant à ma nouvelle position</p>
|
||
<hr />
|
||
<h2 id="6-abonne-dans-la-zone-recoit-notification-push">6. Abonné dans la zone reçoit notification push</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "JeanDupont"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis situé en Île-de-France</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "JeanDupont" démarre un live en Île-de-France</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification push "🔴 JeanDupont est en direct : [Titre du live]"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> quand je tape sur la notification, l'app s'ouvre et le live démarre immédiatement</p>
|
||
<hr />
|
||
<h2 id="7-abonne-hors-zone-ne-recoit-pas-de-notification">7. Abonné hors zone ne reçoit pas de notification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis abonné au créateur "JeanDupont"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis situé à Lyon</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> "JeanDupont" démarre un live en Île-de-France</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de notification push
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cela évite la frustration de ne pas pouvoir écouter un live hors zone</p>
|
||
<hr />
|
||
<h2 id="8-decouverte-dun-live-via-lalgorithme-de-recommandation">8. Découverte d'un live via l'algorithme de recommandation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis dans la zone géographique du live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je navigue dans l'app avec "Suivant"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme propose un live en cours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'indicateur "🔴 EN DIRECT"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir de le rejoindre ou de passer au suivant</p>
|
||
<hr />
|
||
<h2 id="9-reconnexion-rapide-apres-coupure-reseau-moins-de-90-secondes">9. Reconnexion rapide après coupure réseau (moins de 90 secondes)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je perds ma connexion réseau pendant 45 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je retrouve ma connexion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reprends le live au moment actuel (pas au buffer ancien)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le saut temporel est transparent (pas de message d'erreur)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne rate que quelques secondes de contenu</p>
|
||
<hr />
|
||
<h2 id="10-reconnexion-longue-apres-coupure-reseau-90-secondes-ou-plus">10. Reconnexion longue après coupure réseau (90 secondes ou plus)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je perds ma connexion réseau pendant 90 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je retrouve ma connexion</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Live en cours perdu, passage au contenu suivant"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme propose automatiquement le contenu suivant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux manuellement revenir au live s'il est toujours en cours</p>
|
||
<hr />
|
||
<h2 id="11-interactions-disponibles-pendant-le-live-like">11. Interactions disponibles pendant le live - Like</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon véhicule est à l'arrêt</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton "❤️ Like"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le like est enregistré immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur de likes visible par le créateur s'incrémente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge d'intérêt pour les tags du live augmente de +2%</p>
|
||
<hr />
|
||
<h2 id="12-interactions-disponibles-pendant-le-live-abonnement">12. Interactions disponibles pendant le live - Abonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je ne suis pas encore abonné au créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton "S'abonner"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je m'abonne au créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma jauge d'intérêt pour tous les tags du créateur augmente de +5%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je recevrai des notifications pour ses prochains lives</p>
|
||
<hr />
|
||
<h2 id="13-interactions-disponibles-pendant-le-live-skip">13. Interactions disponibles pendant le live - Skip</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant" (ou commande au volant)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je quitte le live immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme propose le contenu suivant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si j'ai écouté moins de 10 secondes, ma jauge d'intérêt diminue de -0.5%</p>
|
||
<hr />
|
||
<h2 id="14-commande-precedent-desactivee-pendant-un-live">14. Commande Précédent désactivée pendant un live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent" (ou commande au volant)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> rien ne se passe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message d'information s'affiche brièvement "Précédent non disponible sur les lives"</p>
|
||
<hr />
|
||
<h2 id="15-chat-en-direct-desactive-decision-definitive">15. Chat en direct désactivé (décision définitive)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune interface de chat n'est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas envoyer de messages au créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas voir de messages d'autres auditeurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette fonctionnalité ne sera jamais implémentée</p>
|
||
<hr />
|
||
<h2 id="16-reactions-emoji-desactivees-decision-definitive">16. Réactions emoji désactivées (décision définitive)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune réaction emoji n'est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas envoyer d'emoji en temps réel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette fonctionnalité ne sera jamais implémentée</p>
|
||
<hr />
|
||
<h2 id="17-message-dinformation-sur-labsence-de-chat">17. Message d'information sur l'absence de chat</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute mon premier live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à l'interface du live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></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"><strong>Et</strong></span> ce bandeau n'apparaît qu'une seule fois (première expérience)</p>
|
||
<hr />
|
||
<h2 id="18-signalement-dun-live-en-cours">18. Signalement d'un live en cours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu me semble inapproprié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le bouton "Signaler"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les catégories de signalement:</p>
|
||
<pre><code>| catégorie |
|
||
|---|
|
||
| Haine et violence |
|
||
| Contenu sexuel |
|
||
| Illégalité |
|
||
| Droits d'auteur |
|
||
| Désinformation dangereuse |
|
||
| Harcèlement |
|
||
| Autre |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> quand je sélectionne une catégorie
|
||
<span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est envoyé en priorité selon la catégorie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un modérateur peut écouter le live en temps réel si besoin</p>
|
||
<hr />
|
||
<h2 id="19-statistiques-visibles-par-les-auditeurs-pendant-le-live">19. Statistiques visibles par les auditeurs pendant le live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les informations du live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<p><span style="color: #F44336"><strong>Mais</strong></span> je ne vois pas les likes ou autres métriques détaillées</p>
|
||
<hr />
|
||
<h2 id="20-compteur-dauditeurs-arrondi-pour-preserver-la-vie-privee">20. Compteur d'auditeurs arrondi pour préserver la vie privée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live avec exactement 247 auditeurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le nombre d'auditeurs</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois "~250 auditeurs" (arrondi à la dizaine supérieure)</p>
|
||
<hr />
|
||
<h2 id="21-qualite-audio-adaptative-pendant-le-live">21. Qualité audio adaptative pendant le live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ma connexion passe de 4G à 3G</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la qualité audio s'adapte automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je passe de 48 kbps à 24 kbps Opus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la transition est transparente sans coupure</p>
|
||
<hr />
|
||
<h2 id="22-consommation-de-donnees-pendant-un-live">22. Consommation de données pendant un live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live en qualité standard 48 kbps
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute pendant 1 heure</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> j'ai consommé environ 21.6 MB de données mobiles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette consommation est affichée dans les paramètres de l'app</p>
|
||
<hr />
|
||
<h2 id="23-lecture-du-replay-apres-la-fin-du-live">23. Lecture du replay après la fin du live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live depuis 30 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le créateur arrête le live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Le live est terminé. Le replay sera disponible dans quelques minutes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu suivant est automatiquement proposé après 2 secondes</p>
|
||
<hr />
|
||
<h2 id="24-notification-de-disponibilite-du-replay">24. Notification de disponibilité du replay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté un live jusqu'à la fin
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le replay est publié 8 minutes plus tard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je rouvre l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une notification in-app "Le replay de [Titre] est maintenant disponible"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux cliquer pour l'écouter immédiatement</p>
|
||
<hr />
|
||
<h2 id="25-aucune-publicite-pendant-un-live-pour-utilisateurs-gratuits">25. Aucune publicité pendant un live pour utilisateurs gratuits</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune publicité n'est insérée pendant le live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la publicité apparaît seulement entre le live et le contenu suivant</p>
|
||
<hr />
|
||
<h2 id="26-detection-de-contexte-voiture-pendant-un-live">26. Détection de contexte voiture pendant un live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse est supérieure à 10 km/h</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'interface tactile est désactivée pour la sécurité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les commandes au volant sont actives (Play/Pause/Suivant)</p>
|
||
<hr />
|
||
<h2 id="27-detection-de-contexte-pieton-pendant-un-live">27. Détection de contexte piéton pendant un live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse est inférieure à 5 km/h</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'interface tactile complète est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux liker, m'abonner, signaler via l'écran tactile</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="demarrage-dun-live">Démarrage d'un live</h1>
|
||
<blockquote>
|
||
<p><em>En tant que créateur</em>
|
||
<em>Je veux démarrer une diffusion en direct</em>
|
||
<em>Afin de partager du contenu audio en temps réel avec mes auditeurs</em></p>
|
||
</blockquote>
|
||
<p><strong>20 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant que créateur vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai les permissions de diffusion live</p>
|
||
</blockquote>
|
||
<h2 id="1-verifications-pre-live-reussies">1. Vérifications pré-live réussies</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma connexion upload est supérieure à 1 Mbps
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai autorisé l'accès au microphone
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai défini une zone de diffusion "Île-de-France"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance les vérifications pré-live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les vérifications sont validées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux démarrer le live</p>
|
||
<hr />
|
||
<h2 id="2-echec-pre-live-avec-connexion-insuffisante">2. Échec pré-live avec connexion insuffisante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma connexion upload est de 0.5 Mbps</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance les vérifications pré-live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un warning "Connexion insuffisante pour garantir une diffusion stable (minimum 1 Mbps)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir de continuer quand même ou d'annuler</p>
|
||
<hr />
|
||
<h2 id="3-echec-pre-live-sans-autorisation-microphone">3. Échec pré-live sans autorisation microphone</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai pas autorisé l'accès au microphone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de démarrer un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Accès au microphone requis pour démarrer un live"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis redirigé vers les paramètres système</p>
|
||
<hr />
|
||
<h2 id="4-echec-pre-live-sans-zone-de-diffusion-definie">4. Échec pré-live sans zone de diffusion définie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai pas défini de zone de diffusion</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de démarrer un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Veuillez définir une zone de diffusion avant de démarrer"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis redirigé vers le formulaire de configuration du live</p>
|
||
<hr />
|
||
<h2 id="5-demarrage-live-avec-buffer-15-secondes">5. Démarrage live avec buffer 15 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que toutes les vérifications pré-live sont validées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Démarrer live"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Live démarre dans 15s... Testez votre micro"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un compte à rebours de 15 secondes s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon flux audio est enregistré pendant ces 15 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le live n'est pas encore visible publiquement</p>
|
||
<hr />
|
||
<h2 id="6-live-devient-public-apres-buffer-initial">6. Live devient public après buffer initial</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai démarré un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le buffer de 15 secondes s'est écoulé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live devient public
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les auditeurs peuvent le rejoindre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les abonnés dans la zone reçoivent une notification push</p>
|
||
<hr />
|
||
<h2 id="7-notification-push-aux-abonnes-dans-la-zone-geographique">7. Notification push aux abonnés dans la zone géographique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 1000 abonnés au total
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 300 abonnés sont situés en Île-de-France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 700 abonnés sont situés hors Île-de-France</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon live en Île-de-France devient public</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 300 abonnés reçoivent une notification push "🔴 [Mon pseudo] est en direct : [Titre live]"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 700 abonnés ne reçoivent pas de notification</p>
|
||
<hr />
|
||
<h2 id="8-configuration-metadonnees-obligatoires-pour-un-live">8. Configuration métadonnées obligatoires pour un live</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je configure un nouveau live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois renseigner:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-validation-echouee-avec-titre-trop-court">9. Validation échouée avec titre trop court</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de créer un live avec le titre "Live"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validation échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Le titre doit contenir entre 5 et 100 caractères"</p>
|
||
<hr />
|
||
<h2 id="10-validation-echouee-sans-tags">10. Validation échouée sans tags</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai rempli tous les champs sauf les tags</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de démarrer le live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la validation échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Veuillez sélectionner entre 1 et 3 centres d'intérêt"</p>
|
||
<hr />
|
||
<h2 id="11-limite-de-duree-8-heures">11. Limite de durée 8 heures</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live dure depuis 7 heures et 30 minutes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un warning "Votre live se terminera dans 30 min"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message est affiché de manière non intrusive</p>
|
||
<hr />
|
||
<h2 id="12-arret-automatique-a-8-heures">12. Arrêt automatique à 8 heures</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live dure depuis 8 heures</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live s'arrête automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></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"><strong>Et</strong></span> le processus de traitement post-live démarre</p>
|
||
<hr />
|
||
<h2 id="13-diffusion-contenu-interdit-concert-en-direct">13. Diffusion contenu interdit - Concert en direct</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un concert en direct depuis une salle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un auditeur signale le contenu pour "Violation droits d'auteur"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un modérateur écoute le live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il confirme la violation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live est arrêté immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un Strike 2 (suspension 7 jours)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Votre live a été interrompu pour violation des droits d'auteur"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le replay n'est pas publié</p>
|
||
<hr />
|
||
<h2 id="14-diffusion-contenu-interdit-evenement-sportif-payant">14. Diffusion contenu interdit - Événement sportif payant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un match de football avec droits TV
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le contenu est détecté par l'IA audio fingerprint</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la détection est confirmée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live est arrêté immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un Strike 2 (suspension 7 jours)</p>
|
||
<hr />
|
||
<h2 id="15-diffusion-contenu-violent">15. Diffusion contenu violent</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse du contenu violent (agression physique)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 5 auditeurs signalent le contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un modérateur vérifie en temps réel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> confirme la violence</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le live est coupé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon compte est banni définitivement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les autorités sont notifiées</p>
|
||
<hr />
|
||
<h2 id="16-detection-musique-protegee-en-arriere-plan">16. Détection musique protégée en arrière-plan</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon live contient de la musique protégée en fond</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'IA audio fingerprint détecte la violation après 2 minutes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></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"><strong>Et</strong></span> j'ai 30 secondes pour corriger
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je ne corrige pas, le live est arrêté avec Strike 1</p>
|
||
<hr />
|
||
<h2 id="17-signalement-pendant-un-live">17. Signalement pendant un live</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je diffuse un live
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un auditeur clique sur "Signaler"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'auditeur sélectionne la catégorie "Harcèlement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le signalement est envoyé en priorité HAUTE
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un modérateur peut écouter le live en temps réel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le live continue pendant l'écoute de vérification</p>
|
||
<hr />
|
||
<h2 id="18-depassement-nombre-de-lives-simultanes-autorises-limite-plateforme">18. Dépassement nombre de lives simultanés autorisés (limite plateforme)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la plateforme héberge actuellement 2000 lives simultanés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que c'est la limite de l'infrastructure actuelle</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de démarrer un nouveau live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Capacité maximale atteinte. Veuillez réessayer dans quelques minutes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma demande est mise en file d'attente prioritaire si je suis créateur Premium</p>
|
||
<hr />
|
||
<h2 id="19-premier-live-dun-nouveau-createur">19. Premier live d'un nouveau créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'ai jamais diffusé de live auparavant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai moins de 3 contenus validés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de démarrer mon premier live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Les lives sont disponibles après validation de vos 3 premiers contenus"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton "Démarrer live" est désactivé</p>
|
||
<hr />
|
||
<h2 id="20-createur-avec-score-de-confiance-faible">20. Créateur avec score de confiance faible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 2 strikes actifs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de démarrer un live</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le message "Fonctionnalité live temporairement indisponible suite à vos sanctions"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois attendre la fin de ma suspension</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="recherche-de-contenu">Recherche de contenu</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur de RoadWave</em>
|
||
<em>Je veux rechercher des contenus audio par mots-clés, localisation et filtres</em>
|
||
<em>Afin de trouver facilement le contenu qui m'intéresse</em></p>
|
||
</blockquote>
|
||
<p><strong>55 scénarios</strong> (49 standards, 6 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application RoadWave est démarrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur "jean@example.com" est connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-recherche-full-text-basique">1. Recherche full-text basique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la base contient les contenus suivants:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur recherche "paris"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 2 résultats sont retournés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les résultats incluent "Balade à Paris"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les résultats incluent "Secrets de Montmartre"</p>
|
||
<hr />
|
||
<h2 id="2-recherche-avec-stemming-francais">2. Recherche avec stemming français</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu avec le titre "Voyage en Bretagne"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur recherche "voyages"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "Voyage en Bretagne" est trouvé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le stemming a transformé "voyages" en racine "voyag"</p>
|
||
<hr />
|
||
<h2 id="3-plan-stemming-francais-sur-differentes-formes">3. 📋 Plan: Stemming français sur différentes formes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu avec le mot "<mot_original>"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur recherche "<recherche>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est trouvé grâce au stemming français</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>mot_original</th>
|
||
<th>recherche</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>voyage</td>
|
||
<td>voyages</td>
|
||
</tr>
|
||
<tr>
|
||
<td>voyager</td>
|
||
<td>voyage</td>
|
||
</tr>
|
||
<tr>
|
||
<td>balades</td>
|
||
<td>balade</td>
|
||
</tr>
|
||
<tr>
|
||
<td>historique</td>
|
||
<td>histoire</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="4-recherche-avec-accents-ignores">4. Recherche avec accents ignorés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu avec le titre "Découverte de l'Élysée"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur recherche "decouverte elysee"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est trouvé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les accents sont normalisés automatiquement</p>
|
||
<hr />
|
||
<h2 id="5-champs-indexes-avec-ponderation">5. Champs indexés avec pondération</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> les contenus suivants:</p>
|
||
<pre><code>| titre | description | créateur | tags |
|
||
|---|---|---|---|
|
||
| Voyage Paris | Balade sympa | @user1 | Tourisme |
|
||
| Balade Lyon | Voyage en ville | @paris_guide | Voyage |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur recherche "paris"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> "Voyage Paris" est en première position
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> "@paris_guide" apparaît en second</p>
|
||
<hr />
|
||
<h2 id="6-ranking-par-pertinence-et-popularite">6. Ranking par pertinence et popularité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> les contenus suivants:</p>
|
||
<pre><code>| titre | écoutes | rang_texte |
|
||
|---|---|---|
|
||
| Balade Paris | 50000 | 0.8 |
|
||
| Paris la nuit | 1000 | 0.9 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur recherche "paris"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score final combine rang_texte × (1 + log(écoutes + 1))
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> "Balade Paris" est mieux classé grâce à sa popularité</p>
|
||
<hr />
|
||
<h2 id="7-autocomplete-pendant-la-frappe">7. Autocomplete pendant la frappe</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur commence à taper "par"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 3 caractères sont saisis</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> des suggestions apparaissent:</p>
|
||
<pre><code>| suggestion |
|
||
|---|
|
||
| paris |
|
||
| parc naturel |
|
||
| parvis notre-dame |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le top 5 des suggestions est affiché</p>
|
||
<hr />
|
||
<h2 id="8-historique-des-10-dernieres-recherches">8. Historique des 10 dernières recherches</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a effectué les recherches suivantes:</p>
|
||
<pre><code>| recherche | date |
|
||
|---|---|
|
||
| voyage paris | 2026-01-20 |
|
||
| audio-guide louvre | 2026-01-19 |
|
||
| podcast automobile | 2026-01-18 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur ouvre la barre de recherche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 10 dernières recherches sont affichées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elles sont triées par date décroissante</p>
|
||
<hr />
|
||
<h2 id="9-correction-automatique-si-aucun-resultat">9. Correction automatique si aucun résultat</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur recherche "ballade paris" (faute d'orthographe)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'aucun résultat n'est trouvé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page de résultats s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une suggestion "Essayez plutôt : balade paris" est affichée</p>
|
||
<hr />
|
||
<h2 id="10-recherches-populaires-suggerees">10. Recherches populaires suggérées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'aucun résultat n'est trouvé pour une recherche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> des suggestions populaires sont affichées:</p>
|
||
<pre><code>| suggestion |
|
||
|---|
|
||
| balade paris |
|
||
| audio-guide louvre |
|
||
| visite montmartre |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-saisie-dun-lieu-avec-autocomplete">11. Saisie d'un lieu avec autocomplete</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur ouvre le filtre "Lieu"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il tape "Louv"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Nominatim retourne des suggestions:</p>
|
||
<pre><code>| suggestion | type |
|
||
|---|---|
|
||
| Musée du Louvre, Paris | monument |
|
||
| Louvres, Val-d'Oise | commune |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="12-selection-dun-lieu-et-definition-du-rayon">12. Sélection d'un lieu et définition du rayon</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur sélectionne "Paris, France"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que les coordonnées sont (48.8566, 2.3522)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il définit un rayon de 50 km</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la recherche PostGIS utilise ST_DWithin avec 50000 mètres</p>
|
||
<hr />
|
||
<h2 id="13-plan-recherche-geographique-avec-differents-rayons">13. 📋 Plan: Recherche géographique avec différents rayons</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu à 30 km de Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur recherche autour de Paris avec un rayon de <rayon></p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est <résultat></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>rayon</th>
|
||
<th>résultat</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>20 km</td>
|
||
<td>non trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50 km</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>100 km</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="14-utilisation-de-autour-de-moi-gps-actuel">14. Utilisation de "Autour de moi" (GPS actuel)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur active le GPS
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que sa position est (48.8566, 2.3522)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il sélectionne "Autour de moi"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la recherche utilise ses coordonnées GPS actuelles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un rayon par défaut de 10 km est appliqué</p>
|
||
<hr />
|
||
<h2 id="15-curseur-de-rayon-avec-limites">15. Curseur de rayon avec limites</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur ouvre le curseur de rayon</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il ajuste le curseur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les valeurs disponibles vont de 5 km à 500 km
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la valeur s'affiche en temps réel "50 km"</p>
|
||
<hr />
|
||
<h2 id="16-affichage-de-la-distance-dans-les-resultats">16. Affichage de la distance dans les résultats</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une recherche géographique autour de Paris
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un contenu à 2.3 km de distance</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les résultats sont affichés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la distance "À 2.3 km" est indiquée pour chaque résultat</p>
|
||
<hr />
|
||
<h2 id="17-plan-tri-par-proximite-geographique">17. 📋 Plan: Tri par proximité géographique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> des contenus à différentes distances de Paris:</p>
|
||
<pre><code>| contenu | distance |
|
||
|---|---|
|
||
| Louvre Guide | 0.5 km |
|
||
| Tour Eiffel | 2.0 km |
|
||
| Versailles | 20 km |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur trie par "Proximité"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les résultats sont affichés dans l'ordre:</p>
|
||
<pre><code>| position | contenu |
|
||
|---|---|
|
||
| 1 | Louvre Guide |
|
||
| 2 | Tour Eiffel |
|
||
| 3 | Versailles |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="18-geocodage-avec-nominatim-mvp">18. Géocodage avec Nominatim (MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application est en phase MVP</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une requête de géocodage est effectuée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'API publique Nominatim est utilisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le rate limit de 1 req/s est respecté</p>
|
||
<hr />
|
||
<h2 id="19-geocodage-avec-fallback-mapbox">19. Géocodage avec fallback Mapbox</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Nominatim ne retourne aucun résultat</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application tente un fallback</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'API Mapbox Geocoding est utilisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût de 0.50€ / 1000 requêtes est appliqué</p>
|
||
<hr />
|
||
<h2 id="20-ouverture-du-panneau-de-filtres">20. Ouverture du panneau de filtres</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur est sur la page de recherche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "Filtres"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un panneau latéral s'ouvre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 7 catégories de filtres sont affichées:</p>
|
||
<pre><code>| catégorie |
|
||
|---|
|
||
| Type de contenu |
|
||
| Durée |
|
||
| Classification âge |
|
||
| Géo-pertinence |
|
||
| Tags |
|
||
| Date de publication |
|
||
| Abonnement |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="21-filtre-par-type-de-contenu-multi-selection">21. Filtre par type de contenu (multi-sélection)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur ouvre les filtres</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il sélectionne:</p>
|
||
<pre><code>| type |
|
||
|---|
|
||
| Contenu court |
|
||
| Audio-guide |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls ces types de contenus sont recherchés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les podcasts et radios live sont exclus</p>
|
||
<hr />
|
||
<h2 id="22-plan-filtre-par-duree">22. 📋 Plan: Filtre par durée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu de <durée> minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur filtre par "<tranche>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est <résultat></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>durée</th>
|
||
<th>tranche</th>
|
||
<th>résultat</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>3</td>
|
||
<td><5 min</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>5-15 min</td>
|
||
<td>non trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>5-15 min</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>20</td>
|
||
<td>15-30 min</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>45</td>
|
||
<td>>30 min</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="23-filtre-par-classification-age">23. Filtre par classification âge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> des contenus avec différentes classifications:</p>
|
||
<pre><code>| contenu | classification |
|
||
|---|---|
|
||
| Conte enfants | Tout public |
|
||
| Podcast news | 13+ |
|
||
| Débat politique | 16+ |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur filtre "Tout public"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seul "Conte enfants" est affiché</p>
|
||
<hr />
|
||
<h2 id="24-filtre-par-geo-pertinence">24. Filtre par géo-pertinence</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> des contenus avec différents types géo:</p>
|
||
<pre><code>| contenu | type_geo |
|
||
|---|---|
|
||
| Guide Louvre | Ancré |
|
||
| Podcast Paris | Contextuel |
|
||
| News nationales | Neutre |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur filtre "Ancré, Contextuel"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> "Guide Louvre" et "Podcast Paris" sont affichés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> "News nationales" est exclu</p>
|
||
<hr />
|
||
<h2 id="25-filtre-par-tags-multi-selection">25. Filtre par tags (multi-sélection)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> des contenus taggés:</p>
|
||
<pre><code>| contenu | tags |
|
||
|---|---|
|
||
| Voyage en Italie | Voyage, Gastronomie |
|
||
| Histoire de Rome | Voyage, Histoire |
|
||
| Économie italienne | Économie |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur sélectionne les tags "Voyage, Histoire"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> "Histoire de Rome" est en priorité (2 tags correspondants)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> "Voyage en Italie" est affiché (1 tag correspondant)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> "Économie italienne" est exclu</p>
|
||
<hr />
|
||
<h2 id="26-plan-filtre-par-date-de-publication">26. 📋 Plan: Filtre par date de publication</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu publié il y a <délai></p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur filtre par "<période>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est <résultat></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>délai</th>
|
||
<th>période</th>
|
||
<th>résultat</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>12 heures</td>
|
||
<td>Dernières 24h</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3 jours</td>
|
||
<td>Cette semaine</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>15 jours</td>
|
||
<td>Ce mois</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>8 mois</td>
|
||
<td>Cette année</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2 ans</td>
|
||
<td>Toutes dates</td>
|
||
<td>trouvé</td>
|
||
</tr>
|
||
<tr>
|
||
<td>2 ans</td>
|
||
<td>Cette année</td>
|
||
<td>non trouvé</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="27-filtre-par-type-dabonnement">27. Filtre par type d'abonnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> des contenus gratuits et Premium:</p>
|
||
<pre><code>| contenu | type |
|
||
|---|---|
|
||
| Balade Paris | Gratuit |
|
||
| Visite VIP Louvre | Premium |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur filtre "Premium uniquement 👑"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seul "Visite VIP Louvre" est affiché</p>
|
||
<hr />
|
||
<h2 id="28-combinaison-de-filtres-multiples-and-logic">28. Combinaison de filtres multiples (AND logic)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur applique les filtres:</p>
|
||
<pre><code>| filtre | valeur |
|
||
|---|---|
|
||
| Type | Audio-guide |
|
||
| Durée | 5-15 min |
|
||
| Tags | Voyage |
|
||
| Classification | Tout public |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la recherche est lancée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus respectant TOUS les critères sont affichés</p>
|
||
<hr />
|
||
<h2 id="29-reinitialisation-des-filtres">29. Réinitialisation des filtres</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a appliqué 5 filtres différents</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "Réinitialiser"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les filtres sont désactivés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la recherche affiche tous les résultats</p>
|
||
<hr />
|
||
<h2 id="30-sauvegarde-dune-recherche">30. Sauvegarde d'une recherche</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a appliqué plusieurs filtres</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur "💾 Sauvegarder cette recherche"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il entre le nom "Podcasts voyage Paris"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la recherche est sauvegardée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle apparaît dans l'onglet "Recherches sauvegardées"</p>
|
||
<hr />
|
||
<h2 id="31-limite-de-5-recherches-sauvegardees">31. Limite de 5 recherches sauvegardées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a déjà 5 recherches sauvegardées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il tente de sauvegarder une 6ème recherche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message d'erreur s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il doit supprimer une recherche existante avant d'en ajouter une nouvelle</p>
|
||
<hr />
|
||
<h2 id="32-notifications-pour-recherches-sauvegardees">32. Notifications pour recherches sauvegardées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une recherche sauvegardée "Podcasts voyage Paris"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur a activé les notifications</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 3 nouveaux contenus correspondants sont publiés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification "3 nouveaux contenus dans 'Podcasts voyage Paris'" est envoyée</p>
|
||
<hr />
|
||
<h2 id="33-plan-options-de-tri-des-resultats">33. 📋 Plan: Options de tri des résultats</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une recherche avec plusieurs résultats</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur sélectionne le tri "<option>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les résultats sont triés selon <algorithme></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>option</th>
|
||
<th>algorithme</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Pertinence</td>
|
||
<td>Score recherche × (1 + log(écoutes + 1))</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Popularité</td>
|
||
<td>Écoutes complètes derniers 30j DESC</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Récent</td>
|
||
<td>Date publication DESC</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Proximité</td>
|
||
<td>Distance GPS ASC (si recherche géo)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Durée</td>
|
||
<td>Durée audio ASC ou DESC</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="34-structure-dun-resultat-de-recherche">34. Structure d'un résultat de recherche</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un résultat de recherche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> chaque résultat contient:</p>
|
||
<pre><code>| é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 | ⋮ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="35-lazy-loading-des-images">35. Lazy loading des images</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une page avec 20 résultats de recherche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page se charge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules les 5 premières images sont chargées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les images suivantes se chargent au scroll</p>
|
||
<hr />
|
||
<h2 id="36-troncature-du-titre-sur-2-lignes-maximum">36. Troncature du titre sur 2 lignes maximum</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un contenu avec un titre de 120 caractères</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le résultat est affiché</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le titre est tronqué après 2 lignes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> "..." est ajouté à la fin</p>
|
||
<hr />
|
||
<h2 id="37-lien-cliquable-vers-le-profil-createur">37. Lien cliquable vers le profil créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> un résultat de recherche pour "@paris_stories"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur "@paris_stories"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est redirigé vers "https://roadwave.fr/@paris_stories"</p>
|
||
<hr />
|
||
<h2 id="38-menu-contextuel-dun-resultat">38. Menu contextuel d'un résultat [⋮]</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur clique sur [⋮] pour un résultat</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le menu s'ouvre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les actions suivantes sont disponibles:</p>
|
||
<pre><code>| action |
|
||
|---|
|
||
| Partager |
|
||
| Ajouter à une playlist |
|
||
| Télécharger (offline) |
|
||
| Signaler |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="39-pagination-avec-20-resultats-par-page">39. Pagination avec 20 résultats par page</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une recherche retournant 100 résultats</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 20 résultats sont chargés initialement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un indicateur "1-20 sur 100 résultats" est visible</p>
|
||
<hr />
|
||
<h2 id="40-infinite-scroll-automatique">40. Infinite scroll automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur scroll dans les résultats</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il atteint 80% de la page</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 20 résultats suivants sont chargés automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un loader est affiché pendant le chargement</p>
|
||
<hr />
|
||
<h2 id="41-bouton-fallback-charger-20-suivants">41. Bouton fallback "Charger 20 suivants"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'infinite scroll est désactivé (paramètres)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur atteint la fin de la page</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un bouton "Charger 20 suivants" est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les résultats se chargent au clic</p>
|
||
<hr />
|
||
<h2 id="42-basculement-entre-vue-liste-et-vue-carte">42. Basculement entre vue liste et vue carte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur est sur la page de résultats</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur le toggle "Liste / Carte"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la vue carte Leaflet s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les résultats sont affichés comme markers sur la carte</p>
|
||
<hr />
|
||
<h2 id="43-affichage-de-la-carte-leaflet">43. Affichage de la carte Leaflet</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la vue carte est activée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la carte se charge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la carte utilise les tuiles OpenStreetMap
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le centre est la position de recherche (ou GPS utilisateur)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le zoom initial montre tous les résultats</p>
|
||
<hr />
|
||
<h2 id="44-markers-cliquables-sur-la-carte">44. Markers cliquables sur la carte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 10 résultats sont affichés sur la carte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur clique sur un marker</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une popup s'affiche avec:</p>
|
||
<pre><code>| élément |
|
||
|---|
|
||
| Titre |
|
||
| Créateur |
|
||
| Durée |
|
||
| Distance |
|
||
| Bouton ▶️ Écouter |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="45-clustering-des-markers-proches">45. Clustering des markers proches</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 50 résultats sont très proches géographiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la carte est affichée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les markers proches sont groupés en clusters
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le nombre de contenus est affiché sur le cluster
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cluster se décompose au zoom</p>
|
||
<hr />
|
||
<h2 id="46-synchronisation-liste-carte">46. Synchronisation liste / carte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur est en vue carte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il clique sur un marker et écoute le contenu
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il rebascule en vue liste</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu écouté est marqué dans la liste
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la position de scroll est maintenue</p>
|
||
<hr />
|
||
<h2 id="47-index-postgresql-full-text-pour-performances">47. Index PostgreSQL full-text pour performances</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la base contient 100K contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une recherche full-text est effectuée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'index GIN sur to_tsvector est utilisé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la requête retourne en moins de 100ms</p>
|
||
<hr />
|
||
<h2 id="48-index-postgis-gist-pour-recherche-geo">48. Index PostGIS GIST pour recherche géo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une recherche géographique avec rayon 50 km</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la requête PostGIS ST_DWithin est exécutée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'index GIST sur la colonne location est utilisé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la requête retourne en moins de 50ms</p>
|
||
<hr />
|
||
<h2 id="49-index-composites-pour-filtres">49. Index composites pour filtres</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une recherche avec filtres multiples</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les filtres type, durée, âge, géo, date sont appliqués</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'index composite idx_content_filters est utilisé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les performances restent optimales</p>
|
||
<hr />
|
||
<h2 id="50-index-gin-pour-recherche-par-tags">50. Index GIN pour recherche par tags</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> une recherche filtrée par tags "Voyage, Histoire"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la requête est exécutée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'index GIN sur la colonne tags est utilisé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la recherche est performante même avec 500K contenus</p>
|
||
<hr />
|
||
<h2 id="51-aucun-resultat-trouve">51. Aucun résultat trouvé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur recherche "xyzabc123"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> aucun résultat n'est trouvé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message "Aucun résultat pour 'xyzabc123'" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> des suggestions de recherches populaires sont proposées</p>
|
||
<hr />
|
||
<h2 id="52-recherche-vide">52. Recherche vide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur clique sur "Rechercher" sans saisir de texte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la recherche est lancée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message "Veuillez entrer au moins 2 caractères" s'affiche</p>
|
||
<hr />
|
||
<h2 id="53-erreur-de-geocodage-nominatim">53. Erreur de géocodage Nominatim</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API Nominatim est indisponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur tente une recherche géographique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message "Service de localisation temporairement indisponible" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la recherche continue sans filtre géographique</p>
|
||
<hr />
|
||
<h2 id="54-gps-desactive-pour-autour-de-moi">54. GPS désactivé pour "Autour de moi"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'utilisateur a désactivé le GPS</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il sélectionne "Autour de moi"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message "Veuillez activer la localisation" s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Activer" ouvre les paramètres système</p>
|
||
<hr />
|
||
<h2 id="55-timeout-de-recherche-apres-10-secondes">55. Timeout de recherche après 10 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une recherche complexe est lancée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la requête dépasse 10 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la recherche est annulée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message "La recherche a pris trop de temps, veuillez réessayer" s'affiche</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="classification-de-geo-pertinence-des-contenus">Classification de géo-pertinence des contenus</h1>
|
||
<blockquote>
|
||
<p><em>En tant que plateforme de contenu géolocalisé</em>
|
||
<em>Je veux classifier les contenus selon leur pertinence géographique</em>
|
||
<em>Afin d'adapter l'algorithme de recommandation</em></p>
|
||
</blockquote>
|
||
<p><strong>10 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-createur-choisit-le-type-geo-ancre-pour-un-audio-guide">1. Créateur choisit le type géo-ancré pour un audio-guide</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur connecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un audio-guide de la Tour Eiffel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je choisis la classification "Géo-ancré"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est enregistré avec:</p>
|
||
<pre><code>| champ | valeur |
|
||
|---|---|
|
||
| type_geo | geo_ancre |
|
||
| ponderation_geo | 0.7 |
|
||
| ponderation_interets | 0.1 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="2-createur-choisit-le-type-geo-contextuel-pour-actualite-regionale">2. Créateur choisit le type géo-contextuel pour actualité régionale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur connecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie une actualité régionale en Bretagne
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je choisis la classification "Géo-contextuel"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est enregistré avec:</p>
|
||
<pre><code>| champ | valeur |
|
||
|---|---|
|
||
| type_geo | geo_contextuel |
|
||
| ponderation_geo | 0.5 |
|
||
| ponderation_interets | 0.3 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="3-createur-choisit-le-type-geo-neutre-pour-un-podcast-philosophie">3. Créateur choisit le type géo-neutre pour un podcast philosophie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur connecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un podcast de philosophie
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je choisis la classification "Géo-neutre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est enregistré avec:</p>
|
||
<pre><code>| champ | valeur |
|
||
|---|---|
|
||
| type_geo | geo_neutre |
|
||
| ponderation_geo | 0.2 |
|
||
| ponderation_interets | 0.6 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="4-publication-impossible-sans-classification-geographique">4. Publication impossible sans classification géographique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée un contenu audio</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de publier sans sélectionner de type géographique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la publication échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Vous devez sélectionner un type de géo-pertinence"</p>
|
||
<hr />
|
||
<h2 id="5-moderateur-reclassifie-un-contenu-mal-categorise">5. Modérateur reclassifie un contenu mal catégorisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu podcast générique est classifié "Géo-ancré"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le modérateur examine le contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le modérateur le reclassifie en "Géo-neutre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la nouvelle classification est appliquée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme utilise la pondération géo = 0.2
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le créateur reçoit une notification de reclassification</p>
|
||
<hr />
|
||
<h2 id="6-createur-modifie-la-classification-apres-publication">6. Créateur modifie la classification après publication</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai publié un contenu classifié "Géo-contextuel"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je réalise qu'il devrait être "Géo-neutre"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie la classification en "Géo-neutre"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est enregistrée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme utilise la nouvelle pondération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Classification modifiée avec succès"</p>
|
||
<hr />
|
||
<h2 id="7-statistiques-de-classification-dans-le-profil-createur">7. Statistiques de classification dans le profil créateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai publié 30 contenus:</p>
|
||
<pre><code>| type | nombre |
|
||
|---|---|
|
||
| Géo-ancré | 10 |
|
||
| Géo-contextuel | 15 |
|
||
| Géo-neutre | 5 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la répartition de mes classifications
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> des suggestions pour optimiser la portée</p>
|
||
<hr />
|
||
<h2 id="8-contenu-geo-ancre-fortement-pondere-par-la-proximite">8. Contenu géo-ancré fortement pondéré par la proximité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide "Géo-ancré" existe à la Tour Eiffel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est à 100m de la Tour Eiffel</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la pondération géo est de 0.7
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le score géo est proche de 1 (très proche)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu a un score final élevé</p>
|
||
<hr />
|
||
<h2 id="9-contenu-geo-neutre-moins-sensible-a-la-distance">9. Contenu géo-neutre moins sensible à la distance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un podcast philosophie "Géo-neutre" existe à Paris
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est à Marseille (750 km)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la pondération géo est de 0.2
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le score géo est bas (distance élevée)
|
||
<span style="color: #F44336"><strong>Mais</strong></span> le score intérêts (0.6) peut compenser
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu peut quand même être recommandé si intérêts match</p>
|
||
<hr />
|
||
<h2 id="10-comparaison-scores-entre-types-geo-pour-meme-distance">10. Comparaison scores entre types géo pour même distance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> 3 contenus au même endroit (Paris):</p>
|
||
<pre><code>| type | ponderation_geo |
|
||
|---|---|
|
||
| Géo-ancré | 0.7 |
|
||
| Géo-contextuel | 0.5 |
|
||
| Géo-neutre | 0.2 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est à 50 km de Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule les scores</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu "Géo-ancré" a le score géo le plus élevé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu "Géo-neutre" a le score géo le plus faible
|
||
<span style="color: #F44336"><strong>Mais</strong></span> peut avoir un score final plus élevé si forte correspondance intérêts</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="gestion-du-contenu-politique-mvp-simplifie">Gestion du contenu politique (MVP simplifié)</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux pouvoir filtrer le contenu politique</em>
|
||
<em>Afin de contrôler mon exposition à ce type de contenu</em></p>
|
||
</blockquote>
|
||
<p><strong>13 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-createur-tagge-son-contenu-comme-politique">1. Créateur tagge son contenu comme "Politique"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur connecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un contenu sur un débat politique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je sélectionne le tag "Politique"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est enregistré avec le tag "Politique"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune classification gauche/droite n'est demandée (MVP)</p>
|
||
<hr />
|
||
<h2 id="2-tag-politique-au-meme-niveau-que-les-autres-tags">2. Tag "Politique" au même niveau que les autres tags</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je crée un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la liste des tags disponibles</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les tags suivants au même niveau:</p>
|
||
<pre><code>| tag |
|
||
|---|
|
||
| Économie |
|
||
| Sport |
|
||
| Culture |
|
||
| Politique |
|
||
| Automobile |
|
||
| Voyage |
|
||
| Musique |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="3-par-defaut-tous-les-contenus-politiques-sont-visibles">3. Par défaut, tous les contenus politiques sont visibles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouvel utilisateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas modifié les paramètres de contenu politique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus tagués "Politique" sont inclus normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun filtrage n'est appliqué</p>
|
||
<hr />
|
||
<h2 id="4-activer-le-filtrage-masquer-contenu-politique">4. Activer le filtrage "Masquer contenu politique"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'active l'option "Masquer contenu politique" dans les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les contenus tagués "Politique" sont exclus de mes recommandations
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Contenu politique masqué"</p>
|
||
<hr />
|
||
<h2 id="5-filtrage-politique-actif-aucun-contenu-politique-recommande">5. Filtrage politique actif - aucun contenu politique recommandé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai activé "Masquer contenu politique"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il existe 100 contenus dont 20 tagués "Politique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande 50 recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois 50 contenus parmi les 80 non-politiques
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 0% de contenus politiques sont proposés</p>
|
||
<hr />
|
||
<h2 id="6-desactiver-le-filtrage-masquer-contenu-politique">6. Désactiver le filtrage "Masquer contenu politique"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai activé "Masquer contenu politique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive cette option dans les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus politiques sont à nouveau inclus dans mes recommandations
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le filtrage est levé immédiatement</p>
|
||
<hr />
|
||
<h2 id="7-mode-kids-filtre-automatiquement-le-contenu-politique">7. Mode Kids filtre automatiquement le contenu politique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de 14 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le mode Kids est activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les contenus tagués "Politique" sont automatiquement exclus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce indépendamment du paramètre "Masquer contenu politique"</p>
|
||
<hr />
|
||
<h2 id="8-statistiques-createur-sur-contenu-politique">8. Statistiques créateur sur contenu politique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai publié 20 contenus dont 5 tagués "Politique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le nombre d'utilisateurs ayant masqué le contenu politique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le taux d'engagement comparé aux autres tags</p>
|
||
<hr />
|
||
<h2 id="9-recherche-avec-tag-politique">9. Recherche avec tag "Politique"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je recherche du contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je filtre par tag "Politique"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus tagués "Politique" sont affichés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce même si j'ai activé "Masquer contenu politique" (recherche explicite)</p>
|
||
<hr />
|
||
<h2 id="10-partage-de-contenu-politique-avec-filtre-actif">10. Partage de contenu politique avec filtre actif</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai activé "Masquer contenu politique"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un ami me partage un lien vers un contenu tagué "Politique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre le lien</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux accéder au contenu (partage explicite)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois un avertissement "Ce contenu est tagué Politique"</p>
|
||
<hr />
|
||
<h2 id="11-pas-de-classification-gauchedroite-en-mvp">11. Pas de classification gauche/droite en MVP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un contenu tagué "Politique"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune option de classification idéologique n'est proposée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux pas indiquer "Gauche", "Droite", "Centre", etc.</p>
|
||
<hr />
|
||
<h2 id="12-pas-dequilibrage-impose-en-mvp">12. Pas d'équilibrage imposé en MVP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur écoute majoritairement du contenu politique de gauche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun équilibrage droite/gauche n'est appliqué
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les recommandations suivent l'algorithme standard (intérêts, géo, engagement)</p>
|
||
<hr />
|
||
<h2 id="13-notification-post-mvp-pour-classification-avancee">13. Notification post-MVP pour classification avancée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave passe en phase post-MVP
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la classification politique avancée est activée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification m'informant des nouvelles options
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux configurer mes préférences d'équilibrage politique</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="contenus-geolocalises-en-mode-voiture">Contenus géolocalisés en mode voiture</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur en voiture</em>
|
||
<em>Je veux recevoir des notifications de contenus géolocalisés au bon moment</em>
|
||
<em>Afin de découvrir du contenu contextuel sans distraction au volant</em></p>
|
||
</blockquote>
|
||
<p><strong>36 scénarios</strong> (32 standards, 4 plans)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'application est ouverte (premier plan)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le GPS est activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur est en mode voiture (vitesse ≥ 5 km/h)</p>
|
||
</blockquote>
|
||
<h2 id="1-calcul-eta-et-notification-7-secondes-avant-le-point-gps">1. Calcul ETA et notification 7 secondes avant le point GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu géolocalisé existe à la Tour Eiffel (48.8584, 2.2945)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je me déplace à 50 km/h vers ce point
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis à 98 mètres du point (ETA = 7 secondes)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule l'ETA</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification est déclenchée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur "7" s'affiche avec l'icône 🏛️
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une notification sonore (bip court) est jouée</p>
|
||
<hr />
|
||
<h2 id="2-plan-calcul-eta-a-differentes-vitesses">2. 📋 Plan: Calcul ETA à différentes vitesses</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu géolocalisé existe à un point GPS
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je me déplace à <vitesse> km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis à <distance> mètres du point</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'ETA calculé est <eta> secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la notification est déclenchée : <notification></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>vitesse</th>
|
||
<th>distance</th>
|
||
<th>eta</th>
|
||
<th>notification</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>19</td>
|
||
<td>7</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>98</td>
|
||
<td>7</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>130</td>
|
||
<td>252</td>
|
||
<td>7</td>
|
||
<td>Oui</td>
|
||
</tr>
|
||
<tr>
|
||
<td>50</td>
|
||
<td>200</td>
|
||
<td>14</td>
|
||
<td>Non</td>
|
||
</tr>
|
||
<tr>
|
||
<td>10</td>
|
||
<td>50</td>
|
||
<td>18</td>
|
||
<td>Non</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="3-notification-immediate-si-vitesse-5-kmh-et-distance-50m">3. Notification immédiate si vitesse <5 km/h ET distance <50m</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu géolocalisé existe à 30m de ma position
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse est 3 km/h (arrêté à un feu rouge)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte cette situation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une notification est déclenchée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai pas besoin d'attendre le calcul ETA</p>
|
||
<hr />
|
||
<h2 id="4-notification-minimaliste-sans-texte-securite-routiere">4. Notification minimaliste sans texte (sécurité routière)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée est déclenchée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la notification s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les éléments suivants sont visibles:</p>
|
||
<pre><code>| élément | présent |
|
||
|---|---|
|
||
| Icône du tag | ✅ |
|
||
| Compteur 7→1 | ✅ |
|
||
| Son bref (bip) | ✅ |
|
||
| Titre texte | ❌ |
|
||
| Description | ❌ |
|
||
| Cover image | ❌ |
|
||
| Bouton Annuler | ❌ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="5-plan-icones-selon-le-tag-du-contenu">5. 📋 Plan: Icônes selon le tag du contenu</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu géolocalisé avec le tag <tag> est disponible</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la notification s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'icône <icone> est affichée</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>tag</th>
|
||
<th>icone</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Culture générale</td>
|
||
<td>🏛️</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Histoire</td>
|
||
<td>📜</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Voyage</td>
|
||
<td>✈️</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Famille</td>
|
||
<td>👨👩👧</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Musique</td>
|
||
<td>🎵</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Sport</td>
|
||
<td>⚽</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Technologie</td>
|
||
<td>💻</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Automobile</td>
|
||
<td>🚗</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="6-compteur-decrementant-de-7-a-1">6. Compteur décrémentant de 7 à 1</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée s'affiche</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le compteur démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur affiche "7"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 1 seconde, il affiche "6"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 2 secondes, il affiche "5"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 3 secondes, il affiche "4"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 4 secondes, il affiche "3"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 5 secondes, il affiche "2"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 6 secondes, il affiche "1"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 7 secondes, la notification disparaît</p>
|
||
<hr />
|
||
<h2 id="7-notification-sonore-uniquement-en-mode-carplay">7. Notification sonore uniquement en mode CarPlay</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application est connectée à CarPlay
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu géolocalisé est détecté (ETA 7s)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la notification est déclenchée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seule la notification sonore (bip) est jouée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun overlay visuel n'est affiché (icône, compteur)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut valider via le bouton "Suivant" au volant</p>
|
||
<hr />
|
||
<h2 id="8-notification-sonore-uniquement-en-mode-android-auto">8. Notification sonore uniquement en mode Android Auto</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application est connectée à Android Auto
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu géolocalisé est détecté (ETA 7s)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la notification est déclenchée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seule la notification sonore (bip) est jouée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun overlay visuel n'est affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut valider via le bouton "Suivant" au volant</p>
|
||
<hr />
|
||
<h2 id="9-notification-complete-sonore-visuelle-en-mode-normal">9. Notification complète (sonore + visuelle) en mode normal</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application n'est PAS connectée à CarPlay/Android Auto
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu géolocalisé est détecté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la notification est déclenchée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification sonore (bip) est jouée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'overlay visuel s'affiche (icône + compteur 7→1)</p>
|
||
<hr />
|
||
<h2 id="10-validation-via-bouton-suivant-et-decompte-5-secondes">10. Validation via bouton "Suivant" et décompte 5 secondes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée est affichée (compteur à 5)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'écoute un podcast</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur le bouton "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur bascule à "5" (décompte final)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu actuel continue de jouer normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur décrémente: 5→4→3→2→1
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après 5 secondes, le contenu géolocalisé démarre (fade in 0.3s)</p>
|
||
<hr />
|
||
<h2 id="11-transition-fluide-avec-fade-outin">11. Transition fluide avec fade out/in</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le décompte atteint "0"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu géolocalisé doit démarrer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu actuel fait un fade out de 0.3s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu géolocalisé fait un fade in de 0.3s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a pas de silence entre les deux</p>
|
||
<hr />
|
||
<h2 id="12-contenu-actuel-se-termine-pendant-le-decompte">12. Contenu actuel se termine pendant le décompte</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai validé la notification (décompte 5s démarre)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon contenu actuel se termine après 2 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu actuel se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu suivant du buffer démarre immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le décompte continue (3→2→1)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> à la fin du décompte, le contenu géolocalisé remplace le buffer</p>
|
||
<hr />
|
||
<h2 id="13-ignorance-de-la-notification-pas-de-clic-pendant-7s">13. Ignorance de la notification (pas de clic pendant 7s)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée s'affiche (compteur 7)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 7 secondes s'écoulent sans que j'appuie sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification disparaît automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu géolocalisé est perdu (pas d'insertion dans la file)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un cooldown de 10 minutes est activé</p>
|
||
<hr />
|
||
<h2 id="14-quota-de-6-contenus-geolocalises-par-heure">14. Quota de 6 contenus géolocalisés par heure</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai validé 6 notifications géolocalisées dans la dernière heure</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un 7ème contenu géolocalisé est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification n'est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu n'est pas inséré dans la file</p>
|
||
<hr />
|
||
<h2 id="15-fenetre-glissante-de-60-minutes">15. Fenêtre glissante de 60 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai validé 6 contenus géolocalisés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le premier contenu a été validé il y a 61 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un nouveau contenu géolocalisé est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification est envoyée (quota libéré : 5/6)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur horaire est mis à jour</p>
|
||
<hr />
|
||
<h2 id="16-plan-gestion-du-quota-horaire">16. 📋 Plan: Gestion du quota horaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que <nb_valides> notifications ont été validées dans la dernière heure</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un nouveau contenu géolocalisé est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification est <action></p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>nb_valides</th>
|
||
<th>action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>0</td>
|
||
<td>envoyée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>3</td>
|
||
<td>envoyée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>5</td>
|
||
<td>envoyée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>6</td>
|
||
<td>non envoyée</td>
|
||
</tr>
|
||
<tr>
|
||
<td>7</td>
|
||
<td>non envoyée</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="17-exception-audio-guides-multi-sequences-comptent-comme-1">17. Exception audio-guides multi-séquences (comptent comme 1)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai démarré un audio-guide avec 8 séquences
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que cet audio-guide compte comme 1 contenu dans le quota</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> toutes les séquences de l'audio-guide sont lues</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon quota reste à 1/6
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux encore valider 5 contenus géolocalisés simples</p>
|
||
<hr />
|
||
<h2 id="18-cooldown-de-10-minutes-apres-notification-ignoree">18. Cooldown de 10 minutes après notification ignorée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée a été ignorée (pas de clic)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un cooldown de 10 minutes est activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 5 minutes s'écoulent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un nouveau contenu géolocalisé est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification n'est envoyée (cooldown actif)</p>
|
||
<hr />
|
||
<h2 id="19-cooldown-expire-apres-10-minutes">19. Cooldown expire après 10 minutes</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un cooldown a été activé il y a 10 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un nouveau contenu géolocalisé est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification est envoyée (cooldown expiré)</p>
|
||
<hr />
|
||
<h2 id="20-pas-de-cooldown-si-notification-validee">20. Pas de cooldown si notification validée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée est affichée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant" dans les 7 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun cooldown n'est activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la prochaine notification pourra être envoyée normalement</p>
|
||
<hr />
|
||
<h2 id="21-contenu-geolocalise-dans-lhistorique-de-navigation">21. Contenu géolocalisé dans l'historique de navigation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu du buffer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai validé un contenu géolocalisé "Tour Eiffel"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai écouté 42 secondes du contenu géolocalisé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Suivant" (skip)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'appuie ensuite sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu géolocalisé reprend à 42 secondes</p>
|
||
<hr />
|
||
<h2 id="22-contenu-ignore-nentre-pas-dans-lhistorique">22. Contenu ignoré n'entre pas dans l'historique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une notification géolocalisée a été ignorée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie sur "Précédent"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu géolocalisé ignoré n'apparaît PAS dans l'historique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reviens au contenu d'avant</p>
|
||
<hr />
|
||
<h2 id="23-skip-pendant-le-decompte-annule-linsertion">23. Skip pendant le décompte annule l'insertion</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai validé une notification (décompte 5s en cours)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le compteur affiche "3"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'appuie à nouveau sur "Suivant"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le décompte est annulé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu suivant du buffer démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu géolocalisé n'entre PAS dans l'historique</p>
|
||
<hr />
|
||
<h2 id="24-detection-mode-pieton-vitesse-5-kmh-stable-10s">24. Détection mode piéton (vitesse <5 km/h stable 10s)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en mode voiture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse passe à 3 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> cette vitesse reste stable pendant 10 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode piéton est activé automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les notifications passent en mode push arrière-plan (si permission accordée)</p>
|
||
<hr />
|
||
<h2 id="25-detection-mode-voiture-vitesse-5-kmh-stable-10s">25. Détection mode voiture (vitesse ≥5 km/h stable 10s)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en mode piéton
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse passe à 15 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> cette vitesse reste stable pendant 10 secondes</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode voiture est activé automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les notifications passent en mode sonore + icône (app premier plan requise)</p>
|
||
<hr />
|
||
<h2 id="26-hysteresis-pour-eviter-basculements-intempestifs">26. Hysteresis pour éviter basculements intempestifs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma vitesse passe de 20 km/h à 3 km/h (arrêt feu rouge)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse remonte à 20 km/h après 8 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système vérifie le mode</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun basculement n'a lieu (hysteresis de 10s non atteinte)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reste en mode voiture</p>
|
||
<hr />
|
||
<h2 id="27-plan-effets-du-basculement-voiture-pieton">27. 📋 Plan: Effets du basculement voiture → piéton</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je bascule de voiture à piéton</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le basculement est effectué</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les paramètres suivants changent:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="28-haute-vitesse-130-kmh-sur-autoroute">28. Haute vitesse (130 km/h sur autoroute)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je roule à 130 km/h (36.1 m/s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu géolocalisé est à 252 mètres</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'ETA de 7s est atteint
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je valide la notification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le décompte 5s démarre
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu géolocalisé démarre encore avant le point GPS (72m avant)</p>
|
||
<hr />
|
||
<h2 id="29-multiples-points-geolocalises-proches-route-touristique">29. Multiples points géolocalisés proches (route touristique)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 3 châteaux sont espacés de 800m chacun
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je valide la notification du Château A</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'arrive près du Château B (57s plus tard à 50 km/h)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la notification du Château B est envoyée (quota 2/6, pas de cooldown)</p>
|
||
<hr />
|
||
<h2 id="30-mode-stationnement-vitesse-1-kmh-pendant-2-min">30. Mode stationnement (vitesse <1 km/h pendant 2 min)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je me gare à 30m d'un château
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse est <1 km/h pendant 2 minutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le mode stationnement est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification de contenu géolocalisé n'est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le système bascule automatiquement en mode piéton</p>
|
||
<hr />
|
||
<h2 id="31-reprise-conduite-apres-stationnement">31. Reprise conduite après stationnement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en mode stationnement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma vitesse passe à 20 km/h pendant 10 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte la reprise de conduite</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode voiture est réactivé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les notifications géolocalisées reprennent (si quota non atteint)</p>
|
||
<hr />
|
||
<h2 id="32-contenu-geolocalise-simple-1-sequence-unique">32. Contenu géolocalisé simple (1 séquence unique)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu géolocalisé simple existe à un point GPS</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la notification est déclenchée (ETA 7s)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je valide</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu démarre après décompte 5s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> à la fin du contenu, le buffer normal reprend
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce contenu compte 1/6 dans le quota</p>
|
||
<hr />
|
||
<h2 id="33-audio-guide-multi-sequences-2-sequences-enchainees">33. Audio-guide multi-séquences (2+ séquences enchaînées)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide avec 8 séquences existe</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je démarre l'audio-guide
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que les séquences s'enchaînent automatiquement (GPS ou manuel)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide entier compte 1/6 dans le quota
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les séquences ne déclenchent PAS de notification avec compteur 7s
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elles se déclenchent au point GPS exact (rayon 30m)</p>
|
||
<hr />
|
||
<h2 id="34-gps-desactive-en-mode-voiture">34. GPS désactivé en mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en mode voiture</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le GPS est désactivé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification géolocalisée ne peut être envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message d'erreur s'affiche: "GPS requis pour les contenus géolocalisés"</p>
|
||
<hr />
|
||
<h2 id="35-app-en-arriere-plan-en-mode-voiture">35. App en arrière-plan en mode voiture</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en mode voiture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'app passe en arrière-plan</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un contenu géolocalisé est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune notification n'est envoyée (app premier plan requise)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu n'est pas perdu (sera proposé si app rouverte dans le rayon)</p>
|
||
<hr />
|
||
<h2 id="36-permission-always-location-refusee-mode-pieton-indisponible">36. Permission "Always Location" refusée (mode piéton indisponible)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je refuse la permission "Always Location"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ma vitesse passe <5 km/h</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode piéton n'est PAS activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le mode voiture reste actif (avec permission "When In Use")
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune notification arrière-plan n'est envoyée</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="gestion-de-lhistorique-et-reproposition">Gestion de l'historique et reproposition</h1>
|
||
<blockquote>
|
||
<p><em>En tant que système de recommandation</em>
|
||
<em>Je veux gérer l'historique d'écoute intelligemment</em>
|
||
<em>Afin d'éviter les répétitions et offrir une découverte maximale</em></p>
|
||
</blockquote>
|
||
<p><strong>19 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-contenu-ecoute-completement-80-jamais-repropose">1. Contenu écouté complètement (>80%) - jamais reproposé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté un contenu à 85%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ce contenu n'est jamais reproposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est marqué comme "écouté" dans l'historique</p>
|
||
<hr />
|
||
<h2 id="2-contenu-ecoute-a-80-exactement-jamais-repropose">2. Contenu écouté à 80% exactement - jamais reproposé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté un contenu exactement à 80%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ce contenu n'est pas reproposé (seuil >= 80%)</p>
|
||
<hr />
|
||
<h2 id="3-contenu-skippe-rapidement-10s-ne-pas-reproposer">3. Contenu skippé rapidement (<10s) - ne pas reproposer</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a skippé un contenu après 8 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ce contenu n'est pas reproposé (signal négatif fort)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la jauge d'intérêt correspondante est réduite de 0.5%</p>
|
||
<hr />
|
||
<h2 id="4-contenu-skippe-exactement-a-10s-ne-pas-reproposer">4. Contenu skippé exactement à 10s - ne pas reproposer</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a skippé un contenu après exactement 10 secondes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ce contenu n'est pas reproposé (seuil < 10s strict)</p>
|
||
<hr />
|
||
<h2 id="5-contenu-partiellement-ecoute-10-80-reproposer-avec-reprise">5. Contenu partiellement écouté (10-80%) - reproposer avec reprise</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté un contenu à 45%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il est arrivé à la position 2:30 (150 secondes)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme propose à nouveau ce contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu peut être reproposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la position de reprise est 150 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur voit "Reprendre à 2:30"</p>
|
||
<hr />
|
||
<h2 id="6-contenu-ecoute-a-11-reproposition-possible">6. Contenu écouté à 11% - reproposition possible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté un contenu à 11%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ce contenu peut être reproposé (>10%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la position de reprise est sauvegardée</p>
|
||
<hr />
|
||
<h2 id="7-contenu-ecoute-a-79-reproposition-possible">7. Contenu écouté à 79% - reproposition possible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté un contenu à 79%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ce contenu peut être reproposé (<80%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'utilisateur peut terminer l'écoute</p>
|
||
<hr />
|
||
<h2 id="8-audio-guide-avec-flag-replayabletrue">8. Audio-guide avec flag replayable=true</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un audio-guide a le flag "replayable = true"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur l'a écouté à 95%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide peut être reproposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est marqué comme "Écouté - Rejouable"</p>
|
||
<hr />
|
||
<h2 id="9-podcast-standard-sans-flag-replayable">9. Podcast standard sans flag replayable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un podcast n'a pas de flag replayable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur l'a écouté à 90%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le podcast n'est jamais reproposé</p>
|
||
<hr />
|
||
<h2 id="10-stockage-dans-user_content_history">10. Stockage dans user_content_history</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur écoute un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'écoute se termine ou est skippée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les données suivantes sont enregistrées:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-historique-illimite-stocke">11. Historique illimité stocké</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté 5000 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il consulte son historique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les 5000 contenus sont disponibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun contenu n'est supprimé automatiquement</p>
|
||
<hr />
|
||
<h2 id="12-algorithme-considere-les-100-derniers-pour-performance">12. Algorithme considère les 100 derniers pour performance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté 500 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il vérifie uniquement les 100 derniers contenus pour exclusion
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette limite est une optimisation de requête SQL</p>
|
||
<hr />
|
||
<h2 id="13-export-historique-complet-rgpd">13. Export historique complet (RGPD)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur demande l'export RGPD</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'historique complet est inclus avec:</p>
|
||
<pre><code>| information | inclus |
|
||
|---|---|
|
||
| Tous les contenus | ✅ |
|
||
| Dates d'écoute | ✅ |
|
||
| Taux complétion | ✅ |
|
||
| Positions reprise | ✅ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="14-reprise-automatique-dun-contenu-partiellement-ecoute">14. Reprise automatique d'un contenu partiellement écouté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté un podcast à 60% (position 10:00)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ce podcast est reproposé par l'algorithme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je lance la lecture</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'écoute reprend automatiquement à 10:00
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois une notification "Reprise à 10:00"</p>
|
||
<hr />
|
||
<h2 id="15-option-reprendre-du-debut-pour-contenu-partiellement-ecoute">15. Option "Reprendre du début" pour contenu partiellement écouté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté un podcast à 60%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> ce podcast est reproposé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois deux options:</p>
|
||
<pre><code>| option | action |
|
||
|---|---|
|
||
| Reprendre à 10:00 | Lecture à partir de 10:00 |
|
||
| Depuis le début | Lecture à partir de 0:00 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="16-contenu-ecoute-il-y-a-6-mois-toujours-en-historique">16. Contenu écouté il y a 6 mois - toujours en historique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté un contenu il y a 6 mois à 90%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ce contenu n'est toujours pas reproposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique n'a pas de limite temporelle</p>
|
||
<hr />
|
||
<h2 id="17-nouveau-contenu-du-meme-createur-apres-ecoute-complete">17. Nouveau contenu du même créateur après écoute complète</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté un contenu de "Créateur A" à 90%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que "Créateur A" publie un nouveau contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le nouveau contenu peut être recommandé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul l'ancien contenu est exclu (pas tout le créateur)</p>
|
||
<hr />
|
||
<h2 id="18-statistiques-personnelles-dhistorique">18. Statistiques personnelles d'historique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte mon profil</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à la section "Historique"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| métrique | exemple |
|
||
|---|---|
|
||
| Nombre total d'écoutes | 1,234 |
|
||
| Heures écoutées | 456h |
|
||
| Taux complétion moyen | 72% |
|
||
| Top 5 catégories | Voyage, Sport |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="19-filtrer-lhistorique-par-date">19. Filtrer l'historique par date</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte mon historique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je filtre par "Dernière semaine"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus écoutés dans les 7 derniers jours sont affichés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux exporter cette sélection</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="medias-traditionnels-sur-roadwave">Médias traditionnels sur RoadWave</h1>
|
||
<blockquote>
|
||
<p><em>En tant que média établi</em>
|
||
<em>Je veux publier du contenu géolocalisé sur RoadWave</em>
|
||
<em>Afin d'atteindre une audience locale et mobile</em></p>
|
||
</blockquote>
|
||
<p><strong>21 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-creation-dun-compte-media-verifie">1. Création d'un compte média vérifié</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je représente Le Monde</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée un compte média
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je fournis les justificatifs (SIRET, documents officiels)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est créé en attente de vérification
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'équipe RoadWave examine ma demande sous 48-72h</p>
|
||
<hr />
|
||
<h2 id="2-validation-compte-media-par-lequipe-roadwave">2. Validation compte média par l'équipe RoadWave</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un compte média "Le Parisien" est en attente</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe RoadWave valide le compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compte reçoit le badge vérifié ✓
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le média peut publier sans validation des 3 premiers contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Compte média vérifié avec succès"</p>
|
||
<hr />
|
||
<h2 id="3-badge-verifie-visible-sur-profil-media">3. Badge vérifié visible sur profil média</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que "France Inter" a un compte vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur consulte le profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il voit le badge ✓ à côté du nom
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une mention "Média vérifié"</p>
|
||
<hr />
|
||
<h2 id="4-pas-de-validation-des-3-premiers-contenus-pour-medias">4. Pas de validation des 3 premiers contenus pour médias</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un média vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie mon premier contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est publié immédiatement sans validation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est visible pour tous les utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne passe pas par la modération initiale</p>
|
||
<hr />
|
||
<h2 id="5-moderation-a-posteriori-uniquement">5. Modération a posteriori uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que "Libération" publie un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le contenu est publié</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est immédiatement disponible
|
||
<span style="color: #F44336"><strong>Mais</strong></span> peut être signalé et modéré a posteriori
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> suit les mêmes règles de modération que les créateurs</p>
|
||
<hr />
|
||
<h2 id="6-publication-flash-info-geolocalise">6. Publication flash info géolocalisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis "Ouest-France" (média régional)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un flash info sur un événement à Rennes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je le géolocalise en Bretagne (géo-contextuel)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est publié immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est recommandé aux utilisateurs en Bretagne</p>
|
||
<hr />
|
||
<h2 id="7-publication-chronique-thematique">7. Publication chronique thématique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis "France Culture"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie une chronique philosophie (géo-neutre)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est disponible partout en France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> suit l'algorithme de recommandation standard</p>
|
||
<hr />
|
||
<h2 id="8-publication-edito-politique">8. Publication édito politique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis "Le Figaro"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un édito politique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je le tague "Politique"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu est publié immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la classification politique MVP s'applique (pas gauche/droite)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs ayant activé "Masquer politique" ne le voient pas</p>
|
||
<hr />
|
||
<h2 id="9-formats-de-contenu-autorises-pour-medias">9. Formats de contenu autorisés pour médias</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un média vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux publier:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="10-medias-suivent-les-regles-standard-de-classification-age">10. Médias suivent les règles standard de classification âge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis "RTL"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je publie un contenu sensible</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois obligatoirement classifier par âge:</p>
|
||
<pre><code>| classification | type contenu |
|
||
|---|---|
|
||
| Tout public | Info générale |
|
||
| 13+ | Actualité avec sujets sensibles |
|
||
| 16+ | Débats avec violence verbale |
|
||
| 18+ | Sujets adultes |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-monetisation-medias-partage-revenus-pub-standard">11. Monétisation médias - partage revenus pub standard</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un média vérifié
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes contenus génèrent des écoutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le mois se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois 3€ / 1000 écoutes complètes (même taux que créateurs)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le paiement suit les mêmes règles (seuil 50€, mensuel)</p>
|
||
<hr />
|
||
<h2 id="12-sponsoring-direct-non-gere-par-plateforme">12. Sponsoring direct non géré par plateforme</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis "Europe 1"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je veux intégrer un sponsor dans mon contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je mentionne le sponsor dans l'audio</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> c'est autorisé (sponsoring éditorial)
|
||
<span style="color: #F44336"><strong>Mais</strong></span> RoadWave ne gère pas la transaction
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je gère la relation sponsor directement</p>
|
||
<hr />
|
||
<h2 id="13-medias-peuvent-avoir-plusieurs-comptes-createurs">13. Médias peuvent avoir plusieurs comptes créateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis "Le Monde"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je veux créer des sous-comptes par rubrique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux créer:</p>
|
||
<pre><code>| compte | description |
|
||
|---|---|
|
||
| @lemonde_politique | Actualité politique |
|
||
| @lemonde_economie | Économie et entreprises |
|
||
| @lemonde_culture | Culture et spectacles |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> tous sont liés au compte média principal</p>
|
||
<hr />
|
||
<h2 id="14-medias-regionaux-privilegies-localement">14. Médias régionaux privilégiés localement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que "Sud-Ouest" publie du contenu géo-contextuel en Nouvelle-Aquitaine
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est à Bordeaux</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le contenu de "Sud-Ouest" a un score géo élevé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est privilégié pour l'audience locale</p>
|
||
<hr />
|
||
<h2 id="15-medias-nationaux-accessibles-partout">15. Médias nationaux accessibles partout</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que "France Inter" publie un podcast géo-neutre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> des utilisateurs à Paris, Lyon, Marseille demandent des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le podcast est accessible partout sans distinction géographique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> suit l'algorithme de recommandation standard</p>
|
||
<hr />
|
||
<h2 id="16-statistiques-detaillees-pour-medias">16. Statistiques détaillées pour médias</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un média vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-medias-peuvent-exporter-analytics">17. Médias peuvent exporter analytics</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis "Libération"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Exporter analytics"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un CSV avec données détaillées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux analyser les données avec mes outils internes</p>
|
||
<hr />
|
||
<h2 id="18-contact-prioritaire-equipe-roadwave">18. Contact prioritaire équipe RoadWave</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un média vérifié</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ai un problème technique ou question</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux contacter le support média prioritaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> j'obtiens une réponse sous 24h (vs 48-72h standard)</p>
|
||
<hr />
|
||
<h2 id="19-medias-peuvent-programmer-la-publication">19. Médias peuvent programmer la publication</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis "France Culture"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je prépare un contenu à l'avance</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux programmer la publication pour une date/heure future
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu sera publié automatiquement au moment choisi</p>
|
||
<hr />
|
||
<h2 id="20-api-dediee-pour-medias-post-mvp">20. API dédiée pour médias (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un grand média avec beaucoup de contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> RoadWave développe l'API médias</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux automatiser la publication via API
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> intégrer RoadWave dans mon workflow de production</p>
|
||
<hr />
|
||
<h2 id="21-signalement-dun-contenu-media-traite-en-priorite">21. Signalement d'un contenu média traité en priorité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu de "Le Monde" est signalé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le signalement arrive en modération</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est traité avec la même priorité qu'un créateur standard
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le badge vérifié ne donne pas d'immunité modération</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="mode-kids-pour-utilisateurs-13-15-ans">Mode Kids pour utilisateurs 13-15 ans</h1>
|
||
<blockquote>
|
||
<p><em>En tant que parent ou adolescent</em>
|
||
<em>Je veux activer un mode Kids avec filtrage de contenu</em>
|
||
<em>Afin de protéger les mineurs des contenus inappropriés</em></p>
|
||
</blockquote>
|
||
<p><strong>15 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-activation-manuelle-du-mode-kids">1. Activation manuelle du mode Kids</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de 14 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le mode Kids n'est pas activé par défaut</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'active le mode Kids dans les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode Kids est activé sur mon compte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Mode Kids activé - Contenus filtrés pour 13-15 ans"</p>
|
||
<hr />
|
||
<h2 id="2-parent-active-le-mode-kids-pour-son-enfant">2. Parent active le mode Kids pour son enfant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis le parent d'un utilisateur de 13 ans
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai accès au compte de mon enfant</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'active le mode Kids</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le mode Kids est activé sur le compte enfant
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les contenus "Tous publics" sont accessibles</p>
|
||
<hr />
|
||
<h2 id="3-filtrage-contenu-uniquement-tous-publics">3. Filtrage contenu - uniquement "Tous publics"</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé sur mon compte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il existe des contenus avec les classifications:</p>
|
||
<pre><code>| classification | nombre |
|
||
|---|---|
|
||
| Tous publics | 100 |
|
||
| 13+ | 50 |
|
||
| 16+ | 30 |
|
||
| 18+ | 20 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les 100 contenus "Tous publics" sont proposés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus 13+, 16+, 18+ sont exclus</p>
|
||
<hr />
|
||
<h2 id="4-exclusion-automatique-du-contenu-politique">4. Exclusion automatique du contenu politique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il existe 20 contenus "Tous publics" dont 5 tagués "Politique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les 15 contenus non-politiques sont proposés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 5 contenus politiques sont automatiquement exclus</p>
|
||
<hr />
|
||
<h2 id="5-pas-de-publicite-en-mode-kids">5. Pas de publicité en mode Kids</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis un utilisateur gratuit</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune publicité n'est diffusée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je n'ai pas d'insertion publicitaire (règle 1/5 désactivée)</p>
|
||
<hr />
|
||
<h2 id="6-publicite-validee-manuellement-en-mode-kids-post-mvp">6. Publicité validée manuellement en mode Kids (post-MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'une publicité a été validée manuellement pour le mode Kids</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'écoute du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> cette publicité peut être diffusée
|
||
<span style="color: #F44336"><strong>Mais</strong></span> la fréquence reste inférieure au mode standard</p>
|
||
<hr />
|
||
<h2 id="7-interface-standard-meme-en-mode-kids">7. Interface standard même en mode Kids</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'interface est identique au mode normal
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul le filtrage de contenu est actif (pas d'UI enfant)</p>
|
||
<hr />
|
||
<h2 id="8-desactivation-du-mode-kids">8. Désactivation du mode Kids</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive le mode Kids dans les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les contenus sont à nouveau accessibles selon mon âge
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Mode Kids désactivé"</p>
|
||
<hr />
|
||
<h2 id="9-utilisateur-16-ans-ne-peut-pas-activer-le-mode-kids-13-15-ans">9. Utilisateur 16 ans ne peut pas activer le mode Kids 13-15 ans</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de 16 ans</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'activer le mode Kids</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'activation réussit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le mode Kids filtre les contenus 16+ et 18+ (pas seulement 13+)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois uniquement les contenus "Tous publics"</p>
|
||
<hr />
|
||
<h2 id="10-tentative-dacces-direct-a-contenu-16-en-mode-kids">10. Tentative d'accès direct à contenu 16+ en mode Kids</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un ami me partage un contenu 16+</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder au contenu via le lien</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'accès est refusé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Ce contenu n'est pas accessible en mode Kids"</p>
|
||
<hr />
|
||
<h2 id="11-recherche-en-mode-kids-filtre-automatiquement">11. Recherche en mode Kids filtre automatiquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je recherche "débat"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus "Tous publics" apparaissent dans les résultats
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus 13+, 16+, 18+ sont exclus de la recherche</p>
|
||
<hr />
|
||
<h2 id="12-audio-guide-en-mode-kids">12. Audio-guide en mode Kids</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un audio-guide "Tous publics" existe au musée du Louvre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis à proximité du Louvre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'audio-guide est proposé normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les séquences sont accessibles</p>
|
||
<hr />
|
||
<h2 id="13-statistiques-createur-audience-mode-kids">13. Statistiques créateur - audience mode Kids</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes contenus "Tous publics" sont écoutés par des utilisateurs mode Kids</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le pourcentage d'écoutes en mode Kids
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux adapter mes contenus en conséquence</p>
|
||
<hr />
|
||
<h2 id="14-notification-lors-de-lactivation-du-mode-kids">14. Notification lors de l'activation du mode Kids</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'active le mode Kids</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une notification explicative:</p>
|
||
<pre><code>| information | description |
|
||
|---|---|
|
||
| Contenu | Seuls les contenus "Tous publics" accessibles |
|
||
| Politique | Contenus politiques automatiquement masqués |
|
||
| Publicité | Aucune publicité affichée |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="15-badge-mode-kids-visible-dans-le-profil">15. Badge mode Kids visible dans le profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le mode Kids est activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mon profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un badge "Mode Kids actif 🛡️"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux le désactiver en un clic</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="parametrabilite-admin-et-ab-testing">Paramétrabilité admin et A/B testing</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'administrateur RoadWave</em>
|
||
<em>Je veux configurer les paramètres de l'algorithme à chaud</em>
|
||
<em>Afin d'optimiser l'engagement sans redéploiement</em></p>
|
||
</blockquote>
|
||
<p><strong>20 scénarios</strong> (19 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté en tant qu'admin</p>
|
||
</blockquote>
|
||
<h2 id="1-acces-au-dashboard-admin">1. Accès au dashboard admin</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède au dashboard admin</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois tous les paramètres configurables de l'algorithme
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois les valeurs actuelles et par défaut</p>
|
||
<hr />
|
||
<h2 id="2-modifier-le-poids-geo-pour-contenu-ancre">2. Modifier le poids géo pour contenu ancré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le poids_geo_ancre est à 0.7 (défaut)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie le poids_geo_ancre à 0.8
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je sauvegarde</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la nouvelle valeur est appliquée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les nouveaux calculs utilisent 0.8
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Paramètre mis à jour avec succès"</p>
|
||
<hr />
|
||
<h2 id="3-validation-des-plages-de-valeurs">3. Validation des plages de valeurs</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de configurer poids_geo_ancre à 1.5 (hors plage 0.5-1.0)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Valeur hors plage autorisée (0.5 - 1.0)"</p>
|
||
<hr />
|
||
<h2 id="4-plan-modification-de-tous-les-parametres-configurables">4. 📋 Plan: Modification de tous les paramètres configurables</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie "<parametre>" à "<nouvelle_valeur>"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est appliquée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la nouvelle valeur respecte la plage "<plage>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>parametre</th>
|
||
<th>nouvelle_valeur</th>
|
||
<th>plage</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>poids_geo_ancre</td>
|
||
<td>0.8</td>
|
||
<td>0.5 - 1.0</td>
|
||
</tr>
|
||
<tr>
|
||
<td>poids_geo_contextuel</td>
|
||
<td>0.6</td>
|
||
<td>0.3 - 0.7</td>
|
||
</tr>
|
||
<tr>
|
||
<td>poids_geo_neutre</td>
|
||
<td>0.3</td>
|
||
<td>0.0 - 0.4</td>
|
||
</tr>
|
||
<tr>
|
||
<td>poids_engagement</td>
|
||
<td>0.3</td>
|
||
<td>0.0 - 0.5</td>
|
||
</tr>
|
||
<tr>
|
||
<td>part_aleatoire_global</td>
|
||
<td>0.15</td>
|
||
<td>0.0 - 0.3</td>
|
||
</tr>
|
||
<tr>
|
||
<td>distance_max_km</td>
|
||
<td>150</td>
|
||
<td>50 - 500</td>
|
||
</tr>
|
||
<tr>
|
||
<td>rayon_gps_point_m</td>
|
||
<td>1000</td>
|
||
<td>100 - 2000</td>
|
||
</tr>
|
||
<tr>
|
||
<td>seuil_min_ecoutes_engagement</td>
|
||
<td>100</td>
|
||
<td>10 - 200</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="5-aucun-recalcul-batch-apres-modification">5. Aucun recalcul batch après modification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le poids_geo_ancre est modifié de 0.7 à 0.8</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la modification est appliquée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun recalcul batch n'est lancé (économie CPU)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les nouveaux calculs utilisent la valeur 0.8</p>
|
||
<hr />
|
||
<h2 id="6-versioning-des-configurations">6. Versioning des configurations</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je modifie plusieurs paramètres</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je sauvegarde la configuration</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une nouvelle version est créée (ex: v1.2.3)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux voir l'historique des versions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux comparer deux versions</p>
|
||
<hr />
|
||
<h2 id="7-rollback-en-1-clic">7. Rollback en 1 clic</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la configuration actuelle est v1.2.3
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la version précédente était v1.2.2</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Restaurer v1.2.2"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les paramètres de v1.2.2 sont réappliqués
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Configuration restaurée à v1.2.2"</p>
|
||
<hr />
|
||
<h2 id="8-creer-une-variante-ab-testing">8. Créer une variante A/B testing</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée une nouvelle variante "Test engagement élevé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je configure:</p>
|
||
<pre><code>| parametre | valeur |
|
||
|---|---|
|
||
| poids_engagement | 0.4 |
|
||
| poids_geo_ancre | 0.6 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> que je lance le test A/B</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 50% des utilisateurs reçoivent la Config A (défaut)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 50% des utilisateurs reçoivent la Config B (test)</p>
|
||
<hr />
|
||
<h2 id="9-split-utilisateurs-aleatoire-pour-ab-test">9. Split utilisateurs aléatoire pour A/B test</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un test A/B est actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> 1000 nouveaux utilisateurs se connectent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> environ 500 sont assignés à la Config A
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> environ 500 sont assignés à la Config B
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'assignation est aléatoire et équilibrée</p>
|
||
<hr />
|
||
<h2 id="10-utilisateur-reste-dans-la-meme-variante">10. Utilisateur reste dans la même variante</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur est assigné à la Config B</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> il se reconnecte plusieurs fois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il reste toujours dans la Config B
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il ne change pas de variante pendant le test</p>
|
||
<hr />
|
||
<h2 id="11-metriques-comparatives-ab-testing">11. Métriques comparatives A/B testing</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un test A/B est actif depuis 7 jours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard A/B testing</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les métriques suivantes pour chaque config:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="12-dashboard-graphique-temps-reel">12. Dashboard graphique temps réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un test A/B est actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte le dashboard</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois des graphiques temps réel:</p>
|
||
<pre><code>| graphique | type |
|
||
|---|---|
|
||
| Évolution engagement | Ligne |
|
||
| Répartition utilisateurs | Camembert |
|
||
| Taux complétion | Barres |
|
||
| Durée session | Ligne |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="13-terminer-un-test-ab-et-appliquer-la-meilleure-config">13. Terminer un test A/B et appliquer la meilleure config</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un test A/B montre que Config B est meilleure</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Appliquer Config B pour tous"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la Config B devient la configuration par défaut
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les utilisateurs utilisent maintenant Config B
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'ancien test est archivé</p>
|
||
<hr />
|
||
<h2 id="14-audit-engagement-global">14. Audit engagement global</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la section "Audit engagement"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| 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% |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="15-graphiques-evolution-engagement-selon-config">15. Graphiques évolution engagement selon config</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que plusieurs modifications de config ont été faites</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les graphiques d'évolution</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'impact de chaque changement de config
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux corréler changements config avec métriques</p>
|
||
<hr />
|
||
<h2 id="16-export-csv-pour-analyse-externe">16. Export CSV pour analyse externe</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Exporter données"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux télécharger un CSV avec:</p>
|
||
<pre><code>| colonne | exemple |
|
||
|---|---|
|
||
| date | 2026-01-21 |
|
||
| version_config | v1.2.3 |
|
||
| taux_completion | 0.72 |
|
||
| engagement_moyen | 0.45 |
|
||
| duree_session_min | 27 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-alerte-automatique-si-metrique-critique-baisse">17. Alerte automatique si métrique critique baisse</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le taux de complétion moyen est à 70%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une nouvelle config fait baisser le taux à 55%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois une alerte email "Baisse critique du taux de complétion"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux rollback rapidement</p>
|
||
<hr />
|
||
<h2 id="18-previsualisation-impact-avant-application">18. Prévisualisation impact avant application</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je modifie poids_geo_ancre de 0.7 à 0.9</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Prévisualiser impact"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois une simulation sur échantillon de 1000 utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois l'estimation d'impact sur les métriques clés</p>
|
||
<hr />
|
||
<h2 id="19-notes-et-commentaires-sur-modifications-config">19. Notes et commentaires sur modifications config</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie une configuration</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux ajouter une note "Test pour améliorer contenu local"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette note est visible dans l'historique des versions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'équipe peut comprendre le contexte des changements</p>
|
||
<hr />
|
||
<h2 id="20-permissions-admin-pour-modification-config">20. Permissions admin pour modification config</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un admin junior</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier un paramètre critique</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'accès est refusé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois "Permission admin senior requise"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seuls les admins seniors peuvent modifier les paramètres</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="parametrabilite-utilisateur-et-profils">Paramétrabilité utilisateur et profils</h1>
|
||
<blockquote>
|
||
<p><em>En tant qu'utilisateur</em>
|
||
<em>Je veux personnaliser mon expérience de recommandation</em>
|
||
<em>Afin d'adapter l'application à mes différents contextes d'usage</em></p>
|
||
</blockquote>
|
||
<p><strong>25 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis connecté</p>
|
||
</blockquote>
|
||
<h2 id="1-acces-aux-parametres-de-personnalisation">1. Accès aux paramètres de personnalisation</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre les paramètres de personnalisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois trois curseurs disponibles:</p>
|
||
<pre><code>| curseur | description |
|
||
|---|---|
|
||
| Géolocalisation | Local ← slider → National |
|
||
| Découverte | 0% ← slider → 50% |
|
||
| Politique | Masquer / Équilibré / Mes préférences |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="2-modifier-le-curseur-geolocalisation-vers-local">2. Modifier le curseur Géolocalisation vers Local</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le curseur Géolocalisation est au centre (défaut)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je déplace le curseur vers "Local" (gauche)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'algorithme privilégie fortement les contenus proches
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la pondération géographique augmente
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Recommandations locales privilégiées"</p>
|
||
<hr />
|
||
<h2 id="3-modifier-le-curseur-geolocalisation-vers-national">3. Modifier le curseur Géolocalisation vers National</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le curseur Géolocalisation est au centre</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je déplace le curseur vers "National" (droite)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'algorithme privilégie la découverte nationale
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la pondération géographique diminue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois des contenus de toute la France</p>
|
||
<hr />
|
||
<h2 id="4-curseur-decouverte-a-0-aucun-aleatoire">4. Curseur Découverte à 0% - aucun aléatoire</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je règle le curseur Découverte à 0%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 0% de contenus aléatoires dans mes recommandations
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 100% de contenus calculés selon score combiné
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Personnalisation maximale"</p>
|
||
<hr />
|
||
<h2 id="5-curseur-decouverte-a-10-defaut-equilibre">5. Curseur Découverte à 10% - défaut équilibré</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je règle le curseur Découverte à 10%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 10% de contenus aléatoires
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 90% de contenus calculés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Équilibre découverte/personnalisation"</p>
|
||
<hr />
|
||
<h2 id="6-curseur-decouverte-a-30-decouverte-elevee">6. Curseur Découverte à 30% - découverte élevée</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je règle le curseur Découverte à 30%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 30% de contenus aléatoires
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 70% de contenus calculés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Découverte élevée activée"</p>
|
||
<hr />
|
||
<h2 id="7-curseur-decouverte-a-50-decouverte-maximale">7. Curseur Découverte à 50% - découverte maximale</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je règle le curseur Découverte à 50%</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 50% de contenus aléatoires
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 50% de contenus calculés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Découverte maximale (équivaut à national)"</p>
|
||
<hr />
|
||
<h2 id="8-creer-un-profil-personnalise-trajet-quotidien">8. Créer un profil personnalisé "Trajet quotidien"</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée un nouveau profil nommé "🚗 Trajet quotidien"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je configure:</p>
|
||
<pre><code>| parametre | valeur |
|
||
|---|---|
|
||
| Géolocalisation | Local |
|
||
| Découverte | 5% |
|
||
| Politique | Masquer |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> que je sauvegarde</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le profil "🚗 Trajet quotidien" est créé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux l'activer en un clic</p>
|
||
<hr />
|
||
<h2 id="9-creer-un-profil-road-trip">9. Créer un profil "Road trip"</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée un profil "🛣️ Road trip"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je configure:</p>
|
||
<pre><code>| parametre | valeur |
|
||
|---|---|
|
||
| Géolocalisation | Régional |
|
||
| Découverte | 30% |
|
||
| Politique | Équilibré |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le profil est sauvegardé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux switcher entre profils facilement</p>
|
||
<hr />
|
||
<h2 id="10-creer-un-profil-enfants">10. Créer un profil "Enfants"</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je crée un profil "👶 Enfants"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'active le Mode Kids</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les paramètres sont adaptés pour enfants:</p>
|
||
<pre><code>| parametre | valeur |
|
||
|---|---|
|
||
| Mode Kids | Activé |
|
||
| Politique | Masquer (forcé) |
|
||
| Publicité | Aucune |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="11-activer-un-profil-existant">11. Activer un profil existant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai créé un profil "🚗 Trajet quotidien"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Activer" pour ce profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les paramètres du profil sont appliqués
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Profil 'Trajet quotidien' activé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'algorithme utilise ces paramètres immédiatement</p>
|
||
<hr />
|
||
<h2 id="12-synchronisation-profils-entre-devices">12. Synchronisation profils entre devices</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai créé 3 profils sur mon iPhone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte sur mon iPad</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes 3 profils sont automatiquement synchronisés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux les utiliser sur l'iPad</p>
|
||
<hr />
|
||
<h2 id="13-modification-dun-profil-synchronisee">13. Modification d'un profil synchronisée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un profil "Road trip" sur iPhone</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je modifie ce profil sur iPhone</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est synchronisée sur tous mes devices
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le profil est mis à jour partout en temps réel</p>
|
||
<hr />
|
||
<h2 id="14-pas-de-partage-de-profils-entre-utilisateurs">14. Pas de partage de profils entre utilisateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai créé des profils personnalisés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que ma conjointe a un compte RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> elle se connecte sur son compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> elle ne voit pas mes profils
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque utilisateur a ses propres profils</p>
|
||
<hr />
|
||
<h2 id="15-auto-switch-selon-contexte-detection-trajet-recurrent">15. Auto-switch selon contexte (détection trajet récurrent)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise toujours le profil "Trajet quotidien"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je pars de chez moi vers mon travail tous les matins à 8h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte ce trajet récurrent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le profil "Trajet quotidien" est activé automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois une notification "Profil 'Trajet quotidien' activé"</p>
|
||
<hr />
|
||
<h2 id="16-desactiver-lauto-switch">16. Désactiver l'auto-switch</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'auto-switch de profil est actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive cette option dans les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les profils ne changent plus automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois les activer manuellement</p>
|
||
<hr />
|
||
<h2 id="17-blocage-modification-si-vitesse-gps-10-kmh">17. Blocage modification si vitesse GPS >10 km/h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je conduis à 50 km/h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier un curseur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est bloquée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Modification impossible pendant la conduite"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois m'arrêter ou être passager pour modifier</p>
|
||
<hr />
|
||
<h2 id="18-modification-possible-si-vitesse-10-kmh">18. Modification possible si vitesse <10 km/h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis arrêté à un feu rouge (5 km/h)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier un curseur</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est autorisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux ajuster les paramètres</p>
|
||
<hr />
|
||
<h2 id="19-warning-au-lancement-app">19. Warning au lancement app</h2>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lance l'application pour la première fois</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un warning "Configurez vos préférences avant de prendre la route"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Configurer maintenant"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accéder rapidement aux paramètres</p>
|
||
<hr />
|
||
<h2 id="20-modification-uniquement-app-arretee-ou-mode-passager">20. Modification uniquement app arrêtée ou mode passager</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis passager dans une voiture
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le mode passager est activé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de modifier les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la modification est autorisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le blocage vitesse GPS ne s'applique pas</p>
|
||
<hr />
|
||
<h2 id="21-statistiques-dutilisation-des-profils">21. Statistiques d'utilisation des profils</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise plusieurs profils</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte mes statistiques</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois:</p>
|
||
<pre><code>| metrique | exemple |
|
||
|---|---|
|
||
| Profil le plus utilisé | Trajet quotidien |
|
||
| Heures par profil | 25h / 10h / 5h |
|
||
| Dernier profil actif | Road trip |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="22-supprimer-un-profil">22. Supprimer un profil</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai créé un profil "Test"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je supprime ce profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le profil est définitivement supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Profil 'Test' supprimé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il disparaît de tous mes devices</p>
|
||
<hr />
|
||
<h2 id="23-limite-de-profils-par-utilisateur">23. Limite de profils par utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai créé 10 profils</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de créer un 11ème profil</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la création échoue
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois le message "Maximum 10 profils par utilisateur"</p>
|
||
<hr />
|
||
<h2 id="24-dupliquer-un-profil-existant">24. Dupliquer un profil existant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un profil "Trajet quotidien"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Dupliquer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un nouveau profil "Trajet quotidien (copie)" est créé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il a les mêmes paramètres que l'original
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux le modifier indépendamment</p>
|
||
<hr />
|
||
<h2 id="25-reinitialiser-un-profil-aux-valeurs-par-defaut">25. Réinitialiser un profil aux valeurs par défaut</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai modifié un profil</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Réinitialiser"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les paramètres reviennent aux valeurs par défaut:</p>
|
||
<pre><code>| parametre | valeur défaut |
|
||
|---|---|
|
||
| Géolocalisation | Équilibré |
|
||
| Découverte | 10% |
|
||
| Politique | Équilibré |
|
||
</code></pre>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="formule-de-scoring-et-recommandation">Formule de scoring et recommandation</h1>
|
||
<blockquote>
|
||
<p><em>En tant que système de recommandation</em>
|
||
<em>Je veux calculer un score combiné pour chaque contenu</em>
|
||
<em>Afin de proposer les contenus les plus pertinents à l'utilisateur</em></p>
|
||
</blockquote>
|
||
<p><strong>21 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'API RoadWave est disponible</p>
|
||
</blockquote>
|
||
<h2 id="1-calcul-du-score-geographique-lineaire">1. Calcul du score géographique linéaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu existe à Paris
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la distance_max_km est configurée à 200 km</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur est à 50 km du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score_geo = 1 - (50 / 200) = 0.75</p>
|
||
<hr />
|
||
<h2 id="2-score-geo-a-distance-nulle-sur-place">2. Score géo à distance nulle (sur place)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu existe à un point GPS précis</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur est exactement au même point (0 km)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score_geo = 1.0 (maximum)</p>
|
||
<hr />
|
||
<h2 id="3-score-geo-a-distance_max-200-km">3. Score géo à distance_max (200 km)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu existe à Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur est à 200 km du contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score_geo = 1 - (200 / 200) = 0.0</p>
|
||
<hr />
|
||
<h2 id="4-score-geo-au-dela-de-distance_max">4. Score géo au-delà de distance_max</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu existe à Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur est à 250 km du contenu (au-delà de 200 km max)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score_geo = 0.0 (minimum)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu a peu de chances d'être recommandé sauf engagement très élevé</p>
|
||
<hr />
|
||
<h2 id="5-calcul-du-score-dinterets-avec-jauges-utilisateur">5. Calcul du score d'intérêts avec jauges utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a les jauges suivantes:</p>
|
||
<pre><code>| categorie | niveau |
|
||
|---|---|
|
||
| Automobile | 80% |
|
||
| Voyage | 60% |
|
||
| Musique | 40% |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu est tagué "Automobile" et "Voyage"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_interets</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> score_interets = (0.8 + 0.6) / 2 = 0.7</p>
|
||
<hr />
|
||
<h2 id="6-score-dinterets-avec-un-seul-tag">6. Score d'intérêts avec un seul tag</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a la jauge "Économie" à 90%
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu est tagué uniquement "Économie"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_interets</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> score_interets = 0.9</p>
|
||
<hr />
|
||
<h2 id="7-score-dinterets-avec-tags-non-matches">7. Score d'intérêts avec tags non matchés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a des jauges "Sport" et "Politique" élevées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un contenu est tagué "Musique" et "Philosophie"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur n'a pas ces catégories</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_interets</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> score_interets = 0.5 (neutre par défaut pour catégories inconnues)</p>
|
||
<hr />
|
||
<h2 id="8-calcul-du-score-dengagement-avec-metriques">8. Calcul du score d'engagement avec métriques</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a:</p>
|
||
<pre><code>| metrique | valeur |
|
||
|---|---|
|
||
| ecoutes | 1000 |
|
||
| ecoutes_completes | 700 |
|
||
| likes | 300 |
|
||
| abonnements_apres | 50 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_engagement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> taux_completion = 700 / 1000 = 0.7
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ratio_likes = 300 / 1000 = 0.3
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ratio_abonnements = 50 / 1000 = 0.05
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> score_engagement = (0.7 × 0.5) + (0.3 × 0.3) + (0.05 × 0.2) = 0.35 + 0.09 + 0.01 = 0.45</p>
|
||
<hr />
|
||
<h2 id="9-contenu-avec-moins-de-50-ecoutes-score-neutre">9. Contenu avec moins de 50 écoutes - score neutre</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a seulement 30 écoutes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_engagement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> score_engagement = 0.5 (neutre par défaut)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu n'est pas pénalisé pour manque de données</p>
|
||
<hr />
|
||
<h2 id="10-contenu-avec-exactement-50-ecoutes-calcul-reel">10. Contenu avec exactement 50 écoutes - calcul réel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu a exactement 50 écoutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> des métriques d'engagement complètes</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_engagement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score est calculé normalement (pas de seuil neutre)</p>
|
||
<hr />
|
||
<h2 id="11-bonus-aleatoire-10-des-recommandations">11. Bonus aléatoire - 10% des recommandations</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur demande 10 recommandations
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la part_aleatoire_global est à 10%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 1 contenu sur 10 est tiré aléatoirement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 9 contenus sont calculés avec le score combiné
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu aléatoire n'est pas dans l'historique déjà écouté</p>
|
||
<hr />
|
||
<h2 id="12-curseur-utilisateur-decouverte-a-0-aucun-aleatoire">12. Curseur utilisateur découverte à 0% - aucun aléatoire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur configure le curseur découverte à 0%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur demande 20 recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les 20 contenus sont calculés avec le score combiné
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun contenu aléatoire n'est proposé</p>
|
||
<hr />
|
||
<h2 id="13-curseur-utilisateur-decouverte-a-50-decouverte-max">13. Curseur utilisateur découverte à 50% - découverte max</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur configure le curseur découverte à 50%</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur demande 20 recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 10 contenus sont tirés aléatoirement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> 10 contenus sont calculés avec le score combiné</p>
|
||
<hr />
|
||
<h2 id="14-score-final-combine-pour-contenu-geo-ancre">14. Score final combiné pour contenu géo-ancré</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu "Géo-ancré" a:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_final</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> score_final = (0.9 × 0.7) + (0.6 × 0.1) + (0.45 × 0.2)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> score_final = 0.63 + 0.06 + 0.09 = 0.78</p>
|
||
<hr />
|
||
<h2 id="15-score-final-combine-pour-contenu-geo-neutre">15. Score final combiné pour contenu géo-neutre</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu "Géo-neutre" a:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_final</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> score_final = (0.3 × 0.2) + (0.9 × 0.6) + (0.6 × 0.2)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> score_final = 0.06 + 0.54 + 0.12 = 0.72
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu peut être recommandé malgré la distance</p>
|
||
<hr />
|
||
<h2 id="16-contenu-viral-lointain-peut-etre-recommande">16. Contenu viral lointain peut être recommandé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contenu viral existe à Paris
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il a un score_engagement très élevé de 0.95
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'un utilisateur est à Marseille (score_geo = 0.1)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme calcule le score_final</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le score_engagement élevé compense le score_geo faible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu peut apparaître dans les recommandations</p>
|
||
<hr />
|
||
<h2 id="17-ordre-de-recommandation-par-score-decroissant">17. Ordre de recommandation par score décroissant</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> 5 contenus avec les scores suivants:</p>
|
||
<pre><code>| contenu | score_final |
|
||
|---|---|
|
||
| Contenu A | 0.85 |
|
||
| Contenu B | 0.72 |
|
||
| Contenu C | 0.90 |
|
||
| Contenu D | 0.65 |
|
||
| Contenu E | 0.78 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur demande des recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'ordre de proposition est:</p>
|
||
<pre><code>| position | contenu |
|
||
|---|---|
|
||
| 1 | Contenu C |
|
||
| 2 | Contenu A |
|
||
| 3 | Contenu E |
|
||
| 4 | Contenu B |
|
||
| 5 | Contenu D |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="18-exclusion-de-lhistorique-deja-ecoute-80">18. Exclusion de l'historique déjà écouté >80%</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur a écouté les contenus suivants:</p>
|
||
<pre><code>| contenu | completion |
|
||
|---|---|
|
||
| Contenu A | 85% |
|
||
| Contenu B | 95% |
|
||
| Contenu C | 30% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme génère les recommandations</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> "Contenu A" et "Contenu B" ne sont jamais proposés
|
||
<span style="color: #F44336"><strong>Mais</strong></span> "Contenu C" peut être reproposé</p>
|
||
<hr />
|
||
<h2 id="19-pre-calcul-de-5-contenus-suivants">19. Pré-calcul de 5 contenus suivants</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un utilisateur écoute un contenu</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'algorithme prépare les contenus suivants</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> 5 contenus sont pré-calculés selon le score
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ces contenus sont mis en cache pour performance</p>
|
||
<hr />
|
||
<h2 id="20-recalcul-si-deplacement-10-km">20. Recalcul si déplacement >10 km</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 5 contenus suivants sont pré-calculés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'utilisateur se déplace de 12 km</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur demande le contenu suivant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'algorithme recalcule les scores avec la nouvelle position
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> propose de nouveaux contenus plus pertinents géographiquement</p>
|
||
<hr />
|
||
<h2 id="21-recalcul-apres-10-minutes-dinactivite">21. Recalcul après 10 minutes d'inactivité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 5 contenus suivants sont pré-calculés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que 11 minutes se sont écoulées sans action</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'utilisateur demande le contenu suivant</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'algorithme recalcule les scores
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> prend en compte les nouveaux contenus publiés</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="anonymisation-des-donnees-gps-apres-24h">Anonymisation des données GPS après 24h</h1>
|
||
<p><strong>18 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur avec le GPS activé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'utilise l'application depuis plusieurs jours</p>
|
||
</blockquote>
|
||
<h2 id="1-conservation-des-donnees-gps-precises-pendant-24h">1. Conservation des données GPS précises pendant 24h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'écoute un contenu à la position GPS 48.8566, 2.3522 (Paris, Tour Eiffel)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> qu'il est 10:00 le 2025-01-20</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'événement d'écoute est enregistré en base de données</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les coordonnées précises 48.8566, 2.3522 sont stockées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le champ <code>anonymized</code> est à <code>false</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le champ <code>created_at</code> contient "2025-01-20 10:00:00"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ces données précises servent à la recommandation personnalisée</p>
|
||
<hr />
|
||
<h2 id="2-conversion-en-geohash-apres-24h">2. Conversion en geohash après 24h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté un contenu le 2025-01-20 à 10:00 à la position 48.8566, 2.3522</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job quotidien d'anonymisation s'exécute le 2025-01-21 à 02:00</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les coordonnées précises sont converties en geohash précision 5
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le geohash correspond à une zone d'environ 5km²
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les coordonnées originales 48.8566, 2.3522 sont supprimées définitivement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le champ <code>anonymized</code> passe à <code>true</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est impossible de retrouver la position précise d'origine</p>
|
||
<hr />
|
||
<h2 id="3-requete-sql-danonymisation-postgis">3. Requête SQL d'anonymisation (PostGIS)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le job quotidien d'anonymisation s'exécute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la requête SQL suivante est exécutée:</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes les positions vieilles de plus de 24h sont anonymisées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le processus est automatique et irréversible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les données sont conformes RGPD</p>
|
||
<hr />
|
||
<h2 id="4-precision-du-geohash-niveau-5">4. Précision du geohash niveau 5</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une position GPS est convertie en geohash précision 5</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on analyse la zone couverte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la zone fait environ 5km² (4.9km × 4.9km)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette précision est suffisante pour des analytics agrégées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette précision ne permet pas d'identifier un individu (conformité CNIL)</p>
|
||
<hr />
|
||
<h2 id="5-exemple-de-conversion-paris">5. Exemple de conversion Paris</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que ma position précise est 48.8566, 2.3522 (Tour Eiffel)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la conversion en geohash précision 5 est appliquée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le geohash généré est "u09wh"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce geohash couvre une zone de ~5km² autour de la Tour Eiffel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les positions dans cette zone partagent le même geohash
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est impossible de distinguer deux utilisateurs dans cette zone</p>
|
||
<hr />
|
||
<h2 id="6-conservation-de-lhistorique-personnel-utilisateur">6. Conservation de l'historique personnel utilisateur</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté des contenus aux positions suivantes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre mon historique personnel dans "Profil > Mes trajets"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois mes trajets avec les positions précises intégrales
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ces données ne sont pas anonymisées tant que mon compte est actif
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul moi peut accéder à ces données
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elles ne sont pas utilisées pour des analytics globales</p>
|
||
<hr />
|
||
<h2 id="7-anonymisation-pour-analytics-globales-uniquement">7. Anonymisation pour analytics globales uniquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave génère des analytics agrégées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe analyse les zones géographiques populaires</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules les données anonymisées (geohash) sont utilisées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les positions précises de l'historique personnel ne sont jamais agrégées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les heatmaps de trafic utilisent uniquement les geohash ~5km²</p>
|
||
<hr />
|
||
<h2 id="8-planification-du-job-danonymisation">8. Planification du job d'anonymisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système est en production</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on consulte les jobs planifiés (cron)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job "anonymize_gps_data" est configuré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le job s'exécute tous les jours à 02:00 (heure creuse)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le job traite toutes les positions vieilles de plus de 24h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un log est généré pour traçabilité</p>
|
||
<hr />
|
||
<h2 id="9-execution-du-job-avec-metriques">9. Exécution du job avec métriques</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le job d'anonymisation s'exécute le 2025-01-21 à 02:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un rapport est généré avec:</p>
|
||
<pre><code>| métrique | valeur |
|
||
|---|---|
|
||
| Nombre de positions traitées | 15420 |
|
||
| Nombre de positions anonymisées | 15420 |
|
||
| Durée d'exécution | 3.5s |
|
||
| Erreurs | 0 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le rapport est loggé dans Sentry/Grafana
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une alerte est envoyée si le job échoue</p>
|
||
<hr />
|
||
<h2 id="10-performances-du-job-danonymisation">10. Performances du job d'anonymisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que 100 000 positions doivent être anonymisées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le traitement se fait en moins de 30 secondes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la requête PostGIS est optimisée avec index
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun impact sur les performances de l'application en production</p>
|
||
<hr />
|
||
<h2 id="11-impossibilite-de-reidentification">11. Impossibilité de réidentification</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une position a été anonymisée en geohash "u09wh"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un attaquant tente de retrouver la position précise d'origine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> il est impossible de déterminer la position exacte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> des milliers de positions précises correspondent au même geohash
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il n'y a aucune traçabilité vers la position originale
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette anonymisation est irréversible</p>
|
||
<hr />
|
||
<h2 id="12-conformite-cnil-donnees-veritablement-anonymisees">12. Conformité CNIL - données véritablement anonymisées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que les positions sont converties en geohash précision 5</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur CNIL vérifie la conformité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les données sont considérées comme véritablement anonymisées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elles ne sont plus considérées comme des données personnelles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun consentement n'est requis pour leur traitement analytique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elles peuvent être conservées indéfiniment</p>
|
||
<hr />
|
||
<h2 id="13-heatmap-de-trafic-avec-donnees-anonymisees">13. Heatmap de trafic avec données anonymisées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave génère une heatmap des zones populaires</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on analyse les données utilisées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seules les positions anonymisées (geohash) sont agrégées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la heatmap montre des zones de ~5km²
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune position précise n'est révélée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette analyse ne nécessite pas de consentement utilisateur (données anonymes)</p>
|
||
<hr />
|
||
<h2 id="14-statistiques-geographiques-par-departement">14. Statistiques géographiques par département</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave analyse l'utilisation par département</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les statistiques sont générées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les données anonymisées sont agrégées par département
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les résultats montrent: "Paris (75): 12 500 écoutes, Lyon (69): 8 300 écoutes"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée personnelle n'est révélée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les statistiques sont RGPD-compliant</p>
|
||
<hr />
|
||
<h2 id="15-cout-de-la-solution-danonymisation">15. Coût de la solution d'anonymisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que PostGIS est utilisé pour l'anonymisation GPS</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût de la solution</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût est de 0€ (PostGIS inclus dans PostgreSQL)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune librairie tierce n'est nécessaire
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la solution est entièrement maîtrisée (self-hosted)</p>
|
||
<hr />
|
||
<h2 id="16-anonymisation-respecte-les-positions-en-cours-de-session">16. Anonymisation respecte les positions en cours de session</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis en train d'écouter du contenu actuellement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que certaines de mes positions ont plus de 24h</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job d'anonymisation s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes positions de plus de 24h sont anonymisées
|
||
<span style="color: #F44336"><strong>Mais</strong></span> ma position actuelle (session en cours) reste précise
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la recommandation continue de fonctionner normalement</p>
|
||
<hr />
|
||
<h2 id="17-suppression-de-compte-et-anonymisation-gps">17. Suppression de compte et anonymisation GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je demande la suppression de mon compte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le compte est supprimé (après grace period de 30j)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> toutes mes positions GPS (précises et anonymisées) sont supprimées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon historique personnel de trajets est supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée GPS ne subsiste, même anonymisée</p>
|
||
<hr />
|
||
<h2 id="18-export-de-donnees-avant-anonymisation">18. Export de données avant anonymisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je demande un export de mes données
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que certaines de mes positions ont été anonymisées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les positions précises de mon historique personnel sont incluses
|
||
<span style="color: #F44336"><strong>Mais</strong></span> les positions déjà anonymisées (>24h, analytics) apparaissent en geohash
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'export précise quelles données ont été anonymisées et pourquoi</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="conformite-administrative-rgpd-registre-breach-dpo">Conformité administrative RGPD (Registre, Breach, DPO)</h1>
|
||
<p><strong>22 scénarios</strong> (21 standards, 1 plan)</p>
|
||
<hr />
|
||
<h2 id="1-registre-des-traitements-en-markdown-versionne-git">1. Registre des traitements en Markdown versionné Git</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave doit tenir un registre des traitements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on consulte la documentation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un fichier <code>docs/rgpd/registre-traitements.md</code> existe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fichier est versionné dans Git
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique des modifications est traçable via Git
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque traitement est documenté dans une section dédiée</p>
|
||
<hr />
|
||
<h2 id="2-contenu-obligatoire-pour-chaque-traitement">2. Contenu obligatoire pour chaque traitement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le registre des traitements contient le traitement "Géolocalisation utilisateurs"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on lit la section correspondante</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les informations suivantes sont présentes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="3-plan-traitements-documentes-dans-le-registre">3. 📋 Plan: Traitements documentés dans le registre</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le registre des traitements est complet</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on liste tous les traitements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le traitement "<traitement>" est documenté avec la base légale "<base_legale>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>traitement</th>
|
||
<th>base_legale</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Géolocalisation utilisateurs</td>
|
||
<td>Consentement</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Historique d'écoute</td>
|
||
<td>Intérêt légitime</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Création de contenu</td>
|
||
<td>Exécution du contrat</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Analytics (Matomo)</td>
|
||
<td>Consentement</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Paiements (Mangopay)</td>
|
||
<td>Exécution du contrat</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Modération contenus</td>
|
||
<td>Intérêt légitime</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Notifications push</td>
|
||
<td>Consentement</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="4-review-trimestrielle-du-registre">4. Review trimestrielle du registre</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le registre des traitements existe</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on consulte l'historique Git</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une mise à jour est effectuée au moins tous les 3 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque mise à jour a un commit avec message explicite
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un tag Git marque chaque review trimestrielle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les modifications sont traçables (auteur, date, changements)</p>
|
||
<hr />
|
||
<h2 id="5-mise-a-jour-immediate-si-nouveau-traitement">5. Mise à jour immédiate si nouveau traitement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une nouvelle fonctionnalité nécessite un traitement de données</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la fonctionnalité est développée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le registre est mis à jour AVANT le déploiement en production
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le nouveau traitement est documenté complètement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un commit Git enregistre l'ajout
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le DPO valide la conformité RGPD du nouveau traitement</p>
|
||
<hr />
|
||
<h2 id="6-migration-future-vers-interface-admin-postgresql">6. Migration future vers interface admin PostgreSQL</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave dépasse 100 000 utilisateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la complexité du registre augmente</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une interface admin PostgreSQL est développée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le registre Markdown est migré vers la base de données
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique Git est conservé pour audit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'interface permet une gestion plus efficace des traitements</p>
|
||
<hr />
|
||
<h2 id="7-detection-automatique-devenements-critiques">7. Détection automatique d'événements critiques</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de monitoring est actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un événement critique se produit</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte est envoyée selon le type d'événement:</p>
|
||
<pre><code>| é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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> les alertes permettent une réaction rapide</p>
|
||
<hr />
|
||
<h2 id="8-runbook-de-procedure-breach-disponible">8. Runbook de procédure breach disponible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une violation de données potentielle est détectée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe consulte la documentation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un runbook <code>docs/rgpd/procedure-breach.md</code> existe
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le runbook contient une checklist 72h CNIL
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque étape est clairement documentée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contacts d'urgence sont listés</p>
|
||
<hr />
|
||
<h2 id="9-checklist-72h-en-cas-de-breach">9. Checklist 72h en cas de breach</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une violation de données est confirmée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe suit la procédure breach</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les étapes suivantes sont exécutées dans les délais:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque étape est documentée pour audit</p>
|
||
<hr />
|
||
<h2 id="10-evaluation-de-la-gravite-du-breach">10. Évaluation de la gravité du breach</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'une violation de données est détectée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe évalue la gravité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les critères suivants sont analysés:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> si le risque est élevé, la CNIL est notifiée sous 72h</p>
|
||
<hr />
|
||
<h2 id="11-notification-cnil-dans-les-72h">11. Notification CNIL dans les 72h</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un breach avec risque élevé est confirmé à 10:00 le 2025-01-20</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la gravité est évaluée comme nécessitant une notification</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la CNIL est notifiée avant 10:00 le 2025-01-23 (72h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la notification contient:</p>
|
||
<pre><code>| information |
|
||
|---|
|
||
| Nature de la violation |
|
||
| Données concernées |
|
||
| Nombre d'utilisateurs impactés |
|
||
| Conséquences probables |
|
||
| Mesures prises |
|
||
| Mesures de remédiation |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> un email pré-rédigé (template) est utilisé pour gagner du temps</p>
|
||
<hr />
|
||
<h2 id="12-notification-des-utilisateurs-si-risque-eleve">12. Notification des utilisateurs si risque élevé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un breach impacte 5000 utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que le risque est élevé (mots de passe non chiffrés exposés)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la CNIL est notifiée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les utilisateurs impactés sont notifiés dans les 72h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'email contient:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un lien de réinitialisation de mot de passe est inclus</p>
|
||
<hr />
|
||
<h2 id="13-aucune-notification-si-risque-faible">13. Aucune notification si risque faible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un breach mineur est détecté (logs techniques exposés, aucune donnée personnelle)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe évalue la gravité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le risque est jugé faible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune notification CNIL n'est requise (Article 33.1 RGPD)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune notification utilisateur n'est envoyée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un log interne est créé pour traçabilité</p>
|
||
<hr />
|
||
<h2 id="14-monitoring-proactif-pour-eviter-decouverte-tardive">14. Monitoring proactif pour éviter découverte tardive</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Sentry et Grafana sont configurés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un comportement anormal est détecté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une alerte est envoyée en temps réel
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'équipe peut réagir avant qu'un breach majeur ne se produise
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les logs sont analysés quotidiennement pour détecter des anomalies
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette approche proactive limite les risques de découverte tardive</p>
|
||
<hr />
|
||
<h2 id="15-fondateur-dpo-temporaire-mvp">15. Fondateur = DPO temporaire (MVP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave est en phase MVP
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'entreprise a moins de 250 employés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on vérifie l'obligation légale d'avoir un DPO</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le DPO n'est pas obligatoire selon le RGPD Article 37
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fondateur assume temporairement le rôle de DPO
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fondateur suit la formation CNIL gratuite (4h)</p>
|
||
<hr />
|
||
<h2 id="16-formation-cnil-du-dpo-temporaire">16. Formation CNIL du DPO temporaire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le fondateur est DPO temporaire</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on vérifie sa formation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fondateur a suivi la formation CNIL en ligne (4h)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le fondateur a obtenu la certification "Atelier RGPD" (gratuit)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le certificat est conservé pour audit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la formation couvre:</p>
|
||
<pre><code>| sujet |
|
||
|---|
|
||
| Principes fondamentaux du RGPD |
|
||
| Droits des personnes |
|
||
| Sécurité des données |
|
||
| Violations de données (breach) |
|
||
| Registre des traitements |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-contact-dpo-publie-et-accessible">17. Contact DPO publié et accessible</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte les mentions légales de RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je cherche le contact du DPO</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'email "dpo@roadwave.fr" est clairement affiché
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cet email est également dans les CGU
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le délai de réponse garanti est de 1 mois maximum (RGPD Article 12.3)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une adresse postale est également fournie</p>
|
||
<hr />
|
||
<h2 id="18-demande-dexercice-de-droits-rgpd-au-dpo">18. Demande d'exercice de droits RGPD au DPO</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux exercer mon droit d'accès à mes données</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'envoie un email à dpo@roadwave.fr</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un accusé de réception dans les 48h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma demande est traitée dans un délai maximum de 1 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si le délai dépasse 1 mois, je suis informé de la prolongation (max 2 mois supplémentaires)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la réponse est complète et conforme au RGPD</p>
|
||
<hr />
|
||
<h2 id="19-types-de-demandes-gerees-par-le-dpo">19. Types de demandes gérées par le DPO</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je contacte le DPO</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'envoie une demande</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le DPO peut traiter les demandes suivantes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque demande reçoit une réponse personnalisée</p>
|
||
<hr />
|
||
<h2 id="20-migration-vers-dpo-externe-si-croissance">20. Migration vers DPO externe si croissance</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave dépasse 100 000 utilisateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la charge de travail DPO augmente</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un DPO externe mutualisé est engagé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût est d'environ 200€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le DPO externe a les certifications CNIL requises
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un contrat de sous-traitance RGPD est signé</p>
|
||
<hr />
|
||
<h2 id="21-recrutement-dpo-interne-si-10-employes">21. Recrutement DPO interne si >10 employés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave a plus de 10 employés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'entreprise se structure</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un DPO interne peut être recruté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le DPO interne a une certification CNIL (AFCDP ou équivalent)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le DPO est indépendant et ne peut être licencié pour ses fonctions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le DPO a un accès direct à la direction</p>
|
||
<hr />
|
||
<h2 id="22-recapitulatif-des-couts-rgpd">22. Récapitulatif des coûts RGPD</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que toutes les mesures RGPD sont en place</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût total mensuel</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le récapitulatif est le suivant:</p>
|
||
<pre><code>| 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€ |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le coût total est d'environ 5€/mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette conformité est 100% opensource et maîtrisée</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="gestion-du-consentement-rgpd">Gestion du consentement RGPD</h1>
|
||
<p><strong>16 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouvel utilisateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'accède à l'application pour la première fois</p>
|
||
</blockquote>
|
||
<h2 id="1-affichage-du-banner-de-consentement-au-premier-lancement-web">1. Affichage du banner de consentement au premier lancement web</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à l'application web pour la première fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page se charge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un banner RGPD Tarteaucitron.js s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le banner est en français
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le banner propose les options suivantes:</p>
|
||
<pre><code>| option | description |
|
||
|---|---|
|
||
| Tout accepter | Active tous les consentements |
|
||
| Tout refuser | Refuse tous les consentements optionnels |
|
||
| Personnaliser | Ouvre le panneau de personnalisation |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le banner est customisé aux couleurs de RoadWave</p>
|
||
<hr />
|
||
<h2 id="2-granularite-des-consentements">2. Granularité des consentements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le banner RGPD est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Personnaliser"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois les catégories de consentements suivantes:</p>
|
||
<pre><code>| catégorie | type | requis |
|
||
|---|---|---|
|
||
| Fonctionnel | Nécessaire | oui |
|
||
| Analytique | Optionnel | non |
|
||
| Marketing | Optionnel | non |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque catégorie a une description claire de son usage
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accepter ou refuser chaque catégorie individuellement</p>
|
||
<hr />
|
||
<h2 id="3-consentement-geolocalisation-precise-obligatoire">3. Consentement géolocalisation précise - obligatoire</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis sur l'application mobile
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que l'onboarding est terminé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application a besoin d'accéder à ma position précise</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un écran de demande de consentement s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le message explique clairement l'usage:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accepter ou refuser
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je refuse, l'application bascule en mode dégradé (GeoIP uniquement)</p>
|
||
<hr />
|
||
<h2 id="4-double-consentement-gps-banner-app-permission-os">4. Double consentement GPS - banner app + permission OS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je veux activer la géolocalisation précise</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accepte le consentement dans l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'application demande également la permission au système d'exploitation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sur iOS, la popup système s'affiche: "Autoriser RoadWave à accéder à votre position ?"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> sur Android, la popup système s'affiche avec les options "Toujours autoriser / Autoriser seulement pendant l'utilisation / Refuser"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les deux consentements (app + OS) doivent être acceptés pour activer le GPS précis</p>
|
||
<hr />
|
||
<h2 id="5-enregistrement-du-consentement-en-base-de-donnees">5. Enregistrement du consentement en base de données</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai accepté les consentements suivants:</p>
|
||
<pre><code>| type | accepté |
|
||
|---|---|
|
||
| Fonctionnel | oui |
|
||
| Analytique | oui |
|
||
| Marketing | non |
|
||
| GPS précis | oui |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je valide mes choix</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un enregistrement est créé dans la table <code>user_consents</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'enregistrement contient les champs suivants:</p>
|
||
<pre><code>| champ | valeur |
|
||
|---|---|
|
||
| user_id | [mon ID utilisateur] |
|
||
| consent_type | fonctionnel / analytique / gps |
|
||
| version | 1 |
|
||
| accepted | true / false |
|
||
| timestamp | [date et heure exacte] |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque type de consentement a un enregistrement séparé</p>
|
||
<hr />
|
||
<h2 id="6-versioning-des-consentements">6. Versioning des consentements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai accepté le consentement "Analytique" version 1 le 2025-01-01
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que les CGU sont mises à jour le 2025-06-01</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte après la mise à jour</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un nouveau consentement version 2 m'est demandé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon ancien consentement version 1 reste dans l'historique
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois accepter la nouvelle version pour continuer à utiliser les analytics</p>
|
||
<hr />
|
||
<h2 id="7-historique-complet-conserve-pour-preuve-legale">7. Historique complet conservé pour preuve légale</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai modifié mes consentements plusieurs fois:</p>
|
||
<pre><code>| date | consent_type | accepted | version |
|
||
|---|---|---|---|
|
||
| 2025-01-01 | Analytique | oui | 1 |
|
||
| 2025-03-15 | Analytique | non | 1 |
|
||
| 2025-06-01 | Analytique | oui | 2 |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur CNIL consulte mon historique de consentements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les enregistrements sont conservés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'historique prouve que chaque consentement a été donné librement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les timestamps permettent de prouver la conformité à tout moment</p>
|
||
<hr />
|
||
<h2 id="8-consentement-analytique-optionnel">8. Consentement analytique - optionnel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je refuse le consentement "Analytique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun cookie Matomo <code>_pk_id</code> n'est déposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée d'usage n'est envoyée à Matomo
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application fonctionne normalement sans analytics</p>
|
||
<hr />
|
||
<h2 id="9-consentement-notifications-push-optionnel">9. Consentement notifications push - optionnel</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je refuse le consentement "Notifications push"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un créateur que je suis publie un nouveau contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je ne reçois pas de notification push
|
||
<span style="color: #F44336"><strong>Mais</strong></span> je peux voir le nouveau contenu dans l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application fonctionne normalement</p>
|
||
<hr />
|
||
<h2 id="10-consentement-gps-precis-requis-pour-fonctionnalites-geo">10. Consentement GPS précis - requis pour fonctionnalités géo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je refuse le consentement "GPS précis"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux accéder aux contenus nationaux
|
||
<span style="color: #F44336"><strong>Mais</strong></span> les contenus géolocalisés précis (Ancré, Contextuel) ne sont pas disponibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les audio-guides nécessitent l'activation du GPS
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un banner permanent me rappelle que l'activation du GPS améliore l'expérience</p>
|
||
<hr />
|
||
<h2 id="11-revocation-dun-consentement-depuis-les-parametres">11. Révocation d'un consentement depuis les paramètres</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai accepté le consentement "Analytique"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'utilise l'application depuis 3 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre "Paramètres > Confidentialité > Gérer mes consentements"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois la liste de tous mes consentements actuels
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux révoquer le consentement "Analytique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je révoque le consentement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un nouvel enregistrement est créé avec <code>accepted = false</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le cookie Matomo est supprimé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les analytics sont désactivées à partir de ce moment</p>
|
||
<hr />
|
||
<h2 id="12-acceptation-dun-consentement-precedemment-refuse">12. Acceptation d'un consentement précédemment refusé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'avais refusé le consentement "GPS précis"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre "Paramètres > Confidentialité > Gérer mes consentements"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je clique sur "Activer la géolocalisation précise"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un nouvel enregistrement est créé avec <code>accepted = true</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la permission OS est demandée si ce n'est pas déjà fait
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application bascule en mode géolocalisation précise
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus géolocalisés deviennent disponibles immédiatement</p>
|
||
<hr />
|
||
<h2 id="13-export-de-lhistorique-des-consentements-pour-audit">13. Export de l'historique des consentements pour audit</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un contrôle CNIL est en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'équipe RoadWave exporte l'historique des consentements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'export contient pour chaque utilisateur:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> l'export est au format CSV pour analyse
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les données prouvent la conformité RGPD</p>
|
||
<hr />
|
||
<h2 id="14-conformite-recommandations-cnil">14. Conformité recommandations CNIL</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de consentement est implémenté</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur CNIL vérifie la conformité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système respecte les critères suivants:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="15-tarteaucitronjs-self-hosted">15. Tarteaucitron.js self-hosted</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que l'application web utilise Tarteaucitron.js</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte les sources JavaScript chargées</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le script Tarteaucitron.js est hébergé sur les serveurs RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun script tiers (CDN externe) n'est chargé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le code source de Tarteaucitron.js est vérifiable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée n'est envoyée à un tiers lors de l'affichage du banner</p>
|
||
<hr />
|
||
<h2 id="16-cout-de-la-solution-0">16. Coût de la solution - 0€</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Tarteaucitron.js est opensource
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que PostgreSQL est utilisé pour le backend</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût de la solution de consentement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût est de 0€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la solution est entièrement maîtrisée (self-hosted)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune dépendance à un service SaaS tiers</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="duree-de-conservation-des-donnees-et-purge-automatique">Durée de conservation des données et purge automatique</h1>
|
||
<p><strong>19 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de purge automatique est actif</p>
|
||
</blockquote>
|
||
<h2 id="1-auditeur-inactif-depuis-5-ans-suppression-automatique">1. Auditeur inactif depuis 5 ans - suppression automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un auditeur (sans contenu créé)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je ne me suis pas connecté depuis le 2020-01-01
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la date actuelle est 2025-01-02 (>5 ans)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de purge automatique s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est automatiquement supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes mes données personnelles sont effacées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune trace ne subsiste dans la base de données</p>
|
||
<hr />
|
||
<h2 id="2-createur-avec-contenus-actifs-conservation-indefinie">2. Créateur avec contenus actifs - conservation indéfinie</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai créé 10 contenus qui reçoivent encore des écoutes
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je ne me suis pas connecté depuis 6 ans</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de purge automatique s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte n'est pas supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes données personnelles sont conservées tant que mes contenus sont écoutés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes contenus continuent d'être diffusés normalement</p>
|
||
<hr />
|
||
<h2 id="3-createur-inactif-sans-ecoutes-suppression-automatique">3. Créateur inactif sans écoutes - suppression automatique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai créé 5 contenus
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je ne me suis pas connecté depuis 5 ans (depuis 2020-01-01)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes contenus n'ont reçu aucune écoute depuis 2 ans (depuis 2023-01-01)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que la date actuelle est 2025-01-02</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de purge automatique s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est automatiquement supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes contenus sont anonymisés (créateur = "Utilisateur supprimé")
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fichiers audio restent disponibles mais anonymisés</p>
|
||
<hr />
|
||
<h2 id="4-notifications-par-email-avant-purge">4. Notifications par email avant purge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis inactif depuis 4 ans et 9 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détecte que je suis éligible à la purge dans 90 jours</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email avec le sujet "Votre compte RoadWave sera supprimé dans 90 jours"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'email contient:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un lien de connexion est inclus dans l'email</p>
|
||
<hr />
|
||
<h2 id="5-rappels-a-90j-30j-et-7j-avant-suppression">5. Rappels à 90j, 30j et 7j avant suppression</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis éligible à la purge automatique</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les délais s'écoulent</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois les emails suivants:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque email contient un lien de connexion pour réactiver le compte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les notifications push sont également envoyées si activées</p>
|
||
<hr />
|
||
<h2 id="6-connexion-annule-la-suppression-programmee">6. Connexion annule la suppression programmée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis éligible à la purge dans 15 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai reçu plusieurs emails d'avertissement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte à mon compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la suppression programmée est annulée immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le compteur d'inactivité est remis à zéro
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de confirmation: "Votre compte a été réactivé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer à utiliser l'application normalement</p>
|
||
<hr />
|
||
<h2 id="7-execution-quotidienne-du-job-de-purge">7. Exécution quotidienne du job de purge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système est en production</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on consulte les jobs planifiés</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job "purge_inactive_accounts" est configuré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le job s'exécute tous les jours à 03:00 (heure creuse)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le job identifie les comptes éligibles à la purge
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le job traite les suppressions automatiques</p>
|
||
<hr />
|
||
<h2 id="8-criteres-deligibilite-a-la-purge">8. Critères d'éligibilité à la purge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le job de purge s'exécute</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système identifie les comptes éligibles</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les critères suivants sont appliqués:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> seuls les comptes remplissant tous les critères sont supprimés</p>
|
||
<hr />
|
||
<h2 id="9-metriques-du-job-de-purge">9. Métriques du job de purge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le job de purge s'exécute le 2025-01-15</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un rapport est généré avec:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le rapport est loggé pour audit</p>
|
||
<hr />
|
||
<h2 id="10-contenus-de-comptes-purges-conserves-anonymement">10. Contenus de comptes purgés conservés anonymement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte créateur est purgé automatiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression est effective</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes contenus créés sont conservés indéfiniment
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus sont anonymisés (créateur = "Utilisateur supprimé")
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fichiers audio restent sur le CDN
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les statistiques d'écoute sont préservées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs peuvent toujours écouter mes contenus</p>
|
||
<hr />
|
||
<h2 id="11-createur-inactif-mais-contenus-populaires-pas-de-purge">11. Créateur inactif mais contenus populaires - pas de purge</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur inactif depuis 6 ans
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que mes contenus reçoivent 500+ écoutes par mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de purge s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte n'est pas supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je continue de recevoir les emails d'avertissement tous les 6 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes contenus continuent d'être diffusés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux me reconnecter à tout moment</p>
|
||
<hr />
|
||
<h2 id="12-quest-ce-quune-ecoute-pour-le-calcul-dinactivite">12. Qu'est-ce qu'une "écoute" pour le calcul d'inactivité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système calcule si mes contenus sont "actifs"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une "écoute" est comptabilisée si:</p>
|
||
<pre><code>| condition | comptabilisée |
|
||
|---|---|
|
||
| Écoute complète (>80%) | oui |
|
||
| Écoute partielle (>30%) | oui |
|
||
| Skip rapide (<30%) | non |
|
||
| Écoute par un bot (détecté) | non |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> au moins 1 écoute valide dans les 2 dernières années maintient le compte actif</p>
|
||
<hr />
|
||
<h2 id="13-conformite-principe-de-minimisation">13. Conformité principe de minimisation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le système de purge automatique est en place</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur RGPD vérifie la conformité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système respecte le principe de minimisation:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le délai de 5 ans est conforme aux standards de l'industrie</p>
|
||
<hr />
|
||
<h2 id="14-actions-qui-reinitialisent-le-compteur-dinactivite">14. Actions qui réinitialisent le compteur d'inactivité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis inactif depuis 4 ans</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'effectue l'une des actions suivantes:</p>
|
||
<pre><code>| action |
|
||
|---|
|
||
| Connexion à l'application |
|
||
| Publication d'un nouveau contenu |
|
||
| Like d'un contenu |
|
||
| Abonnement à un créateur |
|
||
| Modification de mon profil |
|
||
</code></pre>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le compteur d'inactivité est remis à zéro
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la suppression programmée est annulée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne suis plus éligible à la purge pour 5 ans</p>
|
||
<hr />
|
||
<h2 id="15-tracabilite-des-suppressions-automatiques">15. Traçabilité des suppressions automatiques</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> qu'un compte est supprimé automatiquement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression est effective</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un log d'audit est créé avec:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le log est conservé 5 ans pour audit RGPD
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'user_id est pseudonymisé pour anonymat</p>
|
||
<hr />
|
||
<h2 id="16-compte-premium-inactif-pas-de-privilege-special">16. Compte Premium inactif - pas de privilège spécial</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur Premium
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je suis inactif depuis 5 ans</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de purge s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est supprimé comme un compte gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'abonnement Premium ne prolonge pas la durée de conservation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun remboursement n'est effectué (compte inactif depuis 5 ans)</p>
|
||
<hr />
|
||
<h2 id="17-compte-avec-signalements-de-moderation-purge-differee">17. Compte avec signalements de modération - purge différée</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis éligible à la purge
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que j'ai des signalements de modération en cours</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de purge s'exécute</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma purge est différée de 90 jours
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les signalements sont traités en priorité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si les signalements aboutissent à un ban, le compte est supprimé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si les signalements sont infondés, la purge automatique reprend son cours</p>
|
||
<hr />
|
||
<h2 id="18-pourquoi-5-ans-dinactivite">18. Pourquoi 5 ans d'inactivité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le délai de purge est fixé à 5 ans</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on justifie ce choix</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les raisons suivantes sont avancées:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="19-politique-de-conservation-visible-dans-les-cgu">19. Politique de conservation visible dans les CGU</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je consulte les CGU de RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lis la section "Conservation des données"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la politique de purge automatique est clairement expliquée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les utilisateurs sont informés dès l'inscription</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="cookies-et-analytics-avec-matomo-self-hosted">Cookies et analytics avec Matomo self-hosted</h1>
|
||
<p><strong>20 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur de l'application web RoadWave</p>
|
||
</blockquote>
|
||
<h2 id="1-cookies-strictement-necessaires-pas-de-consentement-requis">1. Cookies strictement nécessaires - pas de consentement requis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à l'application web</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je me connecte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les cookies techniques suivants sont déposés:</p>
|
||
<pre><code>| cookie | type | durée | finalité | consentement |
|
||
|---|---|---|---|---|
|
||
| session | Technique | 30j | Authentification | Non requis |
|
||
| refresh_token | Technique | 30j | Session persistante | Non requis |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces cookies sont essentiels au fonctionnement de l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ils sont exemptés de consentement selon l'article 82 de la loi Informatique et Libertés</p>
|
||
<hr />
|
||
<h2 id="2-cookie-analytique-matomo-consentement-requis">2. Cookie analytique Matomo - consentement requis</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai accepté le consentement "Analytique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je navigue sur l'application web</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le cookie <code>_pk_id</code> est déposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la durée de conservation est de 13 mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce cookie sert à Matomo pour analytics
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon IP est automatiquement anonymisée (2 derniers octets)</p>
|
||
<hr />
|
||
<h2 id="3-refus-du-consentement-analytique-pas-de-cookie-matomo">3. Refus du consentement analytique - pas de cookie Matomo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai refusé le consentement "Analytique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je navigue sur l'application web</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun cookie <code>_pk_id</code> n'est déposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée d'usage n'est collectée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application fonctionne normalement sans analytics</p>
|
||
<hr />
|
||
<h2 id="4-matomo-heberge-sur-les-serveurs-roadwave">4. Matomo hébergé sur les serveurs RoadWave</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave utilise Matomo pour les analytics</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on analyse l'infrastructure</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Matomo est installé sur les serveurs RoadWave (Docker)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée n'est envoyée à un service tiers
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les données restent dans l'UE
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'accès à Matomo est restreint à l'équipe RoadWave</p>
|
||
<hr />
|
||
<h2 id="5-ip-anonymisees-automatiquement">5. IP anonymisées automatiquement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Matomo collecte des données d'usage</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> une requête est enregistrée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'adresse IP est automatiquement anonymisée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les 2 derniers octets sont remplacés par des zéros
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une IP 192.168.1.100 devient 192.168.0.0
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette anonymisation est irréversible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> elle est conforme aux recommandations CNIL</p>
|
||
<hr />
|
||
<h2 id="6-configuration-matomo-conforme-rgpd">6. Configuration Matomo conforme RGPD</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Matomo est configuré pour RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on vérifie les paramètres</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les configurations suivantes sont activées:</p>
|
||
<pre><code>| 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é |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> la configuration est RGPD-compliant</p>
|
||
<hr />
|
||
<h2 id="7-aucun-tracker-tiers-utilise">7. Aucun tracker tiers utilisé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'accède à l'application web</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'inspecte les requêtes réseau avec les DevTools</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune requête n'est envoyée vers les domaines suivants:</p>
|
||
<pre><code>| domaine tiers interdit |
|
||
|---|
|
||
| google-analytics.com |
|
||
| facebook.com (Pixel) |
|
||
| hotjar.com |
|
||
| mixpanel.com |
|
||
| segment.io |
|
||
| amplitude.com |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> toutes les requêtes analytics vont uniquement vers matomo.roadwave.fr</p>
|
||
<hr />
|
||
<h2 id="8-conformite-zero-cookie-tiers">8. Conformité zéro cookie tiers</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'analyse les cookies déposés sur roadwave.fr</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte la liste des cookies</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les cookies sont first-party (domaine roadwave.fr)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun cookie tiers (third-party) n'est présent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette politique respecte les recommandations CNIL 2020</p>
|
||
<hr />
|
||
<h2 id="9-alternative-plausible-saas-eu-hosted">9. Alternative Plausible SaaS (EU-hosted)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave pourrait utiliser Plausible au lieu de Matomo</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on compare les deux solutions</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Plausible a les caractéristiques suivantes:</p>
|
||
<pre><code>| 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) |
|
||
</code></pre>
|
||
<p><span style="color: #F44336"><strong>Mais</strong></span> Matomo self-hosted reste le choix prioritaire (0€, contrôle total)</p>
|
||
<hr />
|
||
<h2 id="10-aucun-transfert-de-donnees-hors-ue">10. Aucun transfert de données hors UE</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Matomo est self-hosted</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on analyse les flux de données</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune donnée d'analytics n'est transférée hors de l'UE
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les serveurs sont localisés en France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun transfert vers les US (pas de Privacy Shield / DPF requis)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la souveraineté des données est garantie</p>
|
||
<hr />
|
||
<h2 id="11-matomo-self-hosted-cout-estime">11. Matomo self-hosted - coût estimé</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Matomo est hébergé sur l'infrastructure RoadWave</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût mensuel</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût est d'environ 5€/mois:</p>
|
||
<pre><code>| 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) |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ce coût est marginal comparé à un SaaS tiers (9-50€/mois)</p>
|
||
<hr />
|
||
<h2 id="12-respect-du-signal-do-not-track-dnt">12. Respect du signal Do Not Track (DNT)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon navigateur envoie le header "DNT: 1"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accède à l'application web</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Matomo détecte le signal DNT
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée d'usage n'est collectée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun cookie <code>_pk_id</code> n'est déposé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application fonctionne normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un message discret s'affiche: "Vos préférences de confidentialité sont respectées (DNT activé)"</p>
|
||
<hr />
|
||
<h2 id="13-logs-matomo-supprimes-apres-25-mois">13. Logs Matomo supprimés après 25 mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Matomo collecte des données d'usage</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> les logs atteignent 25 mois d'ancienneté</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job automatique supprime les anciens logs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seules les données agrégées (rapports) sont conservées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les données brutes (logs) sont supprimées définitivement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette politique respecte le principe de minimisation RGPD</p>
|
||
<hr />
|
||
<h2 id="14-donnees-collectees-par-matomo">14. Données collectées par Matomo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai accepté le consentement "Analytique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je navigue sur l'application web</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> Matomo collecte les données suivantes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> aucune donnée personnelle identifiable n'est collectée</p>
|
||
<hr />
|
||
<h2 id="15-user-id-hashe-pour-analytics">15. User ID hashé pour analytics</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté à l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai accepté le consentement "Analytique"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> Matomo enregistre mes actions</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon user_id est hashé (SHA-256)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le hash est 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> il est impossible de retrouver mon user_id original depuis ce hash
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ce processus garantit l'anonymat</p>
|
||
<hr />
|
||
<h2 id="16-conformite-recommandations-cnil-sur-les-cookies">16. Conformité recommandations CNIL sur les cookies</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave utilise Matomo self-hosted</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur CNIL vérifie la conformité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système respecte les recommandations CNIL 2020:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="17-integration-tarteaucitronjs-pour-gerer-matomo">17. Intégration Tarteaucitron.js pour gérer Matomo</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Tarteaucitron.js gère les consentements</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je personnalise mes consentements</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois l'option "Analytique (Matomo)"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> une description est affichée:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux activer ou désactiver Matomo indépendamment
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je désactive, le cookie <code>_pk_id</code> est supprimé immédiatement</p>
|
||
<hr />
|
||
<h2 id="18-analytics-sur-application-mobile">18. Analytics sur application mobile</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise l'application mobile</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'accepte le consentement "Analytique"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'app utilise le SDK Matomo Mobile
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les données sont envoyées à la même instance Matomo self-hosted
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les mêmes règles d'anonymisation s'appliquent
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun SDK tiers (Google Analytics, Firebase) n'est utilisé</p>
|
||
<hr />
|
||
<h2 id="19-refus-analytics-sur-mobile">19. Refus analytics sur mobile</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai refusé le consentement "Analytique" sur mobile</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'utilise l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucune donnée d'usage n'est collectée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le SDK Matomo est désactivé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application fonctionne normalement sans différence d'UX</p>
|
||
<hr />
|
||
<h2 id="20-matomo-opensource-et-auditable">20. Matomo opensource et auditable</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que Matomo est opensource</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on consulte le code source</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le code est disponible publiquement sur GitHub
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le code peut être audité par des experts indépendants
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune backdoor ou collecte cachée n'est possible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette transparence renforce la confiance utilisateur</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="mode-degrade-avec-geoip-sans-gps-precis">Mode dégradé avec GeoIP (sans GPS précis)</h1>
|
||
<p><strong>20 scénarios</strong> (19 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un nouvel utilisateur
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je lance l'application pour la première fois</p>
|
||
</blockquote>
|
||
<h2 id="1-plan-trois-niveaux-de-geolocalisation-disponibles">1. 📋 Plan: Trois niveaux de géolocalisation disponibles</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise le niveau de géolocalisation "<niveau>"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détermine ma position</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la technologie utilisée est "<technologie>"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus accessibles sont "<contenus>"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le consentement RGPD est "<consentement>"</p>
|
||
<p><strong>📊 Exemples de données:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>niveau</th>
|
||
<th>technologie</th>
|
||
<th>contenus</th>
|
||
<th>consentement</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Pays</td>
|
||
<td>Aucune géoloc</td>
|
||
<td>Contenus nationaux uniquement</td>
|
||
<td>Non requis</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Ville</td>
|
||
<td>GeoIP (MaxMind)</td>
|
||
<td>Contenus régionaux/ville</td>
|
||
<td>Non requis</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Précis</td>
|
||
<td>GPS</td>
|
||
<td>Tous contenus (hyperlocaux inclus)</td>
|
||
<td>Requis</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2 id="2-geoip-active-par-defaut-au-premier-lancement">2. GeoIP activé par défaut au premier lancement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je lance l'application pour la première fois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas encore accepté le GPS précis</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'application démarre</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système utilise automatiquement GeoIP basé sur mon adresse IP
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma position est détectée au niveau ville: "Paris, France"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun consentement n'est requis (GeoIP ne collecte pas de données personnelles)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accéder aux contenus régionaux et de ville</p>
|
||
<hr />
|
||
<h2 id="3-detection-de-ville-avec-maxmind-geolite2">3. Détection de ville avec MaxMind GeoLite2</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon adresse IP est 93.184.216.34</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système utilise GeoIP MaxMind GeoLite2</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> ma ville est détectée: "Paris"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> ma région est détectée: "Île-de-France"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon pays est détecté: "France"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la précision est d'environ 80% au niveau ville
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune coordonnée GPS précise n'est révélée</p>
|
||
<hr />
|
||
<h2 id="4-banner-dinvitation-a-activer-le-gps">4. Banner d'invitation à activer le GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise l'application en mode GeoIP</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je suis sur l'écran principal</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un banner discret s'affiche en haut:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le banner n'est pas intrusif (pas de popup modale)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux le fermer temporairement avec un bouton X
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le banner réapparaît tous les 7 jours si je ne l'active pas</p>
|
||
<hr />
|
||
<h2 id="5-upgrade-volontaire-vers-gps-depuis-le-banner">5. Upgrade volontaire vers GPS depuis le banner</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que le banner d'invitation au GPS est affiché</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Activer"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un écran de consentement GPS s'affiche
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'écran explique les avantages:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux accepter ou refuser
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si j'accepte, la permission OS est demandée</p>
|
||
<hr />
|
||
<h2 id="6-contenus-disponibles-en-mode-pays-aucune-geoloc">6. Contenus disponibles en mode Pays (aucune géoloc)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je n'autorise aucune géolocalisation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu à me proposer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> seuls les contenus "National" sont disponibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus géolocalisés (Ancré, Contextuel) ne sont pas proposés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je vois un message: "Activez la géolocalisation pour plus de contenu local"</p>
|
||
<hr />
|
||
<h2 id="7-contenus-disponibles-en-mode-ville-geoip">7. Contenus disponibles en mode Ville (GeoIP)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise le mode GeoIP et que je suis détecté à Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu à me proposer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les contenus suivants sont disponibles:</p>
|
||
<pre><code>| type_contenu | disponible |
|
||
|---|---|
|
||
| National | oui |
|
||
| Région Île-de-France | oui |
|
||
| Ville Paris | oui |
|
||
| Hyperlocal (GPS) | non |
|
||
| Audio-guides | non |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> je reçois des recommandations pertinentes pour Paris</p>
|
||
<hr />
|
||
<h2 id="8-tous-contenus-disponibles-en-mode-precis-gps">8. Tous contenus disponibles en mode Précis (GPS)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai activé la géolocalisation précise</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système recherche du contenu à me proposer</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous les types de contenus sont disponibles:</p>
|
||
<pre><code>| type_contenu | disponible |
|
||
|---|---|
|
||
| National | oui |
|
||
| Régional | oui |
|
||
| Ville | oui |
|
||
| Hyperlocal (Ancré) | oui |
|
||
| Contextuel | oui |
|
||
| Audio-guides | oui |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-geoip-ne-necessite-pas-de-consentement-rgpd">9. GeoIP ne nécessite pas de consentement RGPD</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise le mode GeoIP</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur CNIL vérifie la conformité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> GeoIP n'est pas considéré comme une donnée personnelle
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'adresse IP n'est pas conservée après détection de la ville
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule la ville est stockée (non identifiant)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun consentement n'est requis conformément au RGPD</p>
|
||
<hr />
|
||
<h2 id="10-base-de-donnees-maxmind-self-hosted">10. Base de données MaxMind self-hosted</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave utilise MaxMind GeoLite2</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on analyse l'infrastructure</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la base de données GeoLite2 est hébergée sur les serveurs RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune requête n'est envoyée à un service tiers
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la base de données est mise à jour automatiquement chaque mois
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le coût est de 0€ (GeoLite2 est gratuit)</p>
|
||
<hr />
|
||
<h2 id="11-mise-a-jour-mensuelle-de-la-base-geoip">11. Mise à jour mensuelle de la base GeoIP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que MaxMind publie des mises à jour mensuelles</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le 1er du mois arrive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job automatique télécharge la nouvelle base GeoLite2
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la base est mise à jour sans interruption de service
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un log est créé pour traçabilité
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si la mise à jour échoue, une alerte est envoyée</p>
|
||
<hr />
|
||
<h2 id="12-ux-acceptable-en-mode-geoip">12. UX acceptable en mode GeoIP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise le mode GeoIP à Paris</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je parcours l'application</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux écouter du contenu pertinent pour Paris et l'Île-de-France
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'expérience est satisfaisante même sans GPS précis
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne suis pas bloqué dans l'utilisation de l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir d'activer le GPS quand je le souhaite</p>
|
||
<hr />
|
||
<h2 id="13-incitation-progressive-a-activer-le-gps">13. Incitation progressive à activer le GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise le mode GeoIP depuis 2 semaines
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas activé le GPS</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je consulte un audio-guide dans les résultats de recherche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je clique "Plus tard", je peux continuer à utiliser l'app normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'incitation reste douce et non intrusive</p>
|
||
<hr />
|
||
<h2 id="14-upgrade-geoip-vers-gps">14. Upgrade GeoIP vers GPS</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise le mode GeoIP</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'active la géolocalisation précise</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système bascule immédiatement en mode GPS
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus hyperlocaux deviennent disponibles
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon feed se rafraîchit avec du contenu plus précis
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un toast de confirmation s'affiche: "Géolocalisation activée"</p>
|
||
<hr />
|
||
<h2 id="15-downgrade-gps-vers-geoip">15. Downgrade GPS vers GeoIP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise le mode GPS précis</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je désactive la géolocalisation dans les paramètres OS</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système bascule automatiquement en mode GeoIP
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus hyperlocaux ne sont plus proposés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un banner s'affiche: "Géolocalisation désactivée. Seul le contenu régional est disponible."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'application continue de fonctionner normalement</p>
|
||
<hr />
|
||
<h2 id="16-detection-automatique-au-demarrage-de-lapp">16. Détection automatique au démarrage de l'app</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ouvre l'application</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'app vérifie les permissions de géolocalisation</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système détecte automatiquement le mode disponible:</p>
|
||
<pre><code>| permission GPS | consentement app | mode activé |
|
||
|---|---|---|
|
||
| Refusée | Non demandé | Pays |
|
||
| Refusée | Accepté | GeoIP |
|
||
| Accordée | Accepté | GPS précis |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le mode est appliqué sans interaction utilisateur</p>
|
||
<hr />
|
||
<h2 id="17-precision-acceptable-pour-la-plupart-des-cas">17. Précision acceptable pour la plupart des cas</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'habite à Lyon
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mon IP est une IP résidentielle standard</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système utilise GeoIP pour me localiser</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la ville détectée est "Lyon" (correct à 80%)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> dans 20% des cas, la ville peut être légèrement erronée (banlieue proche)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette précision est suffisante pour proposer du contenu régional pertinent</p>
|
||
<hr />
|
||
<h2 id="18-geoip-avec-vpn-ou-proxy">18. GeoIP avec VPN ou proxy</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise un VPN avec une IP sortante à Paris
|
||
<span style="color: #F44336"><strong>Mais</strong></span> que je suis physiquement à Lyon</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système utilise GeoIP</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la ville détectée est "Paris" (IP du VPN)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus proposés sont pour Paris
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux activer le GPS précis pour corriger la localisation</p>
|
||
<hr />
|
||
<h2 id="19-pas-de-donnee-personnelle-collectee-avec-geoip">19. Pas de donnée personnelle collectée avec GeoIP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'utilise le mode GeoIP</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le système détermine ma ville via mon IP</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'adresse IP n'est pas conservée après détection
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule la ville "Paris" est stockée en base de données
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la ville seule n'est pas une donnée personnelle (RGPD)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun consentement n'est donc requis</p>
|
||
<hr />
|
||
<h2 id="20-solution-geoip-gratuite-et-self-hosted">20. Solution GeoIP gratuite et self-hosted</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que RoadWave utilise MaxMind GeoLite2</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> on calcule le coût de la solution</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le coût est de 0€
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la solution est opensource
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la base de données est hébergée sur les serveurs RoadWave
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun coût SaaS tiers</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="portabilite-des-donnees-article-20-rgpd">Portabilité des données (Article 20 RGPD)</h1>
|
||
<p><strong>22 scénarios</strong> (21 standards, 1 plan)</p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai utilisé l'application depuis 6 mois</p>
|
||
</blockquote>
|
||
<h2 id="1-demande-dexport-depuis-les-parametres">1. Demande d'export depuis les paramètres</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis dans "Paramètres > Confidentialité"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Exporter mes données"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une page d'information s'affiche expliquant:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un bouton "Confirmer l'export" est disponible</p>
|
||
<hr />
|
||
<h2 id="2-confirmation-et-demarrage-de-lexport">2. Confirmation et démarrage de l'export</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Confirmer l'export"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la demande est validée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message de confirmation s'affiche:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un worker background démarre la génération de l'export
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le statut de l'export est "En cours de génération"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux voir le statut dans "Paramètres > Confidentialité > Mes exports"</p>
|
||
<hr />
|
||
<h2 id="3-contenu-de-larchive-zip">3. Contenu de l'archive ZIP</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon export est généré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je télécharge et ouvre l'archive</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'archive a la structure suivante:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> tous les fichiers sont inclus</p>
|
||
<hr />
|
||
<h2 id="4-contenu-du-fichier-exportjson">4. Contenu du fichier export.json</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ouvre le fichier export.json</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'analyse le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le JSON contient les sections suivantes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> le JSON est formaté de manière lisible (indentation)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes les dates sont au format ISO 8601</p>
|
||
<hr />
|
||
<h2 id="5-contenu-du-fichier-indexhtml">5. Contenu du fichier index.html</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ouvre le fichier index.html dans un navigateur</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la page se charge</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois un site web stylé avec navigation
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les sections suivantes sont affichées:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> la navigation est intuitive (menu latéral)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le design est responsive (mobile/desktop)</p>
|
||
<hr />
|
||
<h2 id="6-fichiers-audio-inclus-dans-lexport">6. Fichiers audio inclus dans l'export</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai créé 5 contenus audio</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le dossier <code>audio/</code> contient mes 5 fichiers
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fichiers sont au format Opus original
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque fichier est nommé: <code>content-[id].opus</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fichiers audio correspondent aux métadonnées dans export.json</p>
|
||
<hr />
|
||
<h2 id="7-fichier-readmetxt-explicatif">7. Fichier README.txt explicatif</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ouvre le fichier README.txt</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je lis le contenu</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le fichier explique:</p>
|
||
<hr />
|
||
<h2 id="8-plan-donnees-de-profil-exportees">8. 📋 Plan: Données de profil exportées</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon export est généré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre export.json et lis la section "profile"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je trouve les données suivantes:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="9-historique-decoute-exporte">9. Historique d'écoute exporté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai écouté 150 contenus depuis 6 mois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la section "listening_history" contient 150 entrées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque entrée contient:</p>
|
||
<pre><code>| 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] |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> les contenus sont triés par date décroissante</p>
|
||
<hr />
|
||
<h2 id="10-centres-dinteret-exportes">10. Centres d'intérêt exportés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes jauges d'intérêt sont:</p>
|
||
<pre><code>| catégorie | valeur |
|
||
|---|---|
|
||
| Automobile | 78% |
|
||
| Voyage | 65% |
|
||
| Musique | 52% |
|
||
| Politique | 30% |
|
||
</code></pre>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la section "interest_gauges" contient ces valeurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> chaque jauge indique la date de dernière modification</p>
|
||
<hr />
|
||
<h2 id="11-historique-des-consentements-exporte">11. Historique des consentements exporté</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai modifié mes consentements plusieurs fois</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> mon export est généré</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la section "consent_history" contient:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> l'historique complet est visible</p>
|
||
<hr />
|
||
<h2 id="12-generation-asynchrone-pour-eviter-timeout">12. Génération asynchrone pour éviter timeout</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai beaucoup de données (500 contenus créés, 10 000 écoutes)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande un export</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la génération se fait en arrière-plan via un worker
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la page web ne timeout pas
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux continuer à utiliser l'application pendant la génération
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email quand l'export est prêt</p>
|
||
<hr />
|
||
<h2 id="13-delai-de-generation-conforme-rgpd">13. Délai de génération conforme RGPD</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je demande un export le 2025-01-20 à 10:00</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le worker génère l'export</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'export est disponible maximum 48h plus tard (avant le 2025-01-22 à 10:00)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la plupart des exports sont prêts en moins de 6h
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le délai respecte l'article 20 du RGPD</p>
|
||
<hr />
|
||
<h2 id="14-email-de-notification-avec-lien-de-telechargement">14. Email de notification avec lien de téléchargement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon export est terminé</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le worker finalise la génération</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email avec le sujet "Votre export de données RoadWave est prêt"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'email contient:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le lien de téléchargement est sécurisé (token unique)</p>
|
||
<hr />
|
||
<h2 id="15-lien-de-telechargement-expire-apres-7-jours">15. Lien de téléchargement expire après 7 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon export a été généré le 2025-01-20
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je reçois le lien de téléchargement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie d'accéder au lien le 2025-01-28 (8 jours plus tard)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le lien est expiré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un message "Ce lien a expiré. Veuillez demander un nouvel export."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux demander un nouvel export si nécessaire</p>
|
||
<hr />
|
||
<h2 id="16-limite-de-1-export-par-mois">16. Limite de 1 export par mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé un export le 2025-01-15</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de demander un nouvel export le 2025-01-20</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un message d'erreur:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton "Confirmer l'export" est désactivé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la date du prochain export possible est affichée</p>
|
||
<hr />
|
||
<h2 id="17-nouvel-export-possible-apres-1-mois">17. Nouvel export possible après 1 mois</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé un export le 2025-01-15</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la date atteint le 2025-02-15</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je peux demander un nouvel export
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le bouton "Confirmer l'export" est actif
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune limite ne s'applique</p>
|
||
<hr />
|
||
<h2 id="18-lien-de-telechargement-securise-avec-token-unique">18. Lien de téléchargement sécurisé avec token unique</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon export est prêt</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je reçois le lien de téléchargement</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le lien contient un token unique et non devinable
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le format du lien est: <code>https://roadwave.fr/exports/download/[token_unique]</code>
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le token est valide uniquement pour mon compte
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le token expire après 7 jours ou après 3 téléchargements</p>
|
||
<hr />
|
||
<h2 id="19-verification-de-lauthentification-avant-telechargement">19. Vérification de l'authentification avant téléchargement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je reçois le lien d'export</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le lien</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le système vérifie que je suis connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je ne suis pas connecté, je suis redirigé vers la page de connexion
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> après connexion, le téléchargement démarre automatiquement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seul le propriétaire du compte peut télécharger l'export</p>
|
||
<hr />
|
||
<h2 id="20-conformite-portabilite-des-donnees">20. Conformité portabilité des données</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon export est généré</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur RGPD vérifie la conformité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> l'export respecte les exigences de l'article 20:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="21-gratuite-de-lexport">21. Gratuité de l'export</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je demande un export de mes données</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> l'export est généré et téléchargé</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> aucun coût n'est facturé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'export est entièrement gratuit
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune inscription Premium n'est requise
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le droit à la portabilité est accessible à tous les utilisateurs</p>
|
||
<hr />
|
||
<h2 id="22-suivi-du-statut-de-generation">22. Suivi du statut de génération</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé un export</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'ouvre "Paramètres > Confidentialité > Mes exports"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je vois le statut actuel:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> la date de demande est affichée
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la taille estimée de l'archive est visible</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
|
||
<h1 id="suppression-du-compte-utilisateur-article-17-rgpd-droit-a-leffacement">Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)</h1>
|
||
<p><strong>21 scénarios</strong></p>
|
||
<hr />
|
||
<blockquote>
|
||
<p><strong>Contexte commun à tous les scénarios</strong></p>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un utilisateur connecté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que j'ai utilisé l'application depuis plusieurs mois</p>
|
||
</blockquote>
|
||
<h2 id="1-demande-de-suppression-depuis-les-parametres">1. Demande de suppression depuis les paramètres</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis dans "Paramètres > Compte"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur "Supprimer mon compte"</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> une page d'avertissement s'affiche avec le message:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> deux boutons sont disponibles: "Annuler" et "Confirmer la suppression"</p>
|
||
<hr />
|
||
<h2 id="2-confirmation-de-suppression-avec-mot-de-passe">2. Confirmation de suppression avec mot de passe</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je clique sur "Confirmer la suppression"</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un formulaire de confirmation s'affiche</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je dois entrer mon mot de passe pour confirmer
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je dois cocher "Je comprends que cette action est définitive"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> un captcha peut être requis pour éviter les suppressions automatisées</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je valide le formulaire</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la suppression est initiée</p>
|
||
<hr />
|
||
<h2 id="3-compte-desactive-immediatement-apres-confirmation">3. Compte désactivé immédiatement après confirmation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai confirmé la suppression de mon compte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la demande est traitée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est désactivé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis déconnecté de toutes mes sessions
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je ne peux plus me reconnecter
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si j'essaie de me connecter, je reçois le message:</p>
|
||
<hr />
|
||
<h2 id="4-contenus-caches-pendant-le-grace-period">4. Contenus cachés pendant le grace period</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte est en cours de suppression</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un autre utilisateur recherche mes contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes contenus ne sont plus diffusés dans l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes contenus n'apparaissent plus dans les recherches
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes contenus ne sont plus recommandés
|
||
<span style="color: #F44336"><strong>Mais</strong></span> mes contenus ne sont pas encore supprimés définitivement</p>
|
||
<hr />
|
||
<h2 id="5-email-de-confirmation-envoye-immediatement">5. Email de confirmation envoyé immédiatement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai confirmé la suppression de mon compte</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la demande est traitée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois un email avec le sujet "Confirmation de suppression de votre compte RoadWave"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'email contient:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le lien d'annulation est valide 30 jours</p>
|
||
<hr />
|
||
<h2 id="6-annulation-de-la-suppression-dans-les-30-jours">6. Annulation de la suppression dans les 30 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé la suppression de mon compte le 2025-01-20
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je reçois l'email de confirmation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je clique sur le lien "Annuler la suppression" le 2025-02-05 (16 jours plus tard)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon compte est réactivé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux me reconnecter normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mes contenus redeviennent visibles dans l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes mes données sont restaurées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de confirmation: "Votre compte a été réactivé"</p>
|
||
<hr />
|
||
<h2 id="7-lien-dannulation-expire-apres-30-jours">7. Lien d'annulation expire après 30 jours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé la suppression de mon compte le 2025-01-20</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> j'essaie de cliquer sur le lien d'annulation le 2025-02-25 (36 jours plus tard)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le lien est expiré
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un message "Ce lien a expiré. Votre compte a été définitivement supprimé."
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la suppression effective a déjà eu lieu</p>
|
||
<hr />
|
||
<h2 id="8-suppression-effective-sans-annulation">8. Suppression effective sans annulation</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé la suppression de mon compte le 2025-01-20
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que je n'ai pas cliqué sur le lien d'annulation</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la date atteint le 2025-02-19 (30 jours plus tard)</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un job automatique exécute la suppression définitive
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toutes mes données personnelles sont supprimées</p>
|
||
<hr />
|
||
<h2 id="9-liste-des-donnees-supprimees-definitivement">9. Liste des données supprimées définitivement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la suppression effective est exécutée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de suppression se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les données suivantes sont supprimées:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ces suppressions sont irréversibles</p>
|
||
<hr />
|
||
<h2 id="10-anonymisation-des-contenus-crees">10. Anonymisation des contenus créés</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai créé 10 contenus audio</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression effective est exécutée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes contenus audio restent disponibles dans l'application
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le nom du créateur devient "Utilisateur supprimé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> mon pseudo n'est plus visible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les métadonnées (titre, description, tags, géolocalisation) sont conservées
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les fichiers audio restent sur le CDN
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les statistiques d'écoute sont conservées</p>
|
||
<hr />
|
||
<h2 id="11-justification-de-lanonymisation-interet-legitime">11. Justification de l'anonymisation (intérêt légitime)</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mes contenus sont conservés anonymement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur RGPD vérifie la conformité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> la conservation est justifiée par l'intérêt légitime de la communauté
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus ne contiennent plus de données personnelles identifiables
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> la suppression complète nuirait à l'expérience des autres utilisateurs
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> cette pratique est conforme au RGPD si anonymisation réelle</p>
|
||
<hr />
|
||
<h2 id="12-contenu-anonymise-visible-pour-les-autres-utilisateurs">12. Contenu anonymisé visible pour les autres utilisateurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que mon compte a été supprimé
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> que mes contenus ont été anonymisés</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un utilisateur consulte un de mes anciens contenus</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le créateur affiché est "Utilisateur supprimé"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le profil du créateur n'est plus accessible
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le contenu reste écoutable normalement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les likes et statistiques sont conservés</p>
|
||
<hr />
|
||
<h2 id="13-suppression-de-mes-likes-avec-conservation-des-compteurs">13. Suppression de mes likes avec conservation des compteurs</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'avais liké 50 contenus</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression effective est exécutée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes likes sont supprimés de la base de données
|
||
<span style="color: #F44336"><strong>Mais</strong></span> les compteurs de likes sur les contenus sont préservés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les créateurs ne perdent pas leurs statistiques
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> seule la relation "user X a liké content Y" est supprimée</p>
|
||
<hr />
|
||
<h2 id="14-suppression-de-mes-abonnements">14. Suppression de mes abonnements</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suivais 20 créateurs</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> la suppression effective est exécutée</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mes abonnements sont supprimés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les compteurs d'abonnés des créateurs sont décrémentés de 1
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les créateurs ne reçoivent pas de notification de désabonnement</p>
|
||
<hr />
|
||
<h2 id="15-revocation-de-tous-les-tokens-immediatement">15. Révocation de tous les tokens immédiatement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis connecté sur 3 appareils (mobile, tablette, web)</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande la suppression de mon compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> tous mes tokens d'authentification sont révoqués immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je suis déconnecté de tous mes appareils
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> toute tentative de reconnexion échoue</p>
|
||
<hr />
|
||
<h2 id="16-rappels-par-email-pendant-le-grace-period">16. Rappels par email pendant le grace period</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai demandé la suppression de mon compte le 2025-01-20</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le grace period s'écoule</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> je reçois des emails de rappel:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> chaque email contient le lien d'annulation</p>
|
||
<hr />
|
||
<h2 id="17-conformite-droit-a-leffacement">17. Conformité droit à l'effacement</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la suppression de mon compte est complète</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> un auditeur RGPD vérifie la conformité</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> le processus respecte l'article 17 du RGPD:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<hr />
|
||
<h2 id="18-suppression-dun-compte-premium">18. Suppression d'un compte Premium</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai un abonnement Premium actif</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande la suppression de mon compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> mon abonnement est annulé immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucun remboursement n'est effectué (conformément aux CGV)
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je reçois un email de confirmation d'annulation de l'abonnement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> le reste du processus de suppression se déroule normalement</p>
|
||
<hr />
|
||
<h2 id="19-suppression-dun-compte-createur-avec-revenus-en-attente">19. Suppression d'un compte créateur avec revenus en attente</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que je suis un créateur avec 75€ de revenus en attente de paiement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande la suppression de mon compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un message m'informe:
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> je peux choisir "Recevoir le paiement et attendre" ou "Renoncer au paiement"
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> si je choisis "Recevoir le paiement", la suppression est repoussée de 7 jours</p>
|
||
<hr />
|
||
<h2 id="20-suppression-avec-signalements-de-moderation-en-cours">20. Suppression avec signalements de modération en cours</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que j'ai 2 signalements en cours de traitement</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> je demande la suppression de mon compte</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> les signalements sont automatiquement clôturés
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> les contenus signalés sont masqués immédiatement
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> aucune sanction n'est appliquée (compte déjà en suppression)</p>
|
||
<hr />
|
||
<h2 id="21-log-de-la-suppression-pour-tracabilite">21. Log de la suppression pour traçabilité</h2>
|
||
<p><span style="color: #2196F3"><strong>Étant donné</strong></span> que la suppression effective est exécutée</p>
|
||
<p><span style="color: #FF9800"><strong>Quand</strong></span> le job de suppression se termine</p>
|
||
<p><span style="color: #4CAF50"><strong>Alors</strong></span> un log est créé avec:</p>
|
||
<pre><code>| 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 |
|
||
</code></pre>
|
||
<p><span style="color: #9E9E9E"><strong>Et</strong></span> ce log est conservé 5 ans pour audit RGPD
|
||
<span style="color: #9E9E9E"><strong>Et</strong></span> l'user_id est pseudonymisé pour anonymat</p>
|
||
<hr />
|
||
<div style="page-break-after: always;"></div>
|
||
</body>
|
||
</html> |