Initial commit
This commit is contained in:
671
docs/INCONSISTENCIES-ANALYSIS.md
Normal file
671
docs/INCONSISTENCIES-ANALYSIS.md
Normal file
@@ -0,0 +1,671 @@
|
||||
# Analyse des Incohérences entre ADR et Règles Métier
|
||||
|
||||
**Date d'analyse** : 2026-01-28
|
||||
**Analysé par** : Audit Architecture RoadWave
|
||||
**Scope** : 18 ADR × Règles Métier (17 fichiers)
|
||||
|
||||
---
|
||||
|
||||
## Résumé Exécutif
|
||||
|
||||
Cette analyse a identifié **15 incohérences** entre les décisions d'architecture (ADR) et les règles métier du projet RoadWave.
|
||||
|
||||
### Répartition par Sévérité
|
||||
|
||||
| Sévérité | Nombre | % Total | Statut | Action Required |
|
||||
|----------|--------|---------|--------|-----------------|
|
||||
| 🔴 **CRITICAL** | 2 | 13% | ✅ **RÉSOLU** | ~~avant implémentation~~ |
|
||||
| 🟠 **HIGH** | 4 | 27% | ⏳ 3 restants (1 résolu) | Résolution Sprint 1-2 |
|
||||
| 🟡 **MODERATE** | 8 | 53% | ⏳ En cours | Résolution Sprint 3-5 |
|
||||
| 🟢 **LOW** | 1 | 7% | ⏳ En cours | À clarifier lors du développement |
|
||||
|
||||
### Impact par Domaine
|
||||
|
||||
| Domaine | Nombre d'incohérences | Criticité maximale |
|
||||
|---------|----------------------|-------------------|
|
||||
| Streaming & Géolocalisation | 3 | 🔴 CRITICAL |
|
||||
| Données & Infrastructure | 3 | 🟠 HIGH |
|
||||
| Authentification & Sécurité | 2 | 🟠 HIGH |
|
||||
| Tests & Qualité | 2 | 🟡 MODERATE |
|
||||
| Coûts & Déploiement | 3 | 🟡 MODERATE |
|
||||
| UX & Engagement | 2 | 🟡 MODERATE |
|
||||
|
||||
---
|
||||
|
||||
## 🔴 Incohérences Critiques (Blocantes)
|
||||
|
||||
### #1 : HLS ne supporte pas les Notifications Push en Arrière-plan
|
||||
|
||||
**Statut** : ✅ **RÉSOLU** (ADR-019 créé)
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-002 (Protocole Streaming) |
|
||||
| **Règle métier** | Règle 05, section 5.1.2 (Mode Piéton, lignes 86-120) |
|
||||
| **Conflit** | HLS est unidirectionnel (serveur→client), ne peut pas envoyer de push quand l'app est fermée |
|
||||
| **Impact** | Mode piéton non fonctionnel : notifications "Point d'intérêt à 200m" impossibles |
|
||||
|
||||
**Scénario d'échec** :
|
||||
```
|
||||
Utilisateur: Marie se promène, app fermée
|
||||
Position: 150m de la Tour Eiffel
|
||||
Attendu: Push notification "🗼 À proximité: Histoire de la Tour Eiffel"
|
||||
Réel: Rien (HLS ne peut pas notifier)
|
||||
```
|
||||
|
||||
**Solution implémentée** :
|
||||
- ✅ **ADR-019** : Architecture hybride WebSocket + Firebase Cloud Messaging
|
||||
- Phase 1 (MVP) : Push serveur via FCM/APNS
|
||||
- Phase 2 : Geofencing natif iOS/Android pour mode offline
|
||||
|
||||
**Actions requises** :
|
||||
- [ ] Backend : Implémenter endpoint WebSocket `/ws/location`
|
||||
- [ ] Backend : Worker PostGIS avec requête `ST_DWithin` (30s interval)
|
||||
- [ ] Mobile : Intégrer Firebase SDK (`firebase_messaging`)
|
||||
- [ ] Tests : Validation en conditions réelles (10 testeurs, Paris)
|
||||
|
||||
---
|
||||
|
||||
### #2 : Latence HLS Incompatible avec ETA de 7 Secondes
|
||||
|
||||
**Statut** : ✅ **RÉSOLU** (ADR-002 mis à jour)
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-002 (Protocole Streaming, lignes 40-41) |
|
||||
| **Règle métier** | Règle 05 (lignes 16-20), Règle 17 (lignes 25-30, 120-124) |
|
||||
| **Conflit** | ETA de 7s avant le point, mais HLS a 5-30s de latence → audio démarre APRÈS avoir dépassé le point |
|
||||
| **Impact** | UX catastrophique : utilisateur entend "Vous êtes devant le château" 100m APRÈS l'avoir dépassé |
|
||||
|
||||
**Calcul du problème** (90 km/h = 25 m/s) :
|
||||
```
|
||||
t=0s → Notification "Suivant: Château dans 7s" (175m avant)
|
||||
t=7s → Utilisateur arrive au château
|
||||
t=15s → HLS démarre (latence 15s)
|
||||
Résultat: Audio démarre 200m APRÈS le point ❌
|
||||
```
|
||||
|
||||
**Solution implémentée** :
|
||||
- ✅ **ADR-002 mis à jour** : Section "Gestion de la Latence et Synchronisation Géolocalisée"
|
||||
- Pre-buffering à ETA=30s (15 premières secondes en cache local)
|
||||
- ETA adaptatif : 5s si cache prêt, 15s sinon
|
||||
- Mesure dynamique de latence HLS par utilisateur
|
||||
|
||||
**Actions requises** :
|
||||
- [ ] Backend : Endpoint `/api/v1/audio/poi/:id/intro` (retourne 15s d'audio)
|
||||
- [ ] Mobile : Service `PreBufferService` avec cache local (max 100 MB)
|
||||
- [ ] Mobile : Loader visuel avec progression si buffer > 3s
|
||||
- [ ] Tests : Validation synchronisation ±10m du POI
|
||||
|
||||
---
|
||||
|
||||
## 🟠 Incohérences Importantes (Sprint 1-2)
|
||||
|
||||
### #3 : Souveraineté des Données (Français vs Suisse)
|
||||
|
||||
**Statut** : ✅ **RÉSOLU** (ADR-008 mis à jour)
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concernés** | ADR-004 (CDN, ligne 26), ADR-008 (Auth, mis à jour) |
|
||||
| **Règle métier** | Règle 02 (RGPD, section 13.10) |
|
||||
| **Conflit** | ADR-004 revendique "100% souveraineté française" mais ADR-008 utilisait Zitadel (entreprise suisse) |
|
||||
| **Impact** | Contradiction marketing + risque juridique si promesse "100% français" |
|
||||
|
||||
**Solution implémentée** : **Self-hosting Zitadel sur OVH France**
|
||||
|
||||
- ✅ Container Docker sur le même VPS OVH (Gravelines, France)
|
||||
- ✅ Base de données PostgreSQL partagée (schéma séparé pour Zitadel)
|
||||
- ✅ Aucune donnée ne transite par des serveurs tiers
|
||||
- ✅ Souveraineté totale garantie : 100% des données en France
|
||||
- ✅ Cohérence complète avec ADR-004 (CDN 100% français)
|
||||
|
||||
**Changements apportés** :
|
||||
- ✅ ADR-008 mis à jour avec architecture self-hosted détaillée
|
||||
- ✅ TECHNICAL.md mis à jour (tableau + diagramme architecture)
|
||||
- ✅ Clarification : Zitadel est open source, donc aucune dépendance à une entreprise suisse
|
||||
|
||||
**Actions complétées** :
|
||||
- [x] Décision validée : Self-host sur OVH
|
||||
- [x] ADR-008 mis à jour avec architecture self-hosted
|
||||
- [x] TECHNICAL.md mis à jour
|
||||
|
||||
---
|
||||
|
||||
### #4 : ORM sqlc vs Types PostGIS
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concernés** | ADR-013 (ORM, lignes 12, 33-40), ADR-005 (BDD, lignes 47-56) |
|
||||
| **Règle métier** | N/A (problème technique pur) |
|
||||
| **Conflit** | sqlc génère types Go depuis SQL, mais PostGIS geography/geometry ne mappent pas proprement |
|
||||
| **Impact** | Risque de type `interface{}` ou `[]byte` pour géographie → perte de type safety revendiquée |
|
||||
|
||||
**Nature du problème** :
|
||||
|
||||
sqlc génère du code Go depuis SQL, mais les types PostGIS (`geography`, `geometry`) ne sont pas mappés proprement en Go. Résultat : types opaques (`[]byte`, `interface{}`) qui perdent la **type safety** revendiquée dans ADR-013.
|
||||
|
||||
**Solution retenue** :
|
||||
|
||||
1. **Wrapper types Go** avec méthodes `Scan/Value` pour conversion automatique
|
||||
2. **Utiliser les fonctions PostGIS de conversion** :
|
||||
- `ST_AsGeoJSON()` → struct GeoJSON typée
|
||||
- `ST_AsText()` → string WKT
|
||||
- `geography` brut → `pgtype.Point` (lib pgx)
|
||||
3. **Documenter le pattern** dans ADR-013 section "Gestion des Types PostGIS"
|
||||
|
||||
**Action** :
|
||||
- [ ] Créer package `internal/geo` avec wrappers `GeoJSON`, `WKT`
|
||||
- [ ] Mettre à jour ADR-013 section "Types PostGIS"
|
||||
- [ ] Documenter pattern dans README backend
|
||||
|
||||
---
|
||||
|
||||
### #5 : Cache Redis (TTL 5min) vs Mode Offline (30 jours)
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-005 (BDD, ligne 60) |
|
||||
| **Règle métier** | Règle 11 (Mode Offline, lignes 58-77) |
|
||||
| **Conflit** | Redis avec TTL 5min pour géolocalisation, mais contenu offline valide 30 jours |
|
||||
| **Impact** | En mode offline, impossible de rafraîchir le cache géolocalisation → POI proches non détectés |
|
||||
|
||||
**Analyse du flux** :
|
||||
|
||||
```
|
||||
Mode connecté:
|
||||
1. Requête POI proches → Redis (cache 5min)
|
||||
2. Si miss → PostGIS → Cache Redis
|
||||
3. ✅ Fonctionne
|
||||
|
||||
Mode offline (Règle 11):
|
||||
1. Requête POI proches → Redis (expiré depuis 6 min)
|
||||
2. Impossible de requêter PostGIS (pas de réseau) ❌
|
||||
3. Aucun POI détecté
|
||||
```
|
||||
|
||||
**Solution** :
|
||||
|
||||
Stratégie de **cache à 2 niveaux** :
|
||||
|
||||
| Cache | TTL | Usage | Invalidation |
|
||||
|-------|-----|-------|--------------|
|
||||
| **Redis (L1)** | 5 min | Mode connecté | Automatique |
|
||||
| **SQLite local (L2)** | 30 jours | Mode offline | Manuelle lors sync |
|
||||
|
||||
**Architecture** :
|
||||
|
||||
```
|
||||
[Mode Connecté]
|
||||
→ Redis (L1) → PostGIS → Cache local SQLite (L2)
|
||||
|
||||
[Mode Offline]
|
||||
→ SQLite local (L2) uniquement
|
||||
```
|
||||
|
||||
**Action** :
|
||||
- [ ] Backend : Ajouter endpoint `/sync/nearby-pois?lat=X&lon=Y&radius=10km`
|
||||
- [ ] Mobile : Créer `OfflineCacheService` avec SQLite + index spatial
|
||||
- [ ] Mettre à jour ADR-005 section "Cache" avec stratégie 2 niveaux
|
||||
- [ ] Règle 11 : Clarifier sync automatique vs manuel
|
||||
|
||||
---
|
||||
|
||||
### #6 : Package Geofencing vs Permissions iOS/Android
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-014 (Frontend Mobile, ligne 48) |
|
||||
| **Règle métier** | Règle 05 (lignes 86-134), Règle 11 (RGPD, lignes 51-86) |
|
||||
| **Conflit** | Package `geofence_service` choisi, mais pas de doc sur compatibilité permissions "optionnelles" |
|
||||
| **Impact** | Risque de rejet App Store/Play Store si permissions obligatoires mal gérées |
|
||||
|
||||
**Problématiques** :
|
||||
|
||||
1. **iOS** : Permission "Always Location" exige justification stricte (taux refus 70%)
|
||||
2. **Android** : Background location nécessite déclaration spéciale (depuis Android 10)
|
||||
3. **Règle métier** : Permissions optionnelles (app utilisable sans "Always Location")
|
||||
|
||||
**Package `geofence_service`** :
|
||||
- ✅ Supporte iOS/Android
|
||||
- ⚠️ Documentation peu claire sur permissions optionnelles
|
||||
- ⚠️ Pas de fallback natif si permission refusée
|
||||
|
||||
**Solution** :
|
||||
|
||||
**Stratégie de permissions progressive** :
|
||||
|
||||
```dart
|
||||
enum LocationPermissionLevel {
|
||||
denied, // Pas de permission
|
||||
whenInUse, // "Quand l'app est ouverte" (iOS)
|
||||
always, // "Toujours" (iOS) / Background (Android)
|
||||
}
|
||||
|
||||
class GeofencingService {
|
||||
Future<void> requestPermissions() async {
|
||||
// Étape 1: Demander "When In Use" (moins intrusif)
|
||||
var status = await Permission.locationWhenInUse.request();
|
||||
|
||||
if (status.isGranted) {
|
||||
// Mode basique: détection seulement app ouverte
|
||||
_enableBasicGeofencing();
|
||||
|
||||
// Étape 2 (optionnelle): Proposer upgrade vers "Always"
|
||||
_showUpgradePermissionDialog();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> upgradeToAlwaysPermission() async {
|
||||
// Demandé seulement si utilisateur veut mode piéton complet
|
||||
await Permission.locationAlways.request();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Actions** :
|
||||
- [ ] Mettre à jour ADR-014 avec stratégie permissions progressive
|
||||
- [ ] Créer doc "Permissions Strategy" dans `/docs/mobile/`
|
||||
- [ ] Tests : Validation rejet App Store (TestFlight beta)
|
||||
|
||||
---
|
||||
|
||||
## 🟡 Incohérences Modérées (Sprint 3-5)
|
||||
|
||||
### #7 : Points vs Pourcentages dans les Jauges
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-010 (Commandes Volant, lignes 15-21) |
|
||||
| **Règle métier** | Règle 03 (Centres d'intérêt, lignes 7-14) |
|
||||
| **Conflit** | ADR dit "+2 **points**", Règle dit "+2**%**" pour même action |
|
||||
| **Impact** | Ambiguïté sur calcul : +2 points absolus ou +2% relatifs ? |
|
||||
|
||||
**Exemple du conflit** :
|
||||
|
||||
- **ADR-010 (ligne 18)** : "≥80% d'écoute = +2 **points**"
|
||||
- **Règle 03 (ligne 9)** : "≥80% d'écoute = +2**%** à la jauge"
|
||||
|
||||
**Scénario** :
|
||||
```
|
||||
Jauge "Automobile" = 45%
|
||||
Utilisateur écoute 85% d'un podcast voiture
|
||||
|
||||
Option A (points absolus): 45 + 2 = 47%
|
||||
Option B (pourcentage relatif): 45 * 1.02 = 45.9%
|
||||
```
|
||||
|
||||
**Recommandation** : **Option A (points absolus)** pour simplicité
|
||||
|
||||
**Justification** :
|
||||
- Progression linéaire plus intuitive
|
||||
- Évite effet "rich get richer" (jauges hautes progressent + vite)
|
||||
- Cohérent avec système de gamification classique
|
||||
|
||||
**Actions** :
|
||||
- [ ] Clarifier ADR-010 : remplacer "points" par "points de pourcentage"
|
||||
- [ ] Clarifier Règle 03 : uniformiser terminologie
|
||||
- [ ] Backend : Documenter formule exacte dans code
|
||||
|
||||
---
|
||||
|
||||
### #8 : OAuth2 Complexe vs Email/Password Simple
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-008 (Auth, lignes 12, 52-68) |
|
||||
| **Règle métier** | Règle 01 (Auth, lignes 5-10) |
|
||||
| **Conflit** | ADR implémente OAuth2 PKCE complet, mais Règle dit "❌ Pas d'OAuth tiers, email/password uniquement" |
|
||||
| **Impact** | Sur-ingénierie : OAuth2 conçu pour tiers (Google, Facebook) mais non utilisé ici |
|
||||
|
||||
**Analyse** :
|
||||
|
||||
- **ADR-008** : Architecture OAuth2 avec PKCE, refresh tokens, etc.
|
||||
- **Règle 01** : "❌ Pas de Google, Apple, Facebook OAuth"
|
||||
|
||||
**Zitadel supporte** :
|
||||
- OAuth2 (pour intégrations tierces)
|
||||
- Email/Password natif (ce dont on a besoin)
|
||||
|
||||
**Question** : Pourquoi implémenter OAuth2 si pas de tiers ?
|
||||
|
||||
**Options** :
|
||||
|
||||
| Option | Complexité | Justification |
|
||||
|--------|------------|---------------|
|
||||
| **A. Garder OAuth2** | Haute | Future-proof pour API partenaires |
|
||||
| **B. Session simple** | Basse | Suffit pour MVP email/password |
|
||||
|
||||
**Recommandation** : **Option A** (garder OAuth2) si :
|
||||
- Vision long-terme : API pour partenaires (créateurs, annonceurs)
|
||||
- Coût marginal : Zitadel gère OAuth2 nativement
|
||||
|
||||
Sinon **Option B** (session simple) si MVP pur.
|
||||
|
||||
**Actions** :
|
||||
- [ ] Décision : Confirmer besoin OAuth2 avec product owner
|
||||
- [ ] Si A : Mettre à jour Règle 01 "OAuth tiers en Phase 2"
|
||||
- [ ] Si B : Simplifier ADR-008 (session JWT classique)
|
||||
|
||||
---
|
||||
|
||||
### #9 : GeoIP Database (MaxMind)
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-005 (non mentionné) |
|
||||
| **Règle métier** | Règle 02 (RGPD, lignes 146-149) |
|
||||
| **Conflit** | Règle cite "MaxMind GeoLite2 (gratuit)", mais offre a changé en 2019 |
|
||||
| **Impact** | Coût caché : MaxMind nécessite compte + API calls (plus de base offline gratuite) |
|
||||
|
||||
**Historique** :
|
||||
- **Avant 2019** : GeoLite2 database téléchargeable gratuitement
|
||||
- **Après 2019** : Compte requis + limite 1000 requêtes/jour (gratuit)
|
||||
- **Dépassement** : 0.003$/requête
|
||||
|
||||
**Utilisation RoadWave** :
|
||||
- Mode dégradé (sans GPS) → GeoIP pour localisation approximative
|
||||
- Estimation : 10% des utilisateurs (1000 users × 10% = 100 requêtes/jour)
|
||||
|
||||
**Options** :
|
||||
|
||||
| Option | Coût/mois | Précision | Maintenance |
|
||||
|--------|-----------|-----------|-------------|
|
||||
| **A. MaxMind API** | ~10€ | ±50 km | Nulle |
|
||||
| **B. IP2Location Lite** | Gratuit | ±50 km | Maj mensuelle |
|
||||
| **C. Self-hosted GeoIP** | Gratuit | ±50 km | +2h/mois |
|
||||
|
||||
**Recommandation** : **Option C** (self-hosted avec IP2Location Lite DB)
|
||||
|
||||
**Architecture** :
|
||||
```
|
||||
[Backend Go] → [GeoIP Service]
|
||||
↓
|
||||
[IP2Location SQLite DB]
|
||||
(màj mensuelle via cron)
|
||||
```
|
||||
|
||||
**Actions** :
|
||||
- [ ] Backend : Implémenter service GeoIP avec IP2Location
|
||||
- [ ] DevOps : Cron job màj mensuelle de la DB
|
||||
- [ ] Mettre à jour Règle 02 ligne 147
|
||||
|
||||
---
|
||||
|
||||
### #10 : Tests BDD Synchronisés (Backend + Mobile)
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concernés** | ADR-007 (Tests BDD, lignes 30-68), ADR-015 (Stratégie, lignes 59-62) |
|
||||
| **Règle métier** | Toutes (Gherkin) |
|
||||
| **Conflit** | Features partagées `/features`, step definitions séparées → qui exécute quoi ? |
|
||||
| **Impact** | Risque de divergence backend/mobile si tests pas synchronisés |
|
||||
|
||||
**Architecture actuelle** :
|
||||
|
||||
```
|
||||
/features/*.feature (partagé)
|
||||
/backend/tests/bdd/ (step definitions Go)
|
||||
/mobile/tests/bdd/ (step definitions Dart)
|
||||
```
|
||||
|
||||
**Question non résolue** :
|
||||
- Un test "Authentification" concerne-t-il backend ET mobile ?
|
||||
- Qui est responsable de l'exécuter ?
|
||||
- Si les implémentations divergent ?
|
||||
|
||||
**Recommandation** : **Catégoriser les features**
|
||||
|
||||
```
|
||||
/features/
|
||||
/api/ → Backend uniquement (tests API REST)
|
||||
/ui/ → Mobile uniquement (tests interface)
|
||||
/e2e/ → End-to-end (backend + mobile ensemble)
|
||||
```
|
||||
|
||||
**Exemple** :
|
||||
```gherkin
|
||||
# features/api/authentication.feature (backend)
|
||||
Scénario: Création de compte via API
|
||||
Étant donné une requête POST /api/v1/auth/register
|
||||
Quand j'envoie email "test@example.com" et password "Pass123!"
|
||||
Alors le statut HTTP est 201
|
||||
Et la réponse contient un token JWT
|
||||
|
||||
# features/ui/authentication.feature (mobile)
|
||||
Scénario: Création de compte via interface
|
||||
Étant donné que je suis sur l'écran d'inscription
|
||||
Quand je saisis email "test@example.com"
|
||||
Et je saisis mot de passe "Pass123!"
|
||||
Et je clique sur "S'inscrire"
|
||||
Alors je vois l'écran d'accueil
|
||||
```
|
||||
|
||||
**Actions** :
|
||||
- [ ] Réorganiser `/features` en 3 catégories (api, ui, e2e)
|
||||
- [ ] Mettre à jour ADR-007 avec convention de nommage
|
||||
- [ ] CI/CD : Séparer jobs backend-bdd et mobile-bdd
|
||||
|
||||
---
|
||||
|
||||
### #11 : 70/30 Split Paiements (Vérification Manquante)
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-009 (Paiement, lignes 32-52) |
|
||||
| **Règle métier** | Règle 18 (Monétisation, non fournie complète) |
|
||||
| **Conflit** | ADR assume 70/30 split sans référence règle métier |
|
||||
| **Impact** | Risque de mauvaise répartition revenus créateurs |
|
||||
|
||||
**ADR-009 spécifie** :
|
||||
- 70% créateur
|
||||
- 30% plateforme
|
||||
|
||||
**Question** : Est-ce validé par les règles métier business ?
|
||||
|
||||
**Actions** :
|
||||
- [ ] Lire Règle 18 (Monétisation Créateurs) complète
|
||||
- [ ] Vérifier si 70/30 correspond aux attentes
|
||||
- [ ] Si divergence : mettre à jour ADR-009
|
||||
|
||||
---
|
||||
|
||||
### #12 : Monorepo Path Filters vs Features Partagées
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concernés** | ADR-016 (Monorepo, ligne 80), ADR-015 (Tests) |
|
||||
| **Règle métier** | N/A (problème CI/CD) |
|
||||
| **Conflit** | Path filters pour éviter rebuild tout, mais features partagées déclenchent tout |
|
||||
| **Impact** | Optimisation CI/CD inefficace |
|
||||
|
||||
**Problème** :
|
||||
|
||||
```yaml
|
||||
# .github/workflows/backend.yml
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'features/**' # ❌ Change sur n'importe quel .feature → rebuild backend
|
||||
```
|
||||
|
||||
**Solution** : Path filters **par catégorie** (suite de #10)
|
||||
|
||||
```yaml
|
||||
# .github/workflows/backend.yml
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'features/api/**' # ✅ Seulement features API
|
||||
- 'features/e2e/**' # ✅ E2E impacte backend
|
||||
|
||||
# .github/workflows/mobile.yml
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'mobile/**'
|
||||
- 'features/ui/**' # ✅ Seulement features UI
|
||||
- 'features/e2e/**' # ✅ E2E impacte mobile
|
||||
```
|
||||
|
||||
**Actions** :
|
||||
- [ ] Implémenter catégorisation features (dépend de #10)
|
||||
- [ ] Mettre à jour workflows CI/CD
|
||||
- [ ] Mettre à jour ADR-016 avec stratégie path filters
|
||||
|
||||
---
|
||||
|
||||
### #13 : Coûts Email (Transition Free → Paid)
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concernés** | ADR-018 (Email, lignes 49-52), ADR-017 (Hébergement) |
|
||||
| **Règle métier** | N/A (économique) |
|
||||
| **Conflit** | ADR cite "gratuit" mais limite 9000 emails/mois → plan transition manquant |
|
||||
| **Impact** | Coût surprise lors de la croissance |
|
||||
|
||||
**ADR-018 spécifie** :
|
||||
- Brevo gratuit : 300 emails/jour = 9000/mois
|
||||
- Phase MVP : 0-10K utilisateurs
|
||||
|
||||
**Calcul réaliste** :
|
||||
```
|
||||
Emails par utilisateur/mois:
|
||||
- Vérification email: 1
|
||||
- Reset password: 0.1 (10%)
|
||||
- Notifications (opt-in 30%): 4
|
||||
- Paiements créateurs (5%): 1
|
||||
|
||||
Total: ~2 emails/user/mois (moyenne)
|
||||
|
||||
10K users × 2 = 20K emails/mois → dépassement tier gratuit
|
||||
```
|
||||
|
||||
**Coût Brevo** :
|
||||
- Free: 0-9K emails
|
||||
- Lite: 19€/mois (20K emails)
|
||||
- Business: 49€/mois (50K emails)
|
||||
|
||||
**Actions** :
|
||||
- [ ] Mettre à jour ADR-018 avec projection coûts
|
||||
- [ ] Implémenter alertes (90% quota atteint)
|
||||
- [ ] Plan B : Self-hosted SMTP (Postfix) si budget serré
|
||||
|
||||
---
|
||||
|
||||
### #14 : Kubernetes vs VPS MVP
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concernés** | ADR-017 (Hébergement, ligne 12), ADR-001 (Go, ligne 27) |
|
||||
| **Règle métier** | N/A (infrastructure) |
|
||||
| **Conflit** | ADR-001 justifie Go pour "Kubernetes first-class", mais ADR-017 utilise VPS simple |
|
||||
| **Impact** | Sur-architecture : pourquoi choisir Go pour K8s si pas utilisé ? |
|
||||
|
||||
**Analyse** :
|
||||
|
||||
- **ADR-001** : Go choisi notamment pour "excellent support Kubernetes"
|
||||
- **ADR-017** : MVP sur OVH VPS Essential (single VM, Docker Compose)
|
||||
- **ADR-012** : Mentionne migration K8s "à 1M+ users"
|
||||
|
||||
**Question** : Justification K8s prématurée ?
|
||||
|
||||
**Réponse** : **Non, acceptable** si :
|
||||
- Vision long-terme claire (1M users = besoin K8s)
|
||||
- Go apporte autres avantages (perf, concurrence, typing)
|
||||
- Coût marginal (Go vs Node.js comparable en complexité MVP)
|
||||
|
||||
**Recommandation** : **Clarifier la vision** dans ADR
|
||||
|
||||
**Actions** :
|
||||
- [ ] Mettre à jour ADR-001 : "Go pour scalabilité future (K8s), mais aussi perf/typage"
|
||||
- [ ] ADR-017 : Ajouter section "Roadmap Infrastructure" (VPS → K8s)
|
||||
|
||||
---
|
||||
|
||||
## 🟢 Incohérences Mineures (Clarification)
|
||||
|
||||
### #15 : Unlike Manuel sur Contenu Auto-liké
|
||||
|
||||
| Élément | Détail |
|
||||
|---------|--------|
|
||||
| **ADR concerné** | ADR-010 (ligne 15-21) |
|
||||
| **Règle métier** | Règle 05 (lignes 248-323), Règle 03 (lignes 93-99) |
|
||||
| **Conflit** | Auto-like +2% documenté, mais unlike manuel non spécifié |
|
||||
| **Impact** | Ambiguïté : faut-il annuler (+2%) si unlike ? |
|
||||
|
||||
**Scénario** :
|
||||
```
|
||||
1. Utilisateur écoute 85% → auto-like → jauge +2%
|
||||
2. Utilisateur clique "Unlike" (toggle)
|
||||
3. Que se passe-t-il ?
|
||||
Option A: Jauge -2% (annulation)
|
||||
Option B: Jauge reste (unlike n'affecte pas)
|
||||
```
|
||||
|
||||
**Recommandation** : **Option A** (annulation symétrique)
|
||||
|
||||
**Justification** : Unlike explicite = signal fort "pas intéressé"
|
||||
|
||||
**Actions** :
|
||||
- [ ] Clarifier Règle 03 : section "Unlike Manuel"
|
||||
- [ ] Backend : Implémenter logique annulation dans `GaugeService`
|
||||
|
||||
---
|
||||
|
||||
## Plan d'Action Global
|
||||
|
||||
### Phase 1 : Résolutions Critiques (Avant Implémentation)
|
||||
|
||||
| # | Tâche | Responsable | Effort | Deadline |
|
||||
|---|-------|-------------|--------|----------|
|
||||
| 1 | ✅ Créer ADR-019 (Notifications) | Architecture | 2h | ✅ Fait |
|
||||
| 2 | ✅ Mettre à jour ADR-002 (Pre-buffering) | Architecture | 1h | ✅ Fait |
|
||||
| 3 | Implémenter WebSocket backend | Backend Lead | 3j | Sprint 1 |
|
||||
| 4 | Implémenter Pre-buffer mobile | Mobile Lead | 2j | Sprint 1 |
|
||||
|
||||
### Phase 2 : Résolutions Importantes (Sprint 1-2)
|
||||
|
||||
| # | Tâche | Responsable | Effort | Statut |
|
||||
|---|-------|-------------|--------|--------|
|
||||
| 5 | ✅ Décision souveraineté (Zitadel self-host) | CTO | 1h | ✅ **Fait** |
|
||||
| 6 | Package geo types (PostGIS) | Backend | 1j | ⏳ Sprint 2 |
|
||||
| 7 | Cache 2 niveaux (Redis + SQLite) | Backend + Mobile | 3j | ⏳ Sprint 2 |
|
||||
| 8 | Stratégie permissions progressive | Mobile | 2j | ⏳ Sprint 2 |
|
||||
|
||||
### Phase 3 : Résolutions Modérées (Sprint 3-5)
|
||||
|
||||
| # | Tâche | Responsable | Effort | Deadline |
|
||||
|---|-------|-------------|--------|----------|
|
||||
| 9-15 | Clarifications ADR/Règles | Tech Writer | 5j | Sprint 3-4 |
|
||||
| 16 | Réorganisation features BDD | QA Lead | 2j | Sprint 4 |
|
||||
| 17 | Optimisation CI/CD path filters | DevOps | 1j | Sprint 5 |
|
||||
|
||||
---
|
||||
|
||||
## Métriques de Suivi
|
||||
|
||||
| Métrique | Valeur Initiale | Cible | Actuel |
|
||||
|----------|----------------|-------|--------|
|
||||
| Incohérences CRITICAL | 2 | 0 | ✅ **0** (2/2 résolues) |
|
||||
| Incohérences HIGH | 4 | 0 | ⏳ **3** (1/4 résolue) |
|
||||
| Incohérences MODERATE | 8 | ≤2 | ⏳ 8 |
|
||||
| ADR à jour | 66% (12/18) | 100% | ⏳ 78% (14/18) |
|
||||
| Coverage documentation | N/A | >90% | ⏳ 80% |
|
||||
|
||||
**Dernière mise à jour** : 2026-01-30
|
||||
|
||||
---
|
||||
|
||||
## Contacts et Ressources
|
||||
|
||||
- **Analyse complète** : Ce document
|
||||
- **ADR-019** : `/docs/adr/019-notifications-geolocalisees.md`
|
||||
- **ADR-002 (mis à jour)** : `/docs/adr/002-protocole-streaming.md`
|
||||
- **Questions** : Créer une issue GitHub avec tag `[architecture]`
|
||||
|
||||
---
|
||||
|
||||
**Prochaine revue** : 2026-02-15 (après Sprint 2)
|
||||
65
docs/adr/001-langage-backend.md
Normal file
65
docs/adr/001-langage-backend.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# ADR-001 : Langage Backend
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-17
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave doit gérer 10M d'utilisateurs avec des connexions concurrentes massives pour le streaming audio géolocalisé.
|
||||
|
||||
## Décision
|
||||
|
||||
**Go** avec le framework **Fiber**.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
### Comparatif synthétique
|
||||
|
||||
| Option | Conn/serveur | P99 latency | Simplicité | Écosystème RoadWave |
|
||||
|--------|-------------|------------|------------|------------|
|
||||
| **Go + Fiber** | 1M+ | 5-50ms | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
|
||||
| Rust + Tokio | 2M+ | 2-20ms | ⭐⭐ | ⭐⭐⭐ |
|
||||
| Node.js | 100-500K | 10-100ms | ⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
| Elixir/Phoenix | 2M+ | 5-50ms | ⭐⭐⭐ | ⭐⭐ |
|
||||
|
||||
## Justification
|
||||
|
||||
### Pourquoi Go plutôt que Rust ?
|
||||
|
||||
Rust offre meilleures performances absolues (2M conn/serveur vs 1M, 0 GC pauses) mais **Go gagne sur le plan startup** :
|
||||
|
||||
1. **Time-to-market critique** : MVP en 8 semaines vs 12+ pour Rust
|
||||
- Courbe d'apprentissage borrow checker = grosse friction pour juniors
|
||||
- Temps compilation + refactoring : 30-60s vs 1-2s Go
|
||||
- Recrutement moins onéreux (€35-50K junior Go vs €50-70K Rust)
|
||||
|
||||
2. **Écosystème production-ready pour RoadWave** :
|
||||
- **WebRTC** : pion/webrtc (mature) vs webrtc.rs (naissant)
|
||||
- **Tests BDD** : Godog/Gherkin natif en Go, pas d'équivalent Rust
|
||||
- **PostgreSQL + PostGIS** : pgx (excellent) vs sqlx (bon mais moins mature)
|
||||
- **Zitadel/OAuth2** : clients Go stables vs Rust émergents
|
||||
|
||||
3. **Performance suffisante pour 10M users distribués** :
|
||||
- 1M conn/serveur = 10 serveurs max pour pics
|
||||
- GC pauses (10-100ms) acceptable avec stratégie multi-région
|
||||
- Scaling horizontal plus simple que vertical
|
||||
|
||||
4. **Tooling natif** :
|
||||
- pprof intégré (CPU, mémoire)
|
||||
- race detector systématique
|
||||
- Kubernetes first-class
|
||||
- Cold start ~10ms (vs ~50ms Rust)
|
||||
|
||||
### Quand Rust aurait du sens
|
||||
|
||||
- Si concentrations d'1M+ connexions sur serveur unique (cas rare RoadWave)
|
||||
- Si p99 latencies en prod > 100ms deviennent bottleneck (après Growth phase)
|
||||
- Si refonte majeure planifiée anyway
|
||||
- Stratégie possible : réécrire services hot (WebRTC, HLS streaming) en Rust à phase Scale
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Formation équipe sur Go si nécessaire
|
||||
- Utilisation des bibliothèques : Fiber (HTTP), pgx (PostgreSQL), go-redis
|
||||
- Monitoring GC pauses en production (cibler < 20ms p95)
|
||||
- Potential migration partielle à Rust pour services critiques post-Series A
|
||||
182
docs/adr/002-protocole-streaming.md
Normal file
182
docs/adr/002-protocole-streaming.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# ADR-002 : Protocole de Streaming
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-17
|
||||
|
||||
## Contexte
|
||||
|
||||
Streaming audio vers des utilisateurs mobiles en voiture, avec réseaux instables (tunnels, zones rurales, handoff cellulaire).
|
||||
|
||||
## Décision
|
||||
|
||||
**HLS** (HTTP Live Streaming) pour le contenu à la demande.
|
||||
**WebRTC** réservé à la radio live.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Option | Latence | Fiabilité mobile | Cache CDN | Complexité |
|
||||
|--------|---------|------------------|-----------|------------|
|
||||
| **HLS** | 5-30s | Excellente | Oui | Faible |
|
||||
| DASH | 5-30s | Bonne | Oui | Moyenne |
|
||||
| WebRTC | <500ms | Moyenne | Non | Élevée |
|
||||
| UDP brut | Minimale | Faible | Non | Très élevée |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Réseaux mobiles** : HLS gère les coupures et changements de cellule nativement
|
||||
- **Cache CDN** : Segments .ts cachables = réduction des coûts
|
||||
- **Compatibilité** : Support natif iOS/Android
|
||||
- **Bitrate adaptatif** : Ajustement automatique selon la qualité réseau
|
||||
|
||||
## Pourquoi pas UDP ?
|
||||
|
||||
- Problèmes NAT/firewall sur réseaux mobiles
|
||||
- Perte de paquets = artefacts audio
|
||||
- Impossible à cacher sur CDN
|
||||
- Complexité sans bénéfice pour du contenu non-interactif
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Latence de 5-30s acceptable pour podcasts/audio-guides (avec pré-buffering, voir section 4.3)
|
||||
- WebRTC à implémenter séparément pour la radio live
|
||||
|
||||
## Gestion de la Latence et Synchronisation Géolocalisée
|
||||
|
||||
### Problème Identifié
|
||||
|
||||
La latence HLS (5-30s) entre en conflit avec les notifications géolocalisées qui doivent déclencher l'audio **au moment précis** où l'utilisateur atteint un point d'intérêt.
|
||||
|
||||
**Exemple critique** :
|
||||
- Utilisateur en voiture à 90 km/h (25 m/s)
|
||||
- ETA de 7 secondes avant le point → notification affichée
|
||||
- Latence HLS de 15 secondes
|
||||
- Résultat : audio démarre **200 mètres après** le point d'intérêt ❌
|
||||
|
||||
### Solution : Pre-buffering Anticipé + ETA Adaptatif
|
||||
|
||||
#### 4.3.1 Pre-buffering Automatique
|
||||
|
||||
**Déclenchement** : À ETA = 30 secondes du prochain point d'intérêt
|
||||
|
||||
```
|
||||
[App Mobile]
|
||||
↓ (ETA=30s, position GPS détectée)
|
||||
[Cache Manager]
|
||||
↓ (télécharge en arrière-plan)
|
||||
[CDN NGINX] → /audio/poi-{id}/intro.m4a (10-15s d'audio, ~5-8 MB)
|
||||
↓
|
||||
[Cache Local Mobile] (max 3 POI simultanés)
|
||||
```
|
||||
|
||||
**Stratégie de cache** :
|
||||
- Télécharge les **15 premières secondes** de chaque POI à proximité
|
||||
- Limite : 3 POI simultanés en cache (max ~25 MB)
|
||||
- Purge automatique après 200m de distance passée
|
||||
- Format : M4A haute qualité (128 kbps) pour intro, puis bascule HLS pour la suite
|
||||
|
||||
#### 4.3.2 ETA de Notification Adaptatif
|
||||
|
||||
**Algorithme** :
|
||||
|
||||
```python
|
||||
def calculate_notification_eta(poi, user_position, user_speed):
|
||||
distance_to_poi = haversine(user_position, poi.position)
|
||||
is_cached = cache.has(poi.audio_id)
|
||||
hls_latency = metrics.get_average_latency(user_id) # 8-18s typique
|
||||
|
||||
if is_cached:
|
||||
# Cache prêt → notification courte (temps de réaction)
|
||||
notification_eta = 5 # secondes
|
||||
else:
|
||||
# Pas de cache → compenser latence HLS + marge
|
||||
notification_eta = hls_latency + 3 + 2 # latence + marge + réaction
|
||||
# Typique: 10s + 3s + 2s = 15s
|
||||
|
||||
time_to_poi = distance_to_poi / user_speed
|
||||
|
||||
if time_to_poi <= notification_eta:
|
||||
send_notification(poi)
|
||||
|
||||
if time_to_poi <= 30 and not is_cached:
|
||||
cache.preload_async(poi.audio_id)
|
||||
```
|
||||
|
||||
**Résultat** :
|
||||
|
||||
| Situation | ETA Notification | Distance (à 90 km/h) | Expérience |
|
||||
|-----------|------------------|----------------------|------------|
|
||||
| Cache prêt | 5s | 125m avant | ✅ Lecture instantanée |
|
||||
| Cache en cours | 15s | 375m avant | ✅ Lecture à temps |
|
||||
| 3G lent | 20s | 500m avant | ✅ Compensation latence |
|
||||
|
||||
#### 4.3.3 Mesure Dynamique de Latence
|
||||
|
||||
**Tracking par utilisateur** :
|
||||
|
||||
```go
|
||||
type HLSMetrics struct {
|
||||
UserID uuid.UUID
|
||||
AvgLatency time.Duration // Moyenne glissante 10 lectures
|
||||
NetworkType string // "4G", "5G", "3G", "wifi"
|
||||
LastMeasured time.Time
|
||||
}
|
||||
|
||||
// Mesure lors de chaque lecture
|
||||
func (m *HLSMetrics) RecordPlaybackStart(requestTime, firstByteTime time.Time) {
|
||||
latency := firstByteTime.Sub(requestTime)
|
||||
m.AvgLatency = (m.AvgLatency*9 + latency) / 10 // Moyenne glissante
|
||||
}
|
||||
```
|
||||
|
||||
**Utilisation** : L'ETA adaptatif utilise `AvgLatency` personnalisé par utilisateur au lieu d'une valeur fixe.
|
||||
|
||||
#### 4.3.4 Fallback et Indicateurs Visuels
|
||||
|
||||
Si le pre-buffer échoue (réseau faible, pas de cache), afficher un **loader avec progression** :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 🏰 Château de Fontainebleau │
|
||||
│ │
|
||||
│ [████████░░] 80% │
|
||||
│ Chargement de l'audio... │
|
||||
│ │
|
||||
│ 📍 Vous arrivez dans 12s │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Feedback visuel** : Utilisateur comprend que ça charge
|
||||
- **ETA affiché** : Maintient l'attention et réduit la frustration perçue
|
||||
- **Timeout** : Si > 30s sans succès, proposer mode dégradé (texte seul)
|
||||
|
||||
### Impact sur l'Infrastructure
|
||||
|
||||
#### Backend (Go)
|
||||
- **Nouveau service** : `audiocache.Service` pour préparer les extraits M4A
|
||||
- **Endpoint** : `GET /api/v1/audio/poi/:id/intro` (retourne 15s d'audio)
|
||||
- **CDN** : Cache NGINX avec TTL 7 jours sur `/audio/*/intro.m4a`
|
||||
|
||||
#### Mobile (Flutter)
|
||||
- **Package** : `just_audio` avec cache local (`flutter_cache_manager`)
|
||||
- **Stockage** : Max 100 MB de cache audio (auto-purge LRU)
|
||||
- **Logique** : `PreBufferService` avec scoring de priorité POI
|
||||
|
||||
#### Coûts
|
||||
- **Bande passante** : +10-15 MB/utilisateur/session (vs streaming pur)
|
||||
- **Stockage CDN** : +500 MB pour 1000 POI × 5 MB intro (négligeable)
|
||||
- **Économie** : Cache CDN réduit les requêtes origin (-60% selon tests)
|
||||
|
||||
### Métriques de Succès
|
||||
|
||||
- **Latence perçue** : < 1 seconde dans 95% des cas (cache hit)
|
||||
- **Synchronisation** : Audio démarre à ±10 mètres du POI (objectif ±50m)
|
||||
- **Cache hit rate** : > 90% pour utilisateurs en mode navigation
|
||||
- **Consommation data** : +15% vs HLS pur, mais acceptable pour UX
|
||||
|
||||
### Références
|
||||
|
||||
- [HLS Authoring Specification](https://developer.apple.com/documentation/http_live_streaming/hls_authoring_specification_for_apple_devices)
|
||||
- [Low-Latency HLS (LL-HLS)](https://developer.apple.com/documentation/http_live_streaming/enabling_low-latency_hls)
|
||||
- Règle Métier 05 : Section 5.2 (Mode Voiture, lignes 16-84)
|
||||
- Règle Métier 17 : Section 17.2 (ETA Géolocalisé, lignes 25-65)
|
||||
- **ADR-019** : Architecture des Notifications Géolocalisées
|
||||
41
docs/adr/003-codec-audio.md
Normal file
41
docs/adr/003-codec-audio.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# ADR-003 : Codec Audio
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-17
|
||||
|
||||
## Contexte
|
||||
|
||||
Audio diffusé en voiture : environnement bruyant, réseau mobile variable, qualité studio non nécessaire.
|
||||
|
||||
## Décision
|
||||
|
||||
**Opus** comme codec principal, **AAC-LC** en fallback.
|
||||
|
||||
## Profils d'encodage
|
||||
|
||||
| Qualité | Bitrate | Usage |
|
||||
|---------|---------|-------|
|
||||
| Basse | 24 kbps | 2G/Edge |
|
||||
| Standard | 48 kbps | 3G |
|
||||
| Haute | 64 kbps | 4G/5G |
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Codec | Bitrate | Qualité voix | Support mobile |
|
||||
|-------|---------|--------------|----------------|
|
||||
| **Opus** | 24-64 kbps | Excellente | Android natif, iOS via libs |
|
||||
| AAC-LC | 64-128 kbps | Bonne | Universel |
|
||||
| AAC-HE v2 | 32-64 kbps | Très bonne | Bon |
|
||||
| MP3 | 128-320 kbps | Correcte | Universel (legacy) |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Environnement bruyant** : Opus intègre des algorithmes de résilience au bruit
|
||||
- **Bande passante** : 48 kbps Opus ≈ qualité 96 kbps AAC pour la voix
|
||||
- **Consommation data** : ~20 MB/heure à 48 kbps
|
||||
- **Latence** : 2.5-60ms, idéal pour streaming adaptatif
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Fallback AAC-LC pour appareils legacy
|
||||
- Pipeline d'encodage à prévoir côté ingestion
|
||||
56
docs/adr/004-cdn.md
Normal file
56
docs/adr/004-cdn.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# ADR-004 : CDN
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-25
|
||||
|
||||
## Contexte
|
||||
|
||||
Distribution audio HLS à 10M d'utilisateurs, besoin de performance, coût maîtrisé, et **souveraineté** : pas de dépendance à un service commercial tiers.
|
||||
|
||||
## Décision
|
||||
|
||||
**NGINX auto-hébergé** sur OVH comme cache HLS, avec OVH Object Storage en origin.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Solution | Coût MVP/mois | Souveraineté | Setup | Dépendance |
|
||||
|----------|---------------|--------------|-------|------------|
|
||||
| **NGINX self-hosted** | ~14€ | 100% FR | 2h | Aucune |
|
||||
| CDN commercial externe | ~1 000€ | Slovénie | 15 min | Forte |
|
||||
| OVHcloud CDN | ~50-200€ | 100% FR | 1h | Moyenne |
|
||||
| Cloudflare | 0-5 000€ | US | 5 min | Forte |
|
||||
| CloudFront | ~9 750€ | US (AWS) | 1h | Très forte |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Souveraineté** : 100% français (OVH), 100% contrôlé, zéro dépendance commerciale
|
||||
- **Open-source** : NGINX sous licence BSD, stack entièrement libre
|
||||
- **Coût** : ~14€/mois MVP (VPS inclus), scaling linéaire et prévisible
|
||||
- **Performance** : Cache multiplexing 1→1000 (1 Mbps origin → 1 Gbps clients)
|
||||
- **Conformité RGPD** : Données hébergées en France
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Clients
|
||||
↓
|
||||
NGINX Cache Proxy (OVH VPS Essential)
|
||||
├─ Cache RAM disk (2GB)
|
||||
├─ TTL .m3u8: 5s
|
||||
└─ TTL .ts: 7 jours
|
||||
↓
|
||||
OVH Object Storage (origin)
|
||||
```
|
||||
|
||||
## Évolution prévue
|
||||
|
||||
1. **Phase 1** (0-20K users) : NGINX mono-région
|
||||
2. **Phase 2** (20-100K users) : NGINX multi-région (Gravelines + Strasbourg) + GeoDNS
|
||||
3. **Phase 3** (100K+) : Évaluation CDN managé européen si ROI justifié
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Configuration NGINX avec `proxy_cache_path` et règles de TTL
|
||||
- RAM disk `/mnt/ramdisk` pour performance maximale
|
||||
- Monitoring bande passante et taux de cache hit
|
||||
- Sécurité : token authentication pour protéger les segments HLS
|
||||
67
docs/adr/005-base-de-donnees.md
Normal file
67
docs/adr/005-base-de-donnees.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# ADR-005 : Base de Données
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-17
|
||||
|
||||
## Contexte
|
||||
|
||||
Requêtes géolocalisées intensives (contenus à proximité), données utilisateurs, historiques d'écoute.
|
||||
|
||||
## Décision
|
||||
|
||||
- **PostgreSQL + PostGIS** : Données persistantes et requêtes géospatiales
|
||||
- **Redis Cluster** : Cache géolocalisation et sessions
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Requête → Redis Cache → [HIT] → Réponse
|
||||
↓
|
||||
[MISS]
|
||||
↓
|
||||
PostGIS → Cache → Réponse
|
||||
```
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Usage | Option choisie | Alternatives |
|
||||
|-------|---------------|--------------|
|
||||
| Données utilisateurs | PostgreSQL | MySQL, MongoDB |
|
||||
| Géolocalisation | PostGIS | MongoDB Geo, Elasticsearch |
|
||||
| Cache | Redis | Memcached, KeyDB |
|
||||
| Analytics (futur) | ClickHouse | TimescaleDB |
|
||||
|
||||
## Justification
|
||||
|
||||
### PostgreSQL + PostGIS
|
||||
- Requêtes géospatiales complexes et précises
|
||||
- Index GIST pour performance
|
||||
- ACID, fiabilité éprouvée
|
||||
- Écosystème mature
|
||||
|
||||
### Redis
|
||||
- Cache géo natif (`GEORADIUS`) : 100K+ requêtes/sec
|
||||
- Sessions utilisateurs
|
||||
- Pub/sub pour temps réel
|
||||
|
||||
## Exemple de requête
|
||||
|
||||
```sql
|
||||
SELECT id, name,
|
||||
ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance
|
||||
FROM contents
|
||||
WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000)
|
||||
ORDER BY distance
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
## Conséquences
|
||||
|
||||
- TTL cache Redis : 5 minutes (le contenu géolocalisé ne bouge pas)
|
||||
- Index GIST sur colonnes géométriques
|
||||
- Réplication read replicas pour scaling lecture
|
||||
|
||||
## Documentation technique détaillée
|
||||
|
||||
- [Diagramme de séquence cache géospatial](../architecture/sequences/cache-geospatial.md)
|
||||
- [Schéma base de données](../architecture/database/schema.md)
|
||||
44
docs/adr/006-chiffrement.md
Normal file
44
docs/adr/006-chiffrement.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# ADR-006 : Chiffrement
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-17
|
||||
|
||||
## Contexte
|
||||
|
||||
Streaming audio sur réseaux mobiles, conformité RGPD, protection du contenu.
|
||||
|
||||
## Décision
|
||||
|
||||
- **TLS 1.3** sur tous les endpoints
|
||||
- **DTLS-SRTP** pour WebRTC (radio live)
|
||||
- Pas de DRM au lancement
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Méthode | Overhead | Usage |
|
||||
|---------|----------|-------|
|
||||
| **TLS 1.3** | ~1-2% CPU | HTTPS streaming |
|
||||
| DTLS-SRTP | ~3-5% CPU | WebRTC temps réel |
|
||||
| AES-128-CBC | Minimal | Chiffrement segments HLS |
|
||||
| Widevine/FairPlay | Modéré | DRM (si licences l'exigent) |
|
||||
|
||||
## Justification
|
||||
|
||||
### Pourquoi chiffrer ?
|
||||
|
||||
- **RGPD** : Protection des données utilisateurs obligatoire
|
||||
- **Confiance** : Standard attendu en 2025
|
||||
- **Intégrité** : Empêche injection de contenu par opérateurs
|
||||
- **Overhead minimal** : TLS 1.3 optimisé, impact négligeable
|
||||
|
||||
### Pourquoi pas de DRM ?
|
||||
|
||||
- Contenu généré par utilisateurs (pas de licences)
|
||||
- Complexité et coût d'intégration Widevine/FairPlay
|
||||
- À reconsidérer si partenariats avec labels/éditeurs
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Certificats SSL gérés par Let's Encrypt (auto-renouvelés via Nginx)
|
||||
- Configuration TLS 1.3 sur Nginx/API
|
||||
- DTLS-SRTP à implémenter pour le module radio live
|
||||
62
docs/adr/007-tests-bdd.md
Normal file
62
docs/adr/007-tests-bdd.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# ADR-007 : Tests et Spécifications Exécutables
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-17
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite une documentation des use cases qui soit à la fois lisible par tous les stakeholders et vérifiable automatiquement. Les scénarios utilisateurs (touriste, routier, commerçant) doivent être validés en continu.
|
||||
|
||||
## Décision
|
||||
|
||||
**Gherkin** pour les spécifications avec **Godog** comme runner de tests.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Option | Lisibilité | Intégration Go | Maintenance |
|
||||
|--------|------------|----------------|-------------|
|
||||
| **Gherkin + Godog** | Excellente | Native | Faible |
|
||||
| Gauge (Markdown) | Bonne | Plugin | Moyenne |
|
||||
| Tests Go natifs | Faible (devs only) | Native | Faible |
|
||||
| Concordion | Bonne | Java-centric | Élevée |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Living Documentation** : Les fichiers `.feature` servent de documentation ET de tests
|
||||
- **Accessibilité** : Syntaxe Given/When/Then lisible par PO, devs, testeurs
|
||||
- **Cohérence stack** : Godog est le standard BDD pour Go
|
||||
- **CI/CD** : Intégration simple dans les pipelines
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
features/
|
||||
├── recommendation/
|
||||
│ ├── geolocalisation.feature
|
||||
│ └── interets.feature
|
||||
├── streaming/
|
||||
│ ├── lecture.feature
|
||||
│ └── buffering.feature
|
||||
├── moderation/
|
||||
│ └── signalement.feature
|
||||
└── steps/
|
||||
└── steps.go
|
||||
```
|
||||
|
||||
## Exemple
|
||||
|
||||
```gherkin
|
||||
Feature: Recommandation géolocalisée
|
||||
|
||||
Scenario: Touriste près d'un monument
|
||||
Given un utilisateur avec l'intérêt "tourisme" à 80%
|
||||
And une position GPS à 100m de la Tour Eiffel
|
||||
When le système calcule les recommandations
|
||||
Then l'audio guide "Histoire de la Tour Eiffel" est en première position
|
||||
```
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Dépendance : `github.com/cucumber/godog`
|
||||
- Les use cases du README doivent être traduits en `.feature`
|
||||
- CI exécute `godog run` avant chaque merge
|
||||
119
docs/adr/008-authentification.md
Normal file
119
docs/adr/008-authentification.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# ADR-008 : Authentification et Gestion d'Identité
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-18
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite un système d'authentification sécurisé pour mobile (iOS/Android), scalable jusqu'à 10M utilisateurs, avec contraintes de coût réduit et conformité RGPD.
|
||||
|
||||
**Exigence de souveraineté** : En cohérence avec ADR-004 (CDN 100% français), les données d'authentification doivent être hébergées en France pour garantir une souveraineté totale.
|
||||
|
||||
## Décision
|
||||
|
||||
**Zitadel self-hosted sur OVH France** pour l'IAM avec validation JWT locale côté API Go.
|
||||
|
||||
**Architecture de déploiement** :
|
||||
- Container Docker sur le même VPS OVH (Gravelines, France) que l'API
|
||||
- Base de données PostgreSQL partagée avec RoadWave (séparation logique par schéma)
|
||||
- Aucune donnée d'authentification ne transite par des serveurs tiers
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Solution | Coût (10M users) | Performance | Simplicité | Intégration Go |
|
||||
|----------|------------------|-------------|------------|----------------|
|
||||
| **Zitadel** | 200-500€/mois | Excellente | Élevée | SDK natif |
|
||||
| Supabase Auth | 32K€/mois | Excellente | Élevée | REST API |
|
||||
| Keycloak | 200-800€/mois | Bonne | Faible | Lib tierce |
|
||||
| Auth0 | 50K€+/mois | Excellente | Élevée | SDK natif |
|
||||
| JWT Custom | 0€ (dev) | Excellente | Moyenne | Natif |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Souveraineté garantie** : Self-hosting sur OVH France (Gravelines) = 100% des données en France, cohérent avec ADR-004
|
||||
- **Coût maîtrisé** : 100x moins cher que Supabase/Auth0 à 10M users (pas de coût par utilisateur actif)
|
||||
- **Performance** : JWT validation locale = 0 latence auth sur chaque requête API
|
||||
- **Stack alignée** : Go + PostgreSQL + Redis (déjà dans RoadWave)
|
||||
- **Scalabilité prouvée** : Clients avec 2.3M tenants, architecture event-sourced
|
||||
- **RGPD natif** : Open source, contrôle total des données, DPA non nécessaire (pas de sous-traitant)
|
||||
- **Standards ouverts** : OpenID Connect certifié (pas de vendor lock-in, migration facile si besoin)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Mobile Apps │ OAuth2 PKCE + Refresh tokens
|
||||
│ (iOS/Android) │
|
||||
└────────┬────────┘
|
||||
│ HTTPS
|
||||
│
|
||||
┌────▼─────────────────────────────────┐
|
||||
│ OVH VPS Essential (Gravelines, FR) │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ Zitadel IdP │ Port 8081 │
|
||||
│ │ (Docker) │ Self-hosted │
|
||||
│ └────────┬────────┘ │
|
||||
│ │ JWT token │
|
||||
│ ┌────────▼────────┐ │
|
||||
│ │ Go + Fiber API │ Port 8080 │
|
||||
│ │ (RoadWave) │ Validation │
|
||||
│ │ │ JWT locale │
|
||||
│ └────────┬────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────▼────────┐ │
|
||||
│ │ PostgreSQL │ Schémas: │
|
||||
│ │ + PostGIS │ - roadwave │
|
||||
│ │ │ - zitadel │
|
||||
│ └─────────────────┘ │
|
||||
└───────────────────────────────────────┘
|
||||
|
||||
Données 100% hébergées en France (souveraineté totale)
|
||||
```
|
||||
|
||||
## Exemple d'intégration
|
||||
|
||||
```go
|
||||
import "github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth"
|
||||
|
||||
// Validation JWT locale haute performance
|
||||
verifier := oauth.WithJWT(config)
|
||||
app.Use(verifier.Middleware())
|
||||
|
||||
// Accès aux claims
|
||||
userID := ctx.Locals("sub").(string)
|
||||
```
|
||||
|
||||
## Conséquences
|
||||
|
||||
### Positives
|
||||
|
||||
- ✅ **Souveraineté totale** : Données 100% en France (OVH Gravelines), contrôle complet
|
||||
- ✅ **Coût prévisible** : Pas de surprise à la croissance (pas de facturation par utilisateur)
|
||||
- ✅ **Performance** : Latence minimale (même VPS que l'API)
|
||||
- ✅ **Fonctionnalités avancées** : MFA, passkeys, SSO disponibles gratuitement
|
||||
- ✅ **Conformité RGPD** : Pas de DPA nécessaire (pas de sous-traitant externe)
|
||||
- ✅ **Standards ouverts** : Migration facile vers autre solution si besoin
|
||||
|
||||
### Négatives
|
||||
|
||||
- ❌ **Maintenance** : Nécessite monitoring et mises à jour régulières
|
||||
- ❌ **Complexité initiale** : Configuration PostgreSQL schema partagé
|
||||
- ❌ **Backup** : Responsabilité de sauvegarder les données utilisateurs
|
||||
- ❌ **Scaling** : Migration Kubernetes nécessaire au-delà de 500K utilisateurs
|
||||
|
||||
### Déploiement
|
||||
|
||||
- **MVP (0-20K)** : Docker sur VPS OVH Essential (coût inclus)
|
||||
- **Growth (20K-500K)** : Même architecture, VPS plus puissant si besoin
|
||||
- **Scale (500K+)** : Migration Kubernetes managé avec haute disponibilité
|
||||
|
||||
### Coût Estimé
|
||||
|
||||
| Phase | Utilisateurs | Coût Zitadel/mois |
|
||||
|-------|--------------|-------------------|
|
||||
| MVP | 0-20K | 0€ (inclus VPS) |
|
||||
| Growth | 20K-500K | 0€ (inclus VPS) |
|
||||
| Scale | 500K+ | 50-100€ (instance K8s dédiée) |
|
||||
|
||||
**Comparaison** : Auth0 coûterait 50K€/mois pour 10M utilisateurs vs 100€/mois en self-hosted.
|
||||
61
docs/adr/009-solution-paiement.md
Normal file
61
docs/adr/009-solution-paiement.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# ADR-009 : Solution de Paiement et Gestion des Abonnements
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-19
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite une solution de paiement pour gérer les abonnements Premium (4.99€/mois) et reverser 70% des revenus aux créateurs de contenu. Besoin de marketplace natif (split payments), KYC automatique, conformité RGPD, et coûts maîtrisés.
|
||||
|
||||
## Décision
|
||||
|
||||
**Mangopay** (France/Luxembourg) comme solution unique pour paiements, marketplace et abonnements.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Solution | Coût transaction | Marketplace | KYC | Souveraineté |
|
||||
|----------|-----------------|-------------|-----|--------------|
|
||||
| **Mangopay** | 1.8% + 0.18€ | ✅ Natif | ✅ Gratuit | 🇪🇺 France/LU |
|
||||
| Stripe Connect | 2.9% + 0.30€ | ✅ Natif | ❌ 1.20€ | 🇺🇸 USA |
|
||||
| Mollie | 2.9% + 0.29€ | ❌ Non | ❌ Non | 🇪🇺 Pays-Bas |
|
||||
| Paddle | 5% + 0.50€ | ✅ Natif | ✅ Inclus | 🇬🇧 UK |
|
||||
|
||||
## Justification
|
||||
|
||||
- **38% moins cher** que Stripe (1.8% vs 2.9%)
|
||||
- **Marketplace natif** : E-wallets automatiques, split payments 70/30, payouts SEPA gratuits
|
||||
- **KYC gratuit** : vérification d'identité incluse (vs 1.20€/créateur chez Stripe)
|
||||
- **Souveraineté EU** : France/Luxembourg, régulé ACPR, RGPD natif
|
||||
- **Conformité DAC7** : reporting fiscal automatique
|
||||
- **Spécialisé marketplace** : utilisé par Vinted, Ulule, ManoMano
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌────────────────────────┐
|
||||
│ Utilisateurs Premium │ 4.99€/mois
|
||||
└───────────┬────────────┘
|
||||
│
|
||||
┌───────▼───────┐
|
||||
│ Mangopay │ - Abonnements récurrents
|
||||
│ │ - KYC créateurs (gratuit)
|
||||
│ │ - E-wallets automatiques
|
||||
└───────┬───────┘ - Payouts SEPA (gratuits)
|
||||
│
|
||||
┌─────────┼─────────┐
|
||||
│ │ │
|
||||
┌─▼───┐ ┌─▼───┐ ┌─▼────┐
|
||||
│Créa │ │Créa │ │Plate-│
|
||||
│teur │ │teur │ │forme │
|
||||
│ A │ │ B │ │(30%) │
|
||||
│(70%)│ │(70%)│ │ │
|
||||
└─────┘ └─────┘ └──────┘
|
||||
```
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Solution tout-en-un : 1 seul prestataire vs 2-3
|
||||
- Économie de 2160€/an sur 1000 abonnés (vs Stripe)
|
||||
- Délai activation compte : 2-5 jours
|
||||
- Intégration Go via REST API (pas de SDK Go officiel)
|
||||
- Apple/Google IAP gérés séparément (comme toute solution de paiement)
|
||||
46
docs/adr/010-commandes-volant.md
Normal file
46
docs/adr/010-commandes-volant.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# ADR-010 : Commandes au volant et likes
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2026-01-20
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave est utilisée en conduisant. Les utilisateurs doivent pouvoir liker du contenu pour améliorer les recommandations, mais les commandes au volant ont des limitations :
|
||||
- 40% des véhicules n'ont que Suivant/Précédent/Mute
|
||||
- iOS/Android ne supportent pas nativement les appuis longs ou doubles-appuis
|
||||
- La sécurité impose des interactions minimales
|
||||
|
||||
## Décision
|
||||
|
||||
**Like automatique basé sur le temps d'écoute**.
|
||||
|
||||
Règles :
|
||||
- ≥80% d'écoute → Like renforcé (+2 points)
|
||||
- 30-79% d'écoute → Like standard (+1 point)
|
||||
- <30% d'écoute → Pas de like
|
||||
- Skip <10s → Signal négatif (-0.5 point)
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Option | Compatibilité | Sécurité | Complexité |
|
||||
|--------|---------------|----------|------------|
|
||||
| **Like automatique** | 100% | Maximale | Faible |
|
||||
| Double-tap Pause | ~80% | Moyenne | Moyenne |
|
||||
| Appui long Suivant | ~95% | Faible | Élevée |
|
||||
| Configuration paramétrable | 100% | Variable | Très élevée |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Sécurité maximale** : Aucune action complexe en conduite
|
||||
- **Compatibilité universelle** : Fonctionne sur 100% des véhicules
|
||||
- **UX intuitive** : Comportement standard (Spotify, YouTube Music)
|
||||
- **Engagement** : Tous les contenus génèrent des signaux
|
||||
- **Simplicité** : Une seule logique à implémenter et maintenir
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Tracking du temps d'écoute via le player audio
|
||||
- Calcul du score côté backend basé sur `completion_rate`
|
||||
- Communication onboarding : "Vos likes sont automatiques selon votre temps d'écoute"
|
||||
- Possibilité de like manuel depuis l'app (à l'arrêt)
|
||||
- Métriques à suivre : taux de complétion, distribution des scores, feedbacks utilisateurs
|
||||
137
docs/adr/011-conformite-stores-carplay-android-auto.md
Normal file
137
docs/adr/011-conformite-stores-carplay-android-auto.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# ADR-011 : Conformité App Stores et Plateformes Auto
|
||||
|
||||
**Statut** : Accepté avec actions requises
|
||||
**Date** : 2026-01-20
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave est une app audio géolocalisée utilisée en conduite (CarPlay/Android Auto) avec :
|
||||
- Contenu généré par utilisateurs (UGC)
|
||||
- Monétisation : publicités géolocalisées + Premium (4.99€ web / 5.99€ IAP)
|
||||
- GPS en arrière-plan
|
||||
- Partage de revenus avec créateurs (70/30)
|
||||
|
||||
## Décision
|
||||
|
||||
**Stratégie de conformité multi-plateforme** avec :
|
||||
- Modération UGC robuste (IA + humain)
|
||||
- Prix différenciés selon région (US/EU/Monde)
|
||||
- GPS avec disclosure complète
|
||||
- Paiements créateurs externes (Mangopay)
|
||||
|
||||
## Plateformes analysées
|
||||
|
||||
| Plateforme | Conformité | Points critiques |
|
||||
|------------|------------|------------------|
|
||||
| **Android Auto** | ✅ Conforme | API Level 35+ (Android 15+) |
|
||||
| **CarPlay** | ✅ Conforme | Entitlement audio à demander |
|
||||
| **Google Play** | ⚠️ Actions requises | Déclaration GPS + UGC modération |
|
||||
| **App Store** | ⚠️ Actions requises | Prix différenciés US/EU |
|
||||
|
||||
## Conformité détaillée
|
||||
|
||||
### Android Auto / CarPlay ✅
|
||||
- 100% audio (pas de vidéo)
|
||||
- Commandes standard au volant
|
||||
- Aucun achat in-car
|
||||
- Like automatique = sécurité maximale
|
||||
- **Notifications géolocalisées** : sonore uniquement en mode CarPlay/Android Auto (pas d'overlay visuel)
|
||||
- **Action** : Demander CarPlay Audio Entitlement (Apple)
|
||||
|
||||
### Google Play ⚠️
|
||||
|
||||
**UGC (critique)** :
|
||||
- Modération hybride IA + humain ✅
|
||||
- 3 premiers contenus validés manuellement ✅
|
||||
- Système de strikes (4 = ban) ✅
|
||||
- Signalement + blocage utilisateurs ✅
|
||||
|
||||
**GPS Background (critique)** :
|
||||
- Permission "Always Location" = **OPTIONNELLE**
|
||||
- Demandée uniquement pour mode piéton (notifications arrière-plan audio-guides)
|
||||
- Justification Play Console :
|
||||
> "RoadWave permet aux utilisateurs de recevoir des alertes audio-guides lorsqu'ils passent à pied près de monuments/musées, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée dans les paramètres."
|
||||
- In-app disclosure obligatoire (écran dédié avant demande permission)
|
||||
- Si refusée : app fonctionne en mode voiture uniquement
|
||||
- **Action** : Remplir formulaire background location Play Console avec justification
|
||||
|
||||
**Réponses formulaire Play Console** :
|
||||
|
||||
| Question | Réponse |
|
||||
|----------|---------|
|
||||
| Why does your app need background location? | "RoadWave offers optional pedestrian mode: users receive push notifications when passing near audio-guide points (museums, monuments) even when app is in background. This feature is opt-in and can be disabled in settings." |
|
||||
| Is this feature core to your app? | "No. This is an optional feature. Users can use RoadWave without background location permission (in-car mode works with foreground location only)." |
|
||||
| What user value does this provide? | "Pedestrian users (tourists, museum visitors) can keep phone in pocket and receive audio-guide alerts automatically without opening the app." |
|
||||
| Does a less invasive alternative exist? | "Yes. Users can use manual navigation (open app, select audio-guide). Background location is a convenience feature for hands-free experience." |
|
||||
|
||||
### App Store ⚠️
|
||||
|
||||
**Prix différenciés (légaux depuis 2025-2026)** :
|
||||
- 🇺🇸 US : Lien externe autorisé (0% commission)
|
||||
- 🇪🇺 EU : Paiement externe DMA (7-20% commission réduite)
|
||||
- 🌍 Monde : IAP obligatoire (30% commission)
|
||||
|
||||
**UGC** :
|
||||
- Mode Kids obligatoire (filtrage selon âge) ✅
|
||||
- Système de modération + signalement ✅
|
||||
|
||||
**GPS Background (critique)** :
|
||||
- Permission "Always Location" = **OPTIONNELLE**
|
||||
- Deux strings Info.plist requises :
|
||||
- `NSLocationWhenInUseUsageDescription` : explication mode voiture
|
||||
- `NSLocationAlwaysAndWhenInUseUsageDescription` : explication mode piéton (optionnel)
|
||||
- In-app disclosure obligatoire avant demande "Always"
|
||||
- Flux two-step : When In Use → Always (si user active mode piéton)
|
||||
- Si refusée : app fonctionne en mode voiture uniquement
|
||||
- **Action** : Voir strings détaillés dans [05-interactions-navigation.md](../regles-metier/05-interactions-navigation.md#512-mode-piéton-audio-guides)
|
||||
|
||||
### Revenus créateurs
|
||||
|
||||
**Position** : Paiements créateurs = "services" (comme YouTube/Uber), pas IAP
|
||||
- Paiement via Mangopay Connect (externe)
|
||||
- Commission stores uniquement sur Premium (IAP)
|
||||
- Comparables : YouTube AdSense, TikTok Creator Fund, Uber
|
||||
|
||||
## Actions bloquantes avant soumission
|
||||
|
||||
| Action | Plateforme | Deadline | Complexité |
|
||||
|--------|-----------|----------|------------|
|
||||
| Demander CarPlay Audio Entitlement | Apple | Avant soumission iOS | Faible |
|
||||
| Remplir formulaire background location avec justification | Google Play | Avant soumission Android | Faible |
|
||||
| Implémenter disclosure GPS (écran dédié mode piéton) | iOS + Android | MVP | Moyenne |
|
||||
| Rendre permission "Always Location" optionnelle | iOS + Android | MVP | Moyenne |
|
||||
| Désactiver overlay visuel notification en CarPlay/Android Auto | iOS + Android | MVP | Moyenne |
|
||||
| Mettre à jour strings Info.plist avec justifications détaillées | iOS | MVP | Faible |
|
||||
| Finaliser système modération UGC | Google + Apple | MVP | Élevée |
|
||||
|
||||
**Estimation totale** : +5 jours développement avant soumission stores
|
||||
|
||||
## Stratégie de lancement
|
||||
|
||||
**Phase 1 - MVP** :
|
||||
- IAP uniquement (5.99€/mois mondial)
|
||||
- Modération UGC active
|
||||
- GPS avec disclosure
|
||||
- CarPlay/Android Auto basique
|
||||
|
||||
**Phase 2 - Post-validation** :
|
||||
- Prix différenciés US (lien externe 4.99€)
|
||||
- Paiement externe EU (DMA)
|
||||
- Monétisation créateurs (Mangopay)
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Formation équipe sur politiques stores
|
||||
- Suivi des métriques modération (% rejet, SLA)
|
||||
- Migration iOS 26 SDK (Avril 2026)
|
||||
- API Level 35 Android (2026)
|
||||
- Communication transparente GPS/publicités
|
||||
|
||||
## Sources
|
||||
|
||||
- [Android Auto Media Apps](https://developer.android.com/training/cars/media)
|
||||
- [CarPlay Developer Guide](https://developer.apple.com/carplay)
|
||||
- [Google Play UGC Policy](https://support.google.com/googleplay/android-developer/answer/9876937)
|
||||
- [App Store Guidelines](https://developer.apple.com/app-store/review/guidelines/)
|
||||
- [Apple DMA Update EU](https://www.revenuecat.com/blog/growth/apple-eu-dma-update-june-2025/)
|
||||
- [Google Background Location 2026](https://support.google.com/googleplay/android-developer/answer/9799150)
|
||||
50
docs/adr/012-architecture-backend.md
Normal file
50
docs/adr/012-architecture-backend.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# ADR-012 : Architecture Backend
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-20
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite une architecture backend évolutive tout en gardant la simplicité opérationnelle pour un MVP. Le système doit supporter une croissance progressive de 0 à 10M utilisateurs.
|
||||
|
||||
## Décision
|
||||
|
||||
**Monolithe modulaire** avec séparation claire en modules internes.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Architecture | Complexité | Coûts infra | Time to market | Évolutivité |
|
||||
|--------------|------------|-------------|----------------|-------------|
|
||||
| **Monolithe modulaire** | Faible | Faible | Rapide | 0-1M users |
|
||||
| Microservices | Élevée | Élevée | Lent | 1M+ users |
|
||||
| Hybrid (Mono + Workers) | Moyenne | Moyenne | Moyen | 100K-5M users |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Simplicité** : 1 seul binaire Go, déploiement trivial
|
||||
- **Transactions** : Communications inter-modules en mémoire (pas de latence réseau)
|
||||
- **Debugging** : Stack traces complètes, profiling unifié
|
||||
- **Coûts** : 1 serveur suffit pour 100K users (vs N services)
|
||||
- **Refactoring** : Modules internes bien séparés facilitent migration vers microservices si nécessaire
|
||||
|
||||
## Structure modulaire
|
||||
|
||||
```
|
||||
internal/
|
||||
├── auth/ # Validation JWT, intégration Zitadel
|
||||
├── user/ # Profils, centres d'intérêt
|
||||
├── content/ # CRUD contenus, métadonnées
|
||||
├── geo/ # Recherche géospatiale, algorithme
|
||||
├── streaming/ # Génération HLS, transcoding
|
||||
├── moderation/ # Signalements, workflow
|
||||
├── payment/ # Intégration Mangopay
|
||||
└── analytics/ # Métriques écoute, jauges
|
||||
```
|
||||
|
||||
Chaque module suit : `handler.go` → `service.go` → `repository.go`.
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Scaling horizontal : réplication complète du binaire (acceptable jusqu'à 1M users)
|
||||
- Transition vers microservices possible en phase 2 (extraction progressive des modules)
|
||||
- Importance de maintenir découplage fort entre modules (interfaces claires)
|
||||
57
docs/adr/013-orm-acces-donnees.md
Normal file
57
docs/adr/013-orm-acces-donnees.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# ADR-013 : ORM et Accès Données
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-20
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite des requêtes SQL complexes (PostGIS géospatiales) avec performance optimale et type safety. Le choix entre ORM, query builder ou SQL brut impacte maintenabilité et performance.
|
||||
|
||||
## Décision
|
||||
|
||||
**sqlc** pour génération de code Go type-safe depuis SQL.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Solution | Performance | Type Safety | Contrôle SQL | Courbe apprentissage |
|
||||
|----------|-------------|-------------|--------------|----------------------|
|
||||
| **sqlc** | Excellente | Très haute | Total | Faible |
|
||||
| GORM | Moyenne | Moyenne | Limité | Faible |
|
||||
| pgx + SQL brut | Excellente | Faible | Total | Moyenne |
|
||||
| sqlx | Bonne | Faible | Total | Faible |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Performance** : Génération compile-time, zero overhead runtime
|
||||
- **Type safety** : Structs Go générées automatiquement, erreurs détectées à la compilation
|
||||
- **Contrôle SQL** : Requêtes PostGIS complexes écrites en pur SQL (pas de limitations ORM)
|
||||
- **Maintenabilité** : Modifications SQL → `sqlc generate` → code mis à jour
|
||||
- **Simplicité** : Pas de magic, code généré lisible et debuggable
|
||||
|
||||
## Workflow
|
||||
|
||||
```sql
|
||||
-- queries/content.sql
|
||||
-- name: GetContentNearby :many
|
||||
SELECT id, title, ST_Distance(location, $1::geography) as distance
|
||||
FROM contents
|
||||
WHERE ST_DWithin(location, $1::geography, $2)
|
||||
ORDER BY distance
|
||||
LIMIT $3;
|
||||
```
|
||||
|
||||
```bash
|
||||
sqlc generate
|
||||
```
|
||||
|
||||
```go
|
||||
// Code Go type-safe généré automatiquement
|
||||
contents, err := q.GetContentNearby(ctx, location, radius, limit)
|
||||
```
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Dépendance : `github.com/sqlc-dev/sqlc`
|
||||
- Fichier `sqlc.yaml` à la racine pour configuration
|
||||
- Migrations gérées séparément avec `golang-migrate`
|
||||
- CI doit exécuter `sqlc generate` pour valider cohérence SQL/Go
|
||||
74
docs/adr/014-frontend-mobile.md
Normal file
74
docs/adr/014-frontend-mobile.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# ADR-014 : Frontend Mobile
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-20
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite applications iOS et Android avec support CarPlay/Android Auto, lecture audio HLS avancée, géolocalisation temps réel. Le choix du framework impacte vélocité développement et performances.
|
||||
|
||||
## Décision
|
||||
|
||||
**Flutter** pour iOS et Android avec codebase unique.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Framework | Codebase | Performance | Audio/CarPlay | Communauté |
|
||||
|-----------|----------|-------------|---------------|------------|
|
||||
| **Flutter** | Unique | Native | Excellente | Large |
|
||||
| React Native | Unique | Bonne | Modules natifs requis | Très large |
|
||||
| Native (Swift+Kotlin) | Double | Excellente | Native | Large |
|
||||
| Ionic/Capacitor | Unique | Moyenne | Limitée | Moyenne |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Codebase unique** : iOS + Android maintenus ensemble, vélocité développement x2
|
||||
- **Performance** : Dart compilé en code natif (pas de bridge JS)
|
||||
- **Audio HLS** : Package `just_audio` mature avec support HLS, buffering adaptatif
|
||||
- **CarPlay/Android Auto** : Support via packages communautaires (`flutter_carplay`, `android_auto_flutter`)
|
||||
- **Géolocalisation** : `geolocator` robuste avec gestion permissions
|
||||
- **Écosystème** : Widgets riches (Material/Cupertino), state management mature (Bloc, Riverpod)
|
||||
|
||||
## Packages clés
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
flutter_bloc: ^8.1.3 # State management
|
||||
just_audio: ^0.9.36 # Lecture audio HLS
|
||||
geolocator: ^11.0.0 # GPS temps réel (mode voiture)
|
||||
geofence_service: ^5.2.0 # Geofencing arrière-plan (mode piéton)
|
||||
flutter_local_notifications: ^17.0.0 # Notifications géolocalisées
|
||||
dio: ^5.4.0 # HTTP client
|
||||
flutter_secure_storage: ^9.0.0 # Tokens JWT
|
||||
cached_network_image: ^3.3.1 # Cache images
|
||||
```
|
||||
|
||||
**Nouveaux packages (contenus géolocalisés)** :
|
||||
|
||||
- **`geofence_service`** : Détection entrée/sortie rayon 200m en arrière-plan (mode piéton)
|
||||
- Geofencing natif iOS/Android
|
||||
- Minimise consommation batterie
|
||||
- Supporte notifications push même app fermée
|
||||
|
||||
- **`flutter_local_notifications`** : Notifications locales avec compteur dynamique
|
||||
- Notification avec compteur décroissant (7→1) en mode voiture
|
||||
- Icônes personnalisées selon type contenu
|
||||
- Désactivation overlay en mode CarPlay/Android Auto (conformité)
|
||||
|
||||
## Structure application
|
||||
|
||||
```
|
||||
lib/
|
||||
├── core/ # Config, DI, routes
|
||||
├── data/ # Repositories, API clients
|
||||
├── domain/ # Models, business logic
|
||||
├── presentation/ # UI (screens, widgets, blocs)
|
||||
└── main.dart
|
||||
```
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Équipe doit apprendre Dart (syntaxe proche Java/TypeScript)
|
||||
- Taille binaire : 8-15 MB (acceptable)
|
||||
- Tests : `flutter_test` pour widgets, `integration_test` pour E2E
|
||||
- CI/CD : Fastlane pour déploiement stores
|
||||
84
docs/adr/015-strategie-tests.md
Normal file
84
docs/adr/015-strategie-tests.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# ADR-015 : Stratégie Tests
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-20
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite une couverture tests robuste avec documentation vivante des use cases. La stratégie doit équilibrer vélocité développement et qualité.
|
||||
|
||||
## Décision
|
||||
|
||||
Approche **multi-niveaux** : unitaires, intégration, BDD (Gherkin), E2E, load testing.
|
||||
|
||||
## Stratégie par type
|
||||
|
||||
| Type | Framework | Cible | Fréquence |
|
||||
|------|-----------|-------|-----------|
|
||||
| **Unitaires** | Testify | 80%+ couverture | Chaque commit |
|
||||
| **Intégration DB** | Testify + Testcontainers | Repositories critiques | Avant merge PR |
|
||||
| **BDD (Gherkin)** | Godog | User stories | Avant release |
|
||||
| **E2E Mobile** | Flutter integration_test | Parcours critiques | Nightly |
|
||||
| **Load** | k6 | N/A | Avant mise en prod |
|
||||
|
||||
## Tests unitaires (Testify)
|
||||
|
||||
- **Framework** : `github.com/stretchr/testify`
|
||||
- **Couverture minimale** : 80% sur packages `internal/*/service.go`
|
||||
- **Exécution** : Chaque commit (CI rapide ~30s)
|
||||
|
||||
## Tests BDD (Gherkin + Godog)
|
||||
|
||||
- **Framework** : `github.com/cucumber/godog`
|
||||
- **Couverture** : Tous les cas d'usage du [README.md](../../README.md) traduits en `.feature`
|
||||
- **Exécution** : Avant release
|
||||
- **Détails** : Voir [ADR-007](007-tests-bdd.md) pour contexte complet
|
||||
|
||||
## Tests intégration (Testcontainers)
|
||||
|
||||
- **Framework** : `github.com/testcontainers/testcontainers-go`
|
||||
- **Scope** : Repositories avec PostGIS/Redis (requêtes spatiales complexes)
|
||||
- **Exécution** : Avant merge PR
|
||||
- **Justification** : Validation des requêtes PostGIS impossibles à mocker
|
||||
|
||||
## Tests E2E Mobile (Flutter)
|
||||
|
||||
- **Framework** : `integration_test` (Flutter officiel)
|
||||
- **Scope** : Parcours critiques (authentification, lecture audio, géolocalisation)
|
||||
- **Exécution** : Nightly builds
|
||||
- **Justification** : Validation de l'intégration complète mobile + backend
|
||||
|
||||
## Load testing (k6)
|
||||
|
||||
- **Framework** : `grafana/k6`
|
||||
- **Objectif** : API p99 < 100ms à 10K RPS
|
||||
- **Scénarios** : Recommandations géolocalisées, streaming HLS, authentification
|
||||
- **Exécution** : Avant mise en production
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Approche | Avantages | Inconvénients | Décision |
|
||||
|----------|-----------|---------------|----------|
|
||||
| **Tests unitaires uniquement** | Rapides, simples | Pas de validation intégration | ❌ Insuffisant pour PostGIS |
|
||||
| **TDD strict (100% couverture)** | Qualité maximale | Vélocité réduite (-40%) | ❌ Trop coûteux pour MVP |
|
||||
| **BDD sans tests unitaires** | Documentation vivante | Feedback lent (3-5min/run) | ❌ Cycle développement trop lent |
|
||||
| **Stratégie multi-niveaux** | Équilibre qualité/vitesse | Complexité CI/CD | ✅ **Choisi** |
|
||||
|
||||
## Justification
|
||||
|
||||
- **80% unitaires** : Feedback rapide (<30s), détection précoce des régressions
|
||||
- **BDD** : Documentation vivante alignée avec les règles métier
|
||||
- **Testcontainers** : Seule façon fiable de tester PostGIS `ST_DWithin`, `ST_Distance`
|
||||
- **E2E ciblés** : Validation des parcours critiques sans ralentir le développement
|
||||
- **k6** : Détection des régressions de performance avant production
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Dépendances :
|
||||
- `github.com/stretchr/testify`
|
||||
- `github.com/cucumber/godog`
|
||||
- `github.com/testcontainers/testcontainers-go`
|
||||
- `grafana/k6`
|
||||
- Temps CI : ~3-5 min (tests unitaires + BDD)
|
||||
- Tests intégration/E2E : nightly builds (15-30 min)
|
||||
- Load tests : avant chaque release majeure
|
||||
87
docs/adr/016-organisation-monorepo.md
Normal file
87
docs/adr/016-organisation-monorepo.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# ADR-016 : Organisation en Monorepo
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-24
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave comprend plusieurs composants (backend Go, mobile React Native/Flutter, documentation, tests BDD). Il faut décider de l'organisation des repositories : monorepo unique, multirepo avec submodules, ou repos totalement séparés.
|
||||
|
||||
Les tests Gherkin existants décrivent des **fonctionnalités bout-en-bout** (front + mobile + back) et doivent rester cohérents entre tous les composants.
|
||||
|
||||
## Décision
|
||||
|
||||
**Monorepo unique** contenant backend, mobile, documentation et features Gherkin.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Option | Cohérence | Complexité | Onboarding | Déploiements |
|
||||
|--------|-----------|------------|------------|--------------|
|
||||
| **Monorepo** | Excellente | Faible | Simple (1 clone) | Couplés |
|
||||
| Multirepo + submodules | Moyenne | Élevée | Complexe | Indépendants |
|
||||
| Multirepo séparés | Faible | Moyenne | Moyen | Indépendants |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Source de vérité unique** : documentation et Gherkin partagés, pas de désynchronisation
|
||||
- **Versionning cohérent** : un tag Git = release complète front + back compatible
|
||||
- **Tests e2e simplifiés** : tout le code disponible localement pour tests intégrés
|
||||
- **Expérience développeur** : un seul `git clone`, historique unifié, changements cross-stack facilités
|
||||
- **Stade du projet** : début de projet où agilité et changements rapides sont critiques
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
roadwave/
|
||||
├── docs/ # Documentation commune (ADR, règles métier)
|
||||
├── features/ # Tests BDD Gherkin partagés
|
||||
│ ├── authentication/
|
||||
│ ├── navigation/
|
||||
│ └── ...
|
||||
├── backend/ # Application Go
|
||||
│ ├── cmd/
|
||||
│ ├── internal/
|
||||
│ ├── pkg/
|
||||
│ ├── migrations/
|
||||
│ ├── tests/
|
||||
│ │ └── bdd/ # Step definitions Go (API)
|
||||
│ └── go.mod
|
||||
├── mobile/ # Application React Native/Flutter
|
||||
│ ├── src/
|
||||
│ ├── tests/
|
||||
│ │ └── bdd/ # Step definitions mobile (UI)
|
||||
│ └── package.json / pubspec.yaml
|
||||
├── shared/ # Code partagé (types, contrats API)
|
||||
├── docker-compose.yml # Stack complète locale
|
||||
└── Makefile # Commandes communes
|
||||
```
|
||||
|
||||
## Gestion des tests Gherkin
|
||||
|
||||
**Principe** : Les fichiers `.feature` restent **partagés** dans `/features`, mais chaque projet implémente ses **step definitions**.
|
||||
|
||||
```
|
||||
features/authentication/inscription.feature → Scénario fonctionnel
|
||||
|
||||
backend/tests/bdd/inscription_steps.go → Teste l'API
|
||||
mobile/tests/bdd/inscription_steps.dart → Teste l'UI mobile
|
||||
```
|
||||
|
||||
Cela garantit que :
|
||||
- Les spécifications métier sont uniques et cohérentes
|
||||
- Chaque couche teste sa responsabilité
|
||||
- Les tests valident le contrat entre front et back
|
||||
|
||||
## Outillage
|
||||
|
||||
- **Turborepo** ou **Nx** : orchestration des builds/tests, cache intelligent
|
||||
- **Docker Compose** : environnement de dev local (PostgreSQL, Redis, backend, etc.)
|
||||
- **Make** : commandes communes (`make test`, `make build`, `make dev`)
|
||||
- **CI/CD** : GitHub Actions avec path filters (rebuild seulement ce qui change)
|
||||
|
||||
## Conséquences
|
||||
|
||||
- **Positif** : cohérence maximale, onboarding simplifié, changements cross-stack facilités
|
||||
- **Négatif** : repo plus volumineux, nécessite CI intelligente pour éviter builds lents
|
||||
- **Migration future** : si le monorepo devient ingérable (>100k LOC), split possible mais improbable à moyen terme
|
||||
- **Permissions** : tout le monde voit tout (transparence encouragée en phase startup)
|
||||
59
docs/adr/017-hebergement.md
Normal file
59
docs/adr/017-hebergement.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# ADR-017 : Hébergement
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2025-01-25
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite une infrastructure cloud hébergée dans l'UE (conformité RGPD) avec PostgreSQL+PostGIS, Redis et stockage objet. L'objectif est de minimiser les coûts en phase MVP tout en gardant une évolutivité vers des services managés.
|
||||
|
||||
## Décision
|
||||
|
||||
**OVH VPS** avec services self-hosted via Docker Compose pour le MVP.
|
||||
Migration vers **Scaleway** (services managés) prévue en phase de croissance.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Solution | Coût MVP/mois | Simplicité ops | PostGIS | Redis managé | RGPD |
|
||||
|----------|---------------|----------------|---------|--------------|------|
|
||||
| **OVH VPS** | ~14€ | Moyenne | Self-hosted | Self-hosted | Oui |
|
||||
| Scaleway managé | ~67€ | Élevée | Oui | Oui | Oui |
|
||||
| Clever Cloud | ~120€ | Très élevée | Oui | Oui | Oui |
|
||||
| Infomaniak | ~50€ | Élevée | Non | Non | Suisse |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Économie** : 5x moins cher que les services managés (~14€ vs ~67€)
|
||||
- **Suffisant pour MVP** : VPS 4GB RAM supporte facilement 10-20K users
|
||||
- **Docker Compose** : Stack reproductible, backups automatisables
|
||||
- **RGPD** : Datacenters OVH en France
|
||||
- **Migration simple** : Passage vers Scaleway managé sans refonte
|
||||
|
||||
## Stack MVP
|
||||
|
||||
```
|
||||
OVH VPS Essential (12.50€/mois)
|
||||
├── Go + Fiber (app)
|
||||
├── PostgreSQL 16 + PostGIS 3.4 (Docker)
|
||||
├── Redis 7 (Docker)
|
||||
├── NGINX Cache (distribution HLS)
|
||||
└── Backups quotidiens (cron + pg_dump)
|
||||
|
||||
OVH Object Storage (~1.20€/100GB)
|
||||
└── Fichiers audio source
|
||||
```
|
||||
|
||||
## Évolution prévue
|
||||
|
||||
| Phase | Users | Infrastructure | Coût estimé |
|
||||
|-------|-------|----------------|-------------|
|
||||
| MVP | 0-20K | OVH VPS Essential | ~14€/mois |
|
||||
| Croissance | 20-100K | Scaleway + PostgreSQL managé | ~100€/mois |
|
||||
| Scale | 100K+ | Scaleway Kubernetes | ~500€/mois |
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Backups PostgreSQL à automatiser (cron + pg_dump)
|
||||
- Monitoring basique requis (Uptime Kuma ou alertes OVH)
|
||||
- Sécurité serveur à configurer (UFW, Fail2ban)
|
||||
- Migration vers services managés à planifier dès revenus stables
|
||||
62
docs/adr/018-service-emailing.md
Normal file
62
docs/adr/018-service-emailing.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# ADR-018 : Service d'Emailing Transactionnel
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2026-01-26
|
||||
|
||||
## Contexte
|
||||
|
||||
RoadWave nécessite un service d'envoi d'emails transactionnels pour notifications utilisateurs, modération (strikes, suspensions), authentification (Zitadel), conformité RGPD et communications créateurs.
|
||||
|
||||
**Volume estimé MVP** : 2000-5000 emails/mois.
|
||||
|
||||
**Contraintes** : Souveraineté préférée (France/UE), RGPD natif, coût maîtrisé, API simple pour Go, capacité SMS future (post-MVP).
|
||||
|
||||
## Décision
|
||||
|
||||
**Brevo (ex-Sendinblue)** comme service d'emailing transactionnel et SMS.
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Service | Localisation | Prix gratuit | Prix 100K emails/mois | SMS disponible |
|
||||
|---------|--------------|--------------|----------------------|----------------|
|
||||
| **Brevo** | 🇫🇷 France | 300 emails/jour (~9K/mois) | ~49€ | ✅ Oui |
|
||||
| Scaleway TEM | 🇫🇷 France | 300 emails/mois | ~25€ | ❌ Non |
|
||||
| Mailjet | 🇫🇷 France | 200 emails/jour (~6K/mois) | ~50-100€ | ❌ Non |
|
||||
| Postal | 🏠 Self-hosted | Illimité (coût VPS) | ~20-50€ | ❌ Non |
|
||||
|
||||
## Justification
|
||||
|
||||
- **Plan gratuit généreux** : 300 emails/jour (9000/mois) vs 300/mois chez Scaleway → coût 0 en MVP
|
||||
- **SMS natif** : Vérification anti-spam post-MVP (emails temporaires)
|
||||
- **Souverain français** : Hébergé France/UE, RGPD natif
|
||||
- **CRM intégré** : Gestion contacts, segmentation pour newsletters futures
|
||||
- **Simplicité** : API REST standard, SDK disponibles, documentation complète
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Backend Go → Brevo API REST → Utilisateurs
|
||||
├── Emails transactionnels
|
||||
├── Templates HTML
|
||||
├── Webhooks (bounces, opens)
|
||||
└── [Post-MVP] SMS
|
||||
```
|
||||
|
||||
## Estimation coûts
|
||||
|
||||
| Phase | Utilisateurs | Emails/mois | Coût Brevo |
|
||||
|-------|--------------|-------------|------------|
|
||||
| MVP | 0-10K | ~5K/jour | **Gratuit** (< 300/jour) |
|
||||
| Growth | 10K-50K | ~10K/mois | Gratuit ou 19€/mois |
|
||||
| Scale | 50K-100K | ~100K/mois | 49€/mois |
|
||||
|
||||
**SMS** (post-MVP) : ~0.04€/SMS France, soit ~400€/mois pour 10K inscriptions/mois.
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Coût 0 en MVP (9000 emails/mois gratuits)
|
||||
- SMS intégré pour vérification anti-spam (post-MVP)
|
||||
- API REST simple, pas de SDK Go officiel
|
||||
- Limites quotidiennes strictes (300/jour en gratuit)
|
||||
- Migration possible vers Scaleway TEM si volume >100K emails/mois (coût optimisé)
|
||||
- Configuration DNS requise (SPF, DKIM, DMARC)
|
||||
144
docs/adr/019-notifications-geolocalisees.md
Normal file
144
docs/adr/019-notifications-geolocalisees.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# ADR-019 : Architecture des Notifications Géolocalisées
|
||||
|
||||
**Statut** : Accepté
|
||||
**Date** : 2026-01-28
|
||||
**Supersède** : Résout l'incohérence identifiée entre ADR-002 et Règle Métier 05 (Mode Piéton)
|
||||
|
||||
## Contexte
|
||||
|
||||
Le mode piéton exige des notifications push en temps réel lorsque l'utilisateur approche d'un point d'intérêt (rayon de 200m), **même si l'application est fermée ou en arrière-plan**.
|
||||
|
||||
ADR-002 spécifie HLS pour tout le streaming audio, mais HLS est un protocole unidirectionnel (serveur → client) qui ne permet pas au serveur d'envoyer des notifications push vers un client inactif.
|
||||
|
||||
## Décision
|
||||
|
||||
Architecture hybride en **2 phases** :
|
||||
|
||||
### Phase 1 (MVP) : WebSocket + Firebase Cloud Messaging
|
||||
|
||||
```
|
||||
[App Mobile] → [WebSocket] → [Backend Go]
|
||||
↓
|
||||
[PostGIS Worker]
|
||||
↓
|
||||
[Firebase FCM / APNS]
|
||||
↓
|
||||
[Push Notification]
|
||||
```
|
||||
|
||||
**Fonctionnement** :
|
||||
1. L'utilisateur ouvre l'app → connexion WebSocket établie
|
||||
2. L'app envoie sa position GPS toutes les 30s via WebSocket
|
||||
3. Un worker backend (goroutine) interroge PostGIS toutes les 30s :
|
||||
```sql
|
||||
SELECT poi.*, users.fcm_token
|
||||
FROM points_of_interest poi
|
||||
JOIN user_locations users ON ST_DWithin(
|
||||
poi.geom,
|
||||
users.last_position,
|
||||
200 -- rayon en mètres
|
||||
)
|
||||
WHERE users.notifications_enabled = true
|
||||
AND users.last_update > NOW() - INTERVAL '5 minutes'
|
||||
```
|
||||
4. Si proximité détectée → envoi de push notification via Firebase (Android) ou APNS (iOS)
|
||||
5. Utilisateur clique → app s'ouvre → HLS démarre l'audio (ADR-002)
|
||||
|
||||
**Limitations MVP** :
|
||||
- Fonctionne uniquement si l'utilisateur a envoyé sa position < 5 minutes
|
||||
- En voiture rapide (>80 km/h), possible de "manquer" un POI si position pas mise à jour
|
||||
|
||||
### Phase 2 (Post-MVP) : Ajout du Geofencing Local
|
||||
|
||||
```
|
||||
[Mode Connecté] → WebSocket + Push serveur (Phase 1)
|
||||
[Mode Offline] → Geofencing natif iOS/Android
|
||||
[Mode Économie] → Geofencing natif (batterie < 20%)
|
||||
```
|
||||
|
||||
**Fonctionnement additionnel** :
|
||||
1. Quand l'utilisateur télécharge du contenu pour mode offline → synchronisation des POI proches (rayon 10 km)
|
||||
2. Configuration de **geofences locales** sur iOS/Android (limite : 20 sur iOS, 100 sur Android)
|
||||
3. Sélection intelligente des 20 POI les plus pertinents selon les jauges d'intérêt
|
||||
4. Système d'exploitation surveille les geofences même app fermée
|
||||
5. Entrée dans geofence → notification locale (pas de serveur)
|
||||
|
||||
## Alternatives considérées
|
||||
|
||||
| Option | Fonctionne offline | Batterie | Complexité | Limite POI | Précision |
|
||||
|--------|-------------------|----------|------------|------------|-----------|
|
||||
| **WebSocket + FCM (Phase 1)** | ❌ Non | ⭐ Optimale | ⭐ Faible | ∞ | ⭐⭐ Bonne |
|
||||
| Geofencing local seul | ⭐ Oui | ⚠️ Élevée | ⚠️ Moyenne | 20 (iOS) | ⭐⭐⭐ Excellente |
|
||||
| Polling GPS continu | ⭐ Oui | ❌ Critique | ⭐ Faible | ∞ | ⭐⭐⭐ Excellente |
|
||||
| **Hybride (Phase 1+2)** | ⭐ Oui | ⭐ Adaptative | ⚠️ Moyenne | ∞/20 | ⭐⭐⭐ Excellente |
|
||||
|
||||
## Justification
|
||||
|
||||
### Pourquoi WebSocket et pas HTTP long-polling ?
|
||||
|
||||
- **Efficacité** : 1 connexion TCP vs multiples requêtes HTTP
|
||||
- **Batterie** : Connexion persistante optimisée par l'OS mobile
|
||||
- **Bi-directionnel** : Backend peut envoyer des mises à jour instantanées (ex: "nouveau POI créé par un créateur que tu suis")
|
||||
|
||||
### Pourquoi Firebase FCM et pas implémentation custom ?
|
||||
|
||||
- **Gratuit** : 10M notifications/mois (largement suffisant jusqu'à 100K utilisateurs)
|
||||
- **Fiabilité** : Infrastructure Google avec 99.95% uptime
|
||||
- **Batterie** : Utilise les mécanismes système (Google Play Services)
|
||||
- **Cross-platform** : API unifiée iOS/Android
|
||||
|
||||
### Pourquoi limiter le geofencing local à Phase 2 ?
|
||||
|
||||
- **Complexité** : Permissions "Always Location" difficiles à obtenir (taux d'acceptation ~30%)
|
||||
- **ROI** : 80% des utilisateurs auront un réseau mobile disponible
|
||||
- **Priorité** : Livrer le MVP rapidement avec la solution serveur
|
||||
|
||||
## Conséquences
|
||||
|
||||
### Positives
|
||||
|
||||
- ✅ Notifications temps réel en mode piéton (< 1 minute de latence)
|
||||
- ✅ Fonctionne avec HLS pour l'audio (pas de conflit avec ADR-002)
|
||||
- ✅ Scalable : Worker backend peut gérer 10K utilisateurs/seconde avec PostGIS indexé
|
||||
- ✅ Mode offline disponible en Phase 2 sans refonte
|
||||
|
||||
### Négatives
|
||||
|
||||
- ❌ Dépendance à Firebase (vendor lock-in) - mitigée par l'utilisation de l'interface FCM standard
|
||||
- ❌ WebSocket nécessite maintien de connexion (charge serveur +10-20%)
|
||||
- ❌ Mode offline non disponible au MVP (déception possible des early adopters)
|
||||
|
||||
### Impact sur les autres ADR
|
||||
|
||||
- **ADR-002 (Streaming)** : Aucun conflit - HLS reste pour l'audio
|
||||
- **ADR-005 (Base de données)** : Ajouter index PostGIS `GIST (geom)` sur `points_of_interest`
|
||||
- **ADR-012 (Architecture Backend)** : Ajouter un module `geofencing` avec worker dédié
|
||||
- **ADR-014 (Frontend Mobile)** : Intégrer `firebase_messaging` (Flutter) et gérer permissions
|
||||
|
||||
## Métriques de Succès
|
||||
|
||||
- Latence notification < 60s après entrée dans rayon 200m
|
||||
- Taux de livraison > 95% (hors utilisateurs avec notifications désactivées)
|
||||
- Consommation batterie < 5% / heure en mode piéton
|
||||
- Coût serveur < 0.01€ / utilisateur / mois
|
||||
|
||||
## Migration et Rollout
|
||||
|
||||
### Phase 1 (MVP - Sprint 3-4)
|
||||
1. Backend : Implémenter WebSocket endpoint `/ws/location`
|
||||
2. Backend : Worker PostGIS avec requête ST_DWithin
|
||||
3. Mobile : Intégrer Firebase SDK + gestion FCM token
|
||||
4. Test : Validation en conditions réelles (Paris, 10 testeurs)
|
||||
|
||||
### Phase 2 (Post-MVP - Sprint 8-10)
|
||||
1. Mobile : Implémenter geofencing avec `flutter_background_geolocation`
|
||||
2. Backend : API `/sync/nearby-pois?lat=X&lon=Y&radius=10km`
|
||||
3. Mobile : Algorithme de sélection des 20 POI prioritaires
|
||||
4. Test : Validation mode avion (offline complet)
|
||||
|
||||
## Références
|
||||
|
||||
- [Firebase Cloud Messaging Documentation](https://firebase.google.com/docs/cloud-messaging)
|
||||
- [PostGIS ST_DWithin Performance](https://postgis.net/docs/ST_DWithin.html)
|
||||
- [iOS Geofencing Best Practices](https://developer.apple.com/documentation/corelocation/monitoring_the_user_s_proximity_to_geographic_regions)
|
||||
- Règle Métier 05 : Section 5.1.2 (Mode Piéton, lignes 86-120)
|
||||
319
docs/architecture/sequences/cache-geospatial.md
Normal file
319
docs/architecture/sequences/cache-geospatial.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# Diagramme de Séquence : Cache Géospatial Redis
|
||||
|
||||
> Architecture du cache Redis Geospatial pour l'optimisation des requêtes de découverte de contenu géolocalisé.
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le cache Redis Geospatial permet d'accélérer la recherche de contenus audio à proximité d'une position GPS en évitant des calculs PostGIS coûteux sur PostgreSQL à chaque requête.
|
||||
|
||||
**Performance** :
|
||||
- Sans cache : ~200-500ms (calcul PostGIS sur 100K points)
|
||||
- Avec cache : ~5-10ms (filtrage Redis en mémoire)
|
||||
|
||||
---
|
||||
|
||||
## Flux complet : Cold Start → Warm Cache
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 📱 Utilisateur<br/>(Paris)
|
||||
participant Backend as 🔧 Backend Go
|
||||
participant Redis as 🔴 Redis Geospatial<br/>(Cache)
|
||||
participant PostgreSQL as 🗄️ PostgreSQL<br/>+ PostGIS
|
||||
participant CDN as 🌐 NGINX Cache (OVH VPS)
|
||||
|
||||
Note over User,CDN: 🥶 Cold Start - Cache vide
|
||||
|
||||
User->>Backend: GET /contents?lat=48.8566&lon=2.3522&radius=50km
|
||||
Backend->>Redis: EXISTS geo:catalog
|
||||
Redis-->>Backend: false (cache vide)
|
||||
|
||||
Backend->>PostgreSQL: SELECT id, lat, lon, title, geo_level<br/>FROM contents WHERE active=true
|
||||
Note over PostgreSQL: Tous les contenus actifs<br/>de la plateforme (métadonnées)
|
||||
PostgreSQL-->>Backend: 100K contenus (métadonnées)
|
||||
|
||||
Backend->>Redis: GEOADD geo:catalog<br/>lon1 lat1 "content:1"<br/>lon2 lat2 "content:2"<br/>... (100K entrées)
|
||||
Note over Redis: Stockage index spatial<br/>en mémoire (~20 MB)
|
||||
Redis-->>Backend: OK (100000)
|
||||
|
||||
Backend->>Redis: EXPIRE geo:catalog 300
|
||||
Note over Redis: TTL 5 minutes
|
||||
|
||||
Backend->>Redis: GEORADIUS geo:catalog<br/>2.3522 48.8566 50 km
|
||||
Note over Redis: Filtrage spatial instantané<br/>(index geohash)
|
||||
Redis-->>Backend: [content:123, content:456, ...]<br/>(~500 IDs dans rayon)
|
||||
|
||||
Backend->>PostgreSQL: SELECT * FROM contents<br/>WHERE id IN (123, 456, ...)<br/>AND geo_level = 'gps_precise'
|
||||
Note over PostgreSQL: Récupération détails complets<br/>uniquement contenus proches
|
||||
PostgreSQL-->>Backend: Détails complets (500 contenus GPS)
|
||||
|
||||
Backend->>PostgreSQL: SELECT * FROM contents<br/>WHERE city='Paris' OR dept='75'<br/>OR region='IDF' OR geo_level='national'
|
||||
Note over PostgreSQL: Contenus par niveau géographique
|
||||
PostgreSQL-->>Backend: Contenus ville/région/national
|
||||
|
||||
Backend->>Backend: Scoring & mixage :<br/>- GPS proche : 70%<br/>- Ville : 20%<br/>- Région : 8%<br/>- National : 2%
|
||||
|
||||
Backend-->>User: JSON: [{id, title, creator, audioUrl, score}, ...]<br/>(playlist mixée et scorée)
|
||||
|
||||
Note over User,CDN: 🎵 Lecture audio (requêtes séparées)
|
||||
|
||||
User->>CDN: GET /audio/content-123.m3u8
|
||||
CDN-->>User: Playlist HLS
|
||||
|
||||
User->>CDN: GET /audio/content-123-segment-001.ts
|
||||
CDN-->>User: Segment audio Opus
|
||||
|
||||
Note over User,CDN: 🔥 Warm Cache - Utilisateur 2 à Lyon (45km+)
|
||||
|
||||
participant User2 as 📱 Utilisateur 2<br/>(Lyon)
|
||||
|
||||
User2->>Backend: GET /contents?lat=45.7640&lon=4.8357&radius=50km
|
||||
Backend->>Redis: EXISTS geo:catalog
|
||||
Redis-->>Backend: true ✅ (cache chaud)
|
||||
|
||||
Backend->>Redis: GEORADIUS geo:catalog<br/>4.8357 45.7640 50 km
|
||||
Note over Redis: Filtrage instantané<br/>sur cache existant
|
||||
Redis-->>Backend: [content:789, content:012, ...]<br/>(~300 IDs différents)
|
||||
|
||||
Backend->>PostgreSQL: SELECT * FROM contents<br/>WHERE id IN (789, 012, ...)<br/>AND geo_level = 'gps_precise'
|
||||
PostgreSQL-->>Backend: Détails complets
|
||||
|
||||
Backend->>PostgreSQL: SELECT * FROM contents<br/>WHERE city='Lyon' OR dept='69'<br/>OR region='Auvergne-RA' OR geo_level='national'
|
||||
PostgreSQL-->>Backend: Contenus ville/région/national
|
||||
|
||||
Backend->>Backend: Scoring & mixage
|
||||
|
||||
Backend-->>User2: JSON: [{id, title, creator, audioUrl, score}, ...]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Stratégie de cache
|
||||
|
||||
### Cache du catalogue complet (approche choisie)
|
||||
|
||||
**Principe** : Au premier cache miss, charger **TOUS** les contenus géolocalisés en une seule fois dans Redis.
|
||||
|
||||
**Avantages** :
|
||||
- ✅ 1 seul cache miss au démarrage de l'instance
|
||||
- ✅ Toutes les requêtes suivantes servies par Redis (n'importe quelle position GPS)
|
||||
- ✅ Simple à gérer (1 seule clé Redis : `geo:catalog`)
|
||||
- ✅ Pas de duplication de données
|
||||
|
||||
**Inconvénients** :
|
||||
- ⚠️ Premier utilisateur subit le cold start (~500ms-1s)
|
||||
- ⚠️ Nécessite charger toute la base (acceptable : ~20 MB pour 100K contenus)
|
||||
|
||||
**Alternatives non retenues** :
|
||||
- Cache par zone géographique → cache miss fréquents, complexité gestion chevauchements
|
||||
- Cache à la demande → trop de cache miss, pas d'optimisation réelle
|
||||
|
||||
---
|
||||
|
||||
## Détails techniques
|
||||
|
||||
### 1. Données stockées dans Redis
|
||||
|
||||
**Clé Redis** : `geo:catalog`
|
||||
|
||||
**Structure** :
|
||||
```
|
||||
GEOADD geo:catalog
|
||||
2.3522 48.8566 "content:12345" # lon, lat, member
|
||||
4.8357 45.7640 "content:67890"
|
||||
...
|
||||
```
|
||||
|
||||
**Taille mémoire** :
|
||||
- ~200 bytes par entrée (ID + coordonnées + index geohash)
|
||||
- 100K contenus = ~20 MB
|
||||
- Négligeable pour Redis (plusieurs GB RAM disponibles)
|
||||
|
||||
**TTL** : 5 minutes (300 secondes)
|
||||
- Le contenu géolocalisé est quasi-statique (change peu)
|
||||
- Rechargement automatique toutes les 5 minutes si cache expiré
|
||||
- Permet de propager les nouveaux contenus rapidement
|
||||
|
||||
### 2. Niveaux géographiques
|
||||
|
||||
Le cache Redis ne contient que les contenus avec **GPS précis** (`geo_level = 'gps_precise'`).
|
||||
|
||||
Les autres niveaux géographiques sont gérés par filtrage applicatif :
|
||||
|
||||
| Niveau | Stockage | Requête |
|
||||
|--------|----------|---------|
|
||||
| **GPS précis** | Redis + PostgreSQL | `GEORADIUS` puis `SELECT WHERE id IN (...)` |
|
||||
| **Ville** | PostgreSQL uniquement | `SELECT WHERE city = ?` |
|
||||
| **Département** | PostgreSQL uniquement | `SELECT WHERE department = ?` |
|
||||
| **Région** | PostgreSQL uniquement | `SELECT WHERE region = ?` |
|
||||
| **National** | PostgreSQL uniquement | `SELECT WHERE geo_level = 'national'` |
|
||||
|
||||
### 3. Commandes Redis utilisées
|
||||
|
||||
```bash
|
||||
# Vérifier existence du cache
|
||||
EXISTS geo:catalog
|
||||
# Retour : 0 (n'existe pas) ou 1 (existe)
|
||||
|
||||
# Charger le catalogue complet (cold start)
|
||||
GEOADD geo:catalog 2.3522 48.8566 "content:1" 4.8357 45.7640 "content:2" ...
|
||||
# Retour : nombre d'éléments ajoutés
|
||||
|
||||
# Définir TTL 5 minutes
|
||||
EXPIRE geo:catalog 300
|
||||
|
||||
# Rechercher contenus dans un rayon
|
||||
GEORADIUS geo:catalog 2.3522 48.8566 50 km
|
||||
# Retour : ["content:123", "content:456", ...]
|
||||
|
||||
# Optionnel : obtenir distance et coordonnées
|
||||
GEORADIUS geo:catalog 2.3522 48.8566 50 km WITHDIST WITHCOORD
|
||||
# Retour : [["content:123", "12.5", ["2.35", "48.85"]], ...]
|
||||
```
|
||||
|
||||
### 4. Algorithme de scoring
|
||||
|
||||
Le backend mixe les résultats selon une pondération :
|
||||
|
||||
```
|
||||
Score final =
|
||||
(Pertinence GPS × 0.70) +
|
||||
(Pertinence Ville × 0.20) +
|
||||
(Pertinence Région × 0.08) +
|
||||
(Pertinence National × 0.02)
|
||||
```
|
||||
|
||||
**Critères de pertinence** :
|
||||
- **GPS** : Plus proche = score élevé (distance inversée)
|
||||
- **Ville/Région** : Matching exact = score maximal
|
||||
- **National** : Score fixe faible (contenu générique)
|
||||
|
||||
**Mixage playlist** :
|
||||
1. Trier tous les contenus par score décroissant
|
||||
2. Appliquer diversité créateurs (pas 3 contenus du même créateur d'affilée)
|
||||
3. Injecter contenus sponsorisés/mis en avant (futurs)
|
||||
4. Retourner top 50 pour la session d'écoute
|
||||
|
||||
---
|
||||
|
||||
## Métriques de performance
|
||||
|
||||
### Temps de réponse typiques
|
||||
|
||||
| Scénario | Latence | Détail |
|
||||
|----------|---------|--------|
|
||||
| **Cold start** | 500-1000ms | Chargement 100K contenus dans Redis + requête |
|
||||
| **Warm cache** | 5-10ms | `GEORADIUS` + `SELECT WHERE id IN (...)` |
|
||||
| **TTL expiré** | 500-1000ms | Rechargement automatique |
|
||||
|
||||
### Charge serveurs
|
||||
|
||||
| Composant | Sans cache | Avec cache |
|
||||
|-----------|------------|------------|
|
||||
| **PostgreSQL CPU** | 60-80% | 10-20% |
|
||||
| **Redis CPU** | N/A | 5-15% |
|
||||
| **Throughput** | ~50 req/s | ~500 req/s |
|
||||
|
||||
---
|
||||
|
||||
## Cas limites et optimisations futures
|
||||
|
||||
### Cas limite 1 : Contenu très dense (Paris intra-muros)
|
||||
|
||||
**Problème** : 10K contenus dans rayon 5km → trop de résultats
|
||||
|
||||
**Solution actuelle** :
|
||||
- Limiter résultats Redis à 1000 premiers (tri par distance)
|
||||
- Scorer et filtrer côté application
|
||||
|
||||
**Optimisation future** :
|
||||
- Ajuster rayon dynamiquement selon densité
|
||||
- Utiliser `GEORADIUS ... COUNT 500` pour limiter côté Redis
|
||||
|
||||
### Cas limite 2 : Zones rurales (peu de contenu)
|
||||
|
||||
**Problème** : Rayon 50km retourne <10 contenus
|
||||
|
||||
**Solution actuelle** :
|
||||
- Augmenter poids contenus région/national dans le scoring
|
||||
- Suggérer contenus nationaux populaires
|
||||
|
||||
**Optimisation future** :
|
||||
- Augmenter rayon automatiquement jusqu'à obtenir min 20 contenus
|
||||
- `GEORADIUS ... 100 km` si rayon initial insuffisant
|
||||
|
||||
### Cas limite 3 : Nombreux créateurs actifs (évolutivité)
|
||||
|
||||
**Problème** : Cache 100K → 1M contenus (200 MB Redis)
|
||||
|
||||
**Solution actuelle** :
|
||||
- 200 MB reste acceptable pour Redis
|
||||
|
||||
**Optimisation future** :
|
||||
- Sharding géographique : cache Europe, cache USA, etc.
|
||||
- Limiter cache aux contenus actifs 90 derniers jours
|
||||
|
||||
---
|
||||
|
||||
## Invalidation du cache
|
||||
|
||||
### Stratégies d'invalidation
|
||||
|
||||
| Événement | Action cache | Détail |
|
||||
|-----------|--------------|--------|
|
||||
| **Nouveau contenu publié** | Lazy (TTL) | Visible sous 5 minutes max |
|
||||
| **Contenu supprimé/modéré** | Lazy (TTL) | Disparaît sous 5 minutes max |
|
||||
| **Mise à jour GPS contenu** | Lazy (TTL) | Nouvelle position sous 5 minutes |
|
||||
| **Déploiement backend** | Flush volontaire | `DEL geo:catalog` si schema change |
|
||||
|
||||
**Pas d'invalidation immédiate** pour simplifier l'architecture (cohérence éventuelle acceptable).
|
||||
|
||||
**Alternative future** :
|
||||
- Pub/Sub Redis : notifier toutes les instances backend lors d'un changement
|
||||
- `GEOADD geo:catalog 2.35 48.85 "content:new"` pour ajout immédiat
|
||||
|
||||
---
|
||||
|
||||
## Schéma simplifié
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Utilisateur │
|
||||
│ (Position GPS actuelle) │
|
||||
└────────────────────────┬────────────────────────────────┘
|
||||
│
|
||||
│ GET /contents?lat=X&lon=Y&radius=Z
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Backend Go (Fiber) │
|
||||
│ │
|
||||
│ 1. Vérifier cache Redis │
|
||||
│ ├─ Cache HIT → GEORADIUS rapide │
|
||||
│ └─ Cache MISS → Charger catalogue complet │
|
||||
│ │
|
||||
│ 2. Filtrer contenus GPS proches (Redis) │
|
||||
│ 3. Récupérer contenus ville/région/national (PG) │
|
||||
│ 4. Scorer et mixer selon pondération │
|
||||
│ 5. Retourner playlist │
|
||||
└────────────┬───────────────────────────┬────────────────┘
|
||||
│ │
|
||||
│ GEORADIUS │ SELECT détails
|
||||
▼ ▼
|
||||
┌─────────────────────┐ ┌───────────────────────────┐
|
||||
│ Redis Geospatial │ │ PostgreSQL + PostGIS │
|
||||
│ (Index spatial) │ │ (Données complètes) │
|
||||
│ │ │ │
|
||||
│ • geo:catalog │ │ • contents (détails) │
|
||||
│ • TTL 5 min │ │ • users │
|
||||
│ • ~20 MB mémoire │ │ • playlists │
|
||||
└─────────────────────┘ └───────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Références
|
||||
|
||||
- [ADR-005 : Base de données](../../adr/005-base-de-donnees.md)
|
||||
- [Redis Geospatial Commands](https://redis.io/docs/data-types/geospatial/)
|
||||
- [PostGIS Documentation](https://postgis.net/documentation/)
|
||||
- [Règles métier : Découverte de contenu géolocalisé](../../regles-metier/03-decouverte-contenu.md)
|
||||
1
docs/index.md
Symbolic link
1
docs/index.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../README.md
|
||||
116
docs/legal/README.md
Normal file
116
docs/legal/README.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Documents juridiques RoadWave
|
||||
|
||||
> **IMPORTANT** : Ces documents sont des **structures types** basées sur les règles métier validées. Ils doivent être **obligatoirement relus et validés par un avocat** avant publication.
|
||||
|
||||
---
|
||||
|
||||
## Documents requis
|
||||
|
||||
| Document | Fichier | Obligation légale | Révision avocat |
|
||||
|----------|---------|-------------------|-----------------|
|
||||
| CGU - Conditions Générales d'Utilisation | [cgu.md](cgu.md) | DSA + Code conso | Requis |
|
||||
| Politique de confidentialité | [politique-confidentialite.md](politique-confidentialite.md) | RGPD | Requis |
|
||||
| Charte créateur | [charte-createur.md](charte-createur.md) | DSA (transparence) | Requis |
|
||||
| CGV Premium | [cgv-premium.md](cgv-premium.md) | Code conso | Requis |
|
||||
| CGV Publicités | [cgv-publicites.md](cgv-publicites.md) | Code conso | Requis |
|
||||
| Mentions légales | [mentions-legales.md](mentions-legales.md) | Obligatoire | Requis |
|
||||
|
||||
---
|
||||
|
||||
## Moments de présentation
|
||||
|
||||
### À l'inscription (consentement explicite requis)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Bienvenue sur RoadWave │
|
||||
├─────────────────────────────────────────┤
|
||||
│ Email : [________] │
|
||||
│ Mot de passe : [________] │
|
||||
│ │
|
||||
│ ☐ J'accepte les CGU et la Politique │
|
||||
│ de confidentialité │
|
||||
│ [Lire les CGU] [Lire la Politique] │
|
||||
│ │
|
||||
│ [Créer mon compte] │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Checkbox obligatoire** : bloque création compte si non cochée
|
||||
- **Liens cliquables** : CGU + Politique (ouverture modal ou nouvelle page)
|
||||
- **Horodatage** : enregistrer date/heure acceptation (preuve RGPD)
|
||||
|
||||
### Avant première publication (créateur)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Devenir créateur RoadWave │
|
||||
├─────────────────────────────────────────┤
|
||||
│ En publiant du contenu, vous acceptez │
|
||||
│ la Charte Créateur. │
|
||||
│ │
|
||||
│ Points clés : │
|
||||
│ • Vos 3 premiers contenus seront │
|
||||
│ validés manuellement (24-48h) │
|
||||
│ • Contenus prohibés : haine, violence, │
|
||||
│ musique protégée, spam │
|
||||
│ • Système de strikes : 4 strikes = ban │
|
||||
│ │
|
||||
│ ☐ J'ai lu et j'accepte la Charte │
|
||||
│ Créateur │
|
||||
│ [Lire la Charte complète] │
|
||||
│ │
|
||||
│ [Publier mon premier contenu] │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Avant souscription Premium
|
||||
|
||||
- Affichage CGV Premium
|
||||
- Checkbox acceptation obligatoire
|
||||
- Bouton "Souscrire" désactivé tant que non accepté
|
||||
|
||||
### Avant création campagne publicitaire
|
||||
|
||||
- Affichage CGV Publicités
|
||||
- Checkbox acceptation obligatoire
|
||||
- Validation manuelle obligatoire (24-48h)
|
||||
|
||||
---
|
||||
|
||||
## Accessibilité permanente
|
||||
|
||||
### Footer application/site web
|
||||
|
||||
```
|
||||
RoadWave © 2026
|
||||
[CGU] | [Politique de confidentialité] | [Mentions légales] | [Charte créateur] | [Contact DPO]
|
||||
```
|
||||
|
||||
### Menu utilisateur
|
||||
|
||||
```
|
||||
Mon compte
|
||||
├── Paramètres
|
||||
├── Données personnelles
|
||||
├── Documents légaux
|
||||
│ ├── CGU
|
||||
│ ├── Politique de confidentialité
|
||||
│ ├── Charte créateur (si créateur)
|
||||
│ └── CGV Premium (si abonné)
|
||||
└── Déconnexion
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rappel important
|
||||
|
||||
Ces documents doivent être **obligatoirement relus et validés par un avocat spécialisé** (droit numérique, RGPD) avant publication.
|
||||
|
||||
**Coût avocat estimé** : 1500-3000€ (révision + adaptation contexte)
|
||||
|
||||
**Prochaines étapes** :
|
||||
1. Révision avocat
|
||||
2. Ajout informations manquantes (SIRET, adresse, etc.)
|
||||
3. Intégration technique (pages web, checkboxes, horodatage)
|
||||
4. Tests parcours utilisateur (inscription, première publication, souscription Premium)
|
||||
513
docs/legal/cgu.md
Normal file
513
docs/legal/cgu.md
Normal file
@@ -0,0 +1,513 @@
|
||||
# CGU - Conditions Générales d'Utilisation
|
||||
|
||||
**Version** : 1.0
|
||||
**Date d'effet** : [DATE DE LANCEMENT MVP]
|
||||
**Dernière mise à jour** : [DATE]
|
||||
|
||||
---
|
||||
|
||||
## Article 1 - Définitions
|
||||
|
||||
- **Plateforme** : application mobile et site web RoadWave
|
||||
- **Utilisateur** : toute personne utilisant la Plateforme (auditeur ou créateur)
|
||||
- **Créateur** : utilisateur publiant du contenu audio
|
||||
- **Auditeur** : utilisateur écoutant du contenu audio
|
||||
- **Contenu** : tout fichier audio publié sur la Plateforme
|
||||
- **Compte** : espace personnel de l'utilisateur
|
||||
- **Strike** : sanction appliquée en cas de violation des CGU
|
||||
|
||||
---
|
||||
|
||||
## Article 2 - Objet
|
||||
|
||||
Les présentes Conditions Générales d'Utilisation (CGU) régissent l'utilisation de la plateforme RoadWave, réseau social audio géolocalisé.
|
||||
|
||||
RoadWave permet aux utilisateurs :
|
||||
- **Auditeurs** : écouter du contenu audio contextuel géolocalisé
|
||||
- **Créateurs** : publier du contenu audio géolocalisé
|
||||
|
||||
---
|
||||
|
||||
## Article 3 - Acceptation des CGU
|
||||
|
||||
### 3.1 Consentement
|
||||
|
||||
L'utilisation de la Plateforme implique l'acceptation pleine et entière des présentes CGU.
|
||||
|
||||
### 3.2 Modification des CGU
|
||||
|
||||
RoadWave se réserve le droit de modifier les CGU à tout moment.
|
||||
|
||||
**Notification** :
|
||||
- Email à tous les utilisateurs **14 jours avant** l'entrée en vigueur
|
||||
- Notification in-app au lancement suivant la modification
|
||||
- Version mise à jour disponible sur roadwave.fr/cgu
|
||||
|
||||
**Refus des nouvelles CGU** :
|
||||
- Utilisateur peut supprimer son compte dans les 14 jours
|
||||
- Utilisation continue = acceptation tacite
|
||||
|
||||
---
|
||||
|
||||
## Article 4 - Inscription et compte utilisateur
|
||||
|
||||
### 4.1 Conditions d'inscription
|
||||
|
||||
- **Âge minimum** : 13 ans (conformément au RGPD)
|
||||
- **Mineurs 13-15 ans** : autorisation parentale requise
|
||||
- **Email valide** : requis pour récupération compte
|
||||
- **Mot de passe sécurisé** : minimum 8 caractères, lettres + chiffres
|
||||
|
||||
### 4.2 Vérification email
|
||||
|
||||
- **Auditeurs** : optionnelle, mais limite 5 contenus écoutés avant vérification
|
||||
- **Créateurs** : **obligatoire** pour publier du contenu
|
||||
- Lien de vérification expire après 7 jours
|
||||
|
||||
### 4.3 Responsabilité du compte
|
||||
|
||||
L'utilisateur est responsable de :
|
||||
- La confidentialité de ses identifiants
|
||||
- Toutes actions effectuées depuis son compte
|
||||
- Signalement immédiat en cas d'accès non autorisé
|
||||
|
||||
### 4.4 Un compte par personne
|
||||
|
||||
Création de comptes multiples interdite, sauf :
|
||||
- Compte test (développeurs autorisés)
|
||||
- Demande explicite auprès de RoadWave
|
||||
|
||||
---
|
||||
|
||||
## Article 5 - Utilisation de la Plateforme
|
||||
|
||||
### 5.1 Licence d'utilisation
|
||||
|
||||
RoadWave accorde une licence **non exclusive, révocable, non transférable** pour :
|
||||
- Accéder à la Plateforme
|
||||
- Écouter du contenu
|
||||
- Publier du contenu (créateurs)
|
||||
|
||||
### 5.2 Interdictions générales
|
||||
|
||||
Il est interdit de :
|
||||
- Utiliser la Plateforme à des fins illégales
|
||||
- Tenter de contourner les mesures de sécurité
|
||||
- Utiliser des bots, scripts ou outils automatisés
|
||||
- Revendre l'accès à la Plateforme
|
||||
- Extraire massivement des données (scraping)
|
||||
- Usurper l'identité d'un tiers
|
||||
|
||||
### 5.3 Géolocalisation
|
||||
|
||||
- **Consentement requis** : activation GPS optionnelle
|
||||
- **Mode dégradé** : utilisation possible sans géolocalisation précise (contenus nationaux uniquement)
|
||||
- **Révocation** : désactivation GPS possible à tout moment
|
||||
|
||||
---
|
||||
|
||||
## Article 6 - Publication de contenu (créateurs)
|
||||
|
||||
### 6.1 Règles générales
|
||||
|
||||
Voir **[Charte Créateur](charte-createur.md)** pour règles détaillées.
|
||||
|
||||
**Résumé** :
|
||||
- Contenus originaux ou droits acquis
|
||||
- Pas de contenu haineux, violent, illégal
|
||||
- Pas de musique protégée >30 secondes sans licence
|
||||
- Métadonnées obligatoires : titre, zone diffusion, tags, classification âge
|
||||
|
||||
### 6.2 Validation des 3 premiers contenus
|
||||
|
||||
- Nouveaux créateurs : validation manuelle sous **24-48h** (jours ouvrés)
|
||||
- Après 3 validations : statut "Vérifié" → publication immédiate
|
||||
|
||||
### 6.3 Propriété intellectuelle
|
||||
|
||||
- **Créateur conserve** tous les droits sur ses contenus
|
||||
- **Licence accordée à RoadWave** : diffusion, hébergement, transcoding (licence non exclusive)
|
||||
- **Suppression** : créateur peut supprimer à tout moment (irréversible)
|
||||
|
||||
### 6.4 Responsabilité du créateur
|
||||
|
||||
Le créateur garantit :
|
||||
- Détenir tous les droits nécessaires
|
||||
- Ne pas violer de droits tiers (musique, marques, image)
|
||||
- Respecter les classifications d'âge
|
||||
- Exactitude des métadonnées (zone géographique, tags)
|
||||
|
||||
---
|
||||
|
||||
## Article 7 - Contenus prohibés
|
||||
|
||||
### 7.1 Liste exhaustive
|
||||
|
||||
Sont strictement interdits :
|
||||
|
||||
#### **Haine et violence** (Article 7.1.1)
|
||||
- Incitation à la haine raciale, ethnique, religieuse
|
||||
- Discrimination sexiste, homophobe, transphobe
|
||||
- Apologie de crimes contre l'humanité
|
||||
- Menaces de violence physique
|
||||
|
||||
#### **Contenu sexuel** (Article 7.1.2)
|
||||
- Pornographie ou contenu sexuellement explicite
|
||||
- Contenu impliquant des mineurs (strictement interdit)
|
||||
- Sollicitation sexuelle
|
||||
|
||||
#### **Illégalité** (Article 7.1.3)
|
||||
- Apologie du terrorisme
|
||||
- Incitation aux actes criminels
|
||||
- Vente de produits illégaux (drogues, armes)
|
||||
- Pédopornographie → **ban immédiat + signalement autorités**
|
||||
|
||||
#### **Droits d'auteur** (Article 7.1.4)
|
||||
- Musique protégée en intégrale ou extraits >30 secondes
|
||||
- Films, séries, livres audio protégés
|
||||
- Diffusion de concerts, événements sportifs payants (lives)
|
||||
- **Exception** : extraits ≤30s pour critique/analyse (fair use)
|
||||
|
||||
#### **Désinformation dangereuse** (Article 7.1.5)
|
||||
- Fausses informations sur la santé (COVID, vaccins, traitements)
|
||||
- Fausses informations sur la sécurité routière
|
||||
- Manipulation électorale
|
||||
|
||||
#### **Harcèlement** (Article 7.1.6)
|
||||
- Menaces répétées envers une personne
|
||||
- Doxxing (publication informations privées)
|
||||
- Intimidation, chantage
|
||||
|
||||
#### **Fraude** (Article 7.1.7)
|
||||
- Arnaques, escroqueries
|
||||
- Pyramides de Ponzi, MLM illégaux
|
||||
- Phishing, vol d'identité
|
||||
|
||||
#### **Spam** (Article 7.1.8)
|
||||
- Publicité non autorisée (hors système pub RoadWave)
|
||||
- Répétition de contenus identiques
|
||||
- Liens vers sites externes de spam
|
||||
|
||||
### 7.2 Modération
|
||||
|
||||
**Délais de traitement** :
|
||||
- CRITIQUE (violence, suicide) : <2h (24/7)
|
||||
- HAUTE (haine, harcèlement) : <24h
|
||||
- MOYENNE (spam) : <48h
|
||||
- BASSE (qualité audio) : <72h
|
||||
|
||||
---
|
||||
|
||||
## Article 8 - Système de strikes et sanctions
|
||||
|
||||
### 8.1 Grille de sanctions
|
||||
|
||||
| Strike | Sanction | Durée suspension upload |
|
||||
|--------|----------|-------------------------|
|
||||
| **Strike 1** | Avertissement + suppression contenu | 3 jours |
|
||||
| **Strike 2** | Suppression contenu | 7 jours |
|
||||
| **Strike 3** | Suppression contenu | 30 jours |
|
||||
| **Strike 4** | **Ban définitif** compte créateur | Permanent |
|
||||
|
||||
### 8.2 Exceptions
|
||||
|
||||
**Tolérance 1ère fois (droits d'auteur uniquement)** :
|
||||
- Avertissement sans strike si première violation musique protégée
|
||||
- Explication pédagogique + lien vers règles
|
||||
|
||||
**Violations graves (pas de tolérance)** :
|
||||
- Haine, violence → Strike 1 immédiat
|
||||
- Contenu illégal (terrorisme, pédopornographie) → Strike 4 (ban) + signalement autorités
|
||||
|
||||
### 8.3 Strikes immédiats (radio live)
|
||||
|
||||
| Violation | Sanction |
|
||||
|-----------|----------|
|
||||
| Concert/événement sportif payant | Strike 2 immédiat (7 jours) |
|
||||
| Contenu violent en direct | Strike 3 immédiat (30 jours) |
|
||||
| Contenu illégal en direct | Strike 4 (ban) + signalement |
|
||||
|
||||
### 8.4 Réhabilitation
|
||||
|
||||
- **-1 strike automatique** tous les **6 mois** sans nouvelle violation
|
||||
- Minimum : 0 strikes (pas de valeur négative)
|
||||
- Avertissement (sans strike) ne compte pas pour réhabilitation
|
||||
|
||||
**Exemple** :
|
||||
```
|
||||
Créateur a Strike 2
|
||||
→ 6 mois sans incident → Strike 1
|
||||
→ 6 mois sans incident → Compte propre (0 strike)
|
||||
```
|
||||
|
||||
### 8.5 Notification des sanctions
|
||||
|
||||
**Multi-canal** (email + push + in-app) :
|
||||
- Catégorie violée (référence article CGU)
|
||||
- Raison détaillée (langage clair)
|
||||
- Extrait audio concerné (timestamp)
|
||||
- Transcription du passage problématique
|
||||
- Strike actuel (ex: "Strike 2/4")
|
||||
- Lien vers processus d'appel
|
||||
|
||||
---
|
||||
|
||||
## Article 9 - Processus d'appel
|
||||
|
||||
### 9.1 Droit de contestation
|
||||
|
||||
Tout utilisateur sanctionné peut contester la décision.
|
||||
|
||||
### 9.2 Délai
|
||||
|
||||
- **7 jours** après notification de sanction
|
||||
- Après 7 jours : appel automatiquement refusé
|
||||
|
||||
### 9.3 Procédure
|
||||
|
||||
1. Bouton "Contester cette décision" dans notification
|
||||
2. Formulaire d'appel (raison + arguments + preuves optionnelles)
|
||||
3. Numéro de ticket unique (ex: `#MOD-2026-00142`)
|
||||
4. Email confirmation : "Votre appel sera traité sous 72h"
|
||||
|
||||
### 9.4 Traitement
|
||||
|
||||
- Délai : **72h maximum** (3 jours ouvrés)
|
||||
- Examen par modérateur senior
|
||||
- Décision finale (maintien / annulation / réduction sanction)
|
||||
|
||||
### 9.5 Décision définitive
|
||||
|
||||
- Une seule contestation par sanction
|
||||
- Décision d'appel = définitive (pas de second appel)
|
||||
|
||||
---
|
||||
|
||||
## Article 10 - Publicités
|
||||
|
||||
### 10.1 Publicités RoadWave (auditeurs gratuits)
|
||||
|
||||
- Fréquence : **1 publicité / 5 contenus**
|
||||
- Durée : 10-60 secondes
|
||||
- Skippable après **5 secondes**
|
||||
- Pas de publicités pour utilisateurs Premium
|
||||
|
||||
### 10.2 Publicités créateurs (annonceurs)
|
||||
|
||||
Voir **[CGV Publicités](cgv-publicites.md)**
|
||||
|
||||
**Résumé** :
|
||||
- Interface self-service
|
||||
- Budget minimum : 50€
|
||||
- Validation manuelle obligatoire (24-48h)
|
||||
- Facturation : écoute complète 0.05€, skip après 5s = 0.02€
|
||||
|
||||
---
|
||||
|
||||
## Article 11 - Abonnement Premium
|
||||
|
||||
Voir **[CGV Premium](cgv-premium.md)**
|
||||
|
||||
**Résumé** :
|
||||
- Prix : 4.99€/mois OU 49.99€/an
|
||||
- Avantages : 0 pub, contenus exclusifs 👑, qualité 64 kbps, offline illimité
|
||||
- Résiliation : à tout moment (effet fin période en cours)
|
||||
|
||||
---
|
||||
|
||||
## Article 12 - Monétisation créateurs
|
||||
|
||||
### 12.1 Conditions d'éligibilité
|
||||
|
||||
- Compte ≥3 mois
|
||||
- ≥500 abonnés
|
||||
- ≥10 000 écoutes totales
|
||||
- 0 strike actif
|
||||
- ≥5 contenus publiés dans les 90 derniers jours
|
||||
|
||||
### 12.2 KYC (Know Your Customer)
|
||||
|
||||
Vérification obligatoire via Mangopay :
|
||||
- SIRET, numéro TVA, RIB professionnel
|
||||
- Pièce d'identité
|
||||
- Kbis <3 mois (entreprises)
|
||||
|
||||
### 12.3 Revenus
|
||||
|
||||
**Publicités** :
|
||||
- 3€ / 1000 écoutes complètes
|
||||
- Représente ~6% du CA publicitaire
|
||||
|
||||
**Premium** :
|
||||
- 70% créateur, 30% plateforme
|
||||
- Proportionnel au temps d'écoute
|
||||
|
||||
### 12.4 Paiement
|
||||
|
||||
- Seuil minimum : **50€**
|
||||
- Fréquence : mensuelle (15 du mois suivant)
|
||||
- Virement SEPA via Mangopay
|
||||
|
||||
---
|
||||
|
||||
## Article 13 - Données personnelles
|
||||
|
||||
Voir **[Politique de confidentialité](politique-confidentialite.md)** pour détails complets.
|
||||
|
||||
### 13.1 Données collectées
|
||||
|
||||
- Position GPS (avec consentement explicite)
|
||||
- Historique d'écoute
|
||||
- Centres d'intérêt (jauges)
|
||||
- Identité créateur (KYC si monétisation)
|
||||
|
||||
### 13.2 Droits RGPD
|
||||
|
||||
- **Accès** : consulter toutes ses données
|
||||
- **Rectification** : modifier ses informations
|
||||
- **Suppression** : supprimer son compte (grace period 30 jours)
|
||||
- **Portabilité** : export JSON + HTML + audio
|
||||
- **Opposition** : désactiver profilage publicitaire
|
||||
|
||||
### 13.3 Contact DPO
|
||||
|
||||
- Email : dpo@roadwave.fr
|
||||
- Délai réponse : **1 mois** (RGPD)
|
||||
|
||||
---
|
||||
|
||||
## Article 14 - Propriété intellectuelle
|
||||
|
||||
### 14.1 Propriété de la Plateforme
|
||||
|
||||
RoadWave et tous ses éléments (code, design, logo, marque) sont protégés par :
|
||||
- Droit d'auteur
|
||||
- Droit des marques
|
||||
- Droit sui generis des bases de données
|
||||
|
||||
### 14.2 Propriété des contenus
|
||||
|
||||
- **Créateur conserve** tous les droits sur ses contenus
|
||||
- **Licence accordée à RoadWave** :
|
||||
- Non exclusive
|
||||
- Mondiale
|
||||
- Gratuite
|
||||
- Révocable (suppression contenu par créateur)
|
||||
- Incluant : hébergement, diffusion, transcoding, transcription IA
|
||||
|
||||
### 14.3 Utilisation des contenus par RoadWave
|
||||
|
||||
RoadWave peut :
|
||||
- Diffuser les contenus aux auditeurs
|
||||
- Transcrire automatiquement (modération IA)
|
||||
- Générer segments HLS (streaming)
|
||||
- Promouvoir la Plateforme (captures d'écran, extraits marketing)
|
||||
|
||||
RoadWave **ne peut pas** :
|
||||
- Revendre les contenus à des tiers
|
||||
- Modifier substantiellement les contenus (hors transcoding technique)
|
||||
|
||||
---
|
||||
|
||||
## Article 15 - Résiliation
|
||||
|
||||
### 15.1 Résiliation par l'utilisateur
|
||||
|
||||
- **Suppression compte** : à tout moment depuis paramètres
|
||||
- **Grace period** : 30 jours (récupération possible)
|
||||
- **Après 30 jours** : suppression définitive
|
||||
|
||||
### 15.2 Résiliation par RoadWave
|
||||
|
||||
RoadWave peut suspendre ou supprimer un compte en cas de :
|
||||
- Violation grave des CGU
|
||||
- Strike 4 (ban définitif)
|
||||
- Activité frauduleuse
|
||||
- Compte inactif >24 mois (après notification)
|
||||
|
||||
### 15.3 Conséquences de la résiliation
|
||||
|
||||
**Contenus créateurs** :
|
||||
- Dépubliés immédiatement
|
||||
- Marqués "Utilisateur supprimé" (anonymisation)
|
||||
- Fichiers audio supprimés sous 48h
|
||||
|
||||
**Abonnement Premium** :
|
||||
- Pas de remboursement au prorata
|
||||
- Accès jusqu'à fin période payée
|
||||
|
||||
**Revenus créateurs** :
|
||||
- Solde <50€ → perdu
|
||||
- Solde ≥50€ → virement final sous 30 jours
|
||||
|
||||
---
|
||||
|
||||
## Article 16 - Limitation de responsabilité
|
||||
|
||||
### 16.1 Disponibilité de la Plateforme
|
||||
|
||||
RoadWave s'efforce d'assurer une disponibilité 24/7 mais :
|
||||
- **Aucune garantie** de disponibilité continue
|
||||
- Maintenances programmées notifiées 48h avant
|
||||
- Interruptions d'urgence possibles sans préavis
|
||||
|
||||
### 16.2 Contenus utilisateurs
|
||||
|
||||
RoadWave n'est **pas responsable** des contenus publiés par les créateurs.
|
||||
|
||||
**Modération** :
|
||||
- Validation 3 premiers contenus (nouveaux créateurs)
|
||||
- Modération a posteriori (créateurs vérifiés)
|
||||
- Réactivité <24-48h après signalement
|
||||
|
||||
### 16.3 Limitation de dommages
|
||||
|
||||
RoadWave ne peut être tenu responsable de :
|
||||
- Perte de données (sauvegardes régulières recommandées)
|
||||
- Perte de revenus créateurs (indisponibilité temporaire)
|
||||
- Dommages indirects ou consécutifs
|
||||
|
||||
**Plafond** :
|
||||
- Responsabilité limitée aux **12 derniers mois d'abonnement Premium** (auditeurs)
|
||||
- Responsabilité limitée aux **revenus perçus sur 12 derniers mois** (créateurs)
|
||||
|
||||
---
|
||||
|
||||
## Article 17 - Droit applicable et juridiction
|
||||
|
||||
### 17.1 Droit applicable
|
||||
|
||||
Les présentes CGU sont régies par le **droit français**.
|
||||
|
||||
### 17.2 Médiation
|
||||
|
||||
Avant toute action judiciaire, l'utilisateur doit tenter une **médiation** :
|
||||
- Médiateur de la consommation : [NOM MÉDIATEUR]
|
||||
- Plateforme européenne ODR : https://ec.europa.eu/consumers/odr
|
||||
|
||||
### 17.3 Juridiction
|
||||
|
||||
En cas d'échec de la médiation, les tribunaux français sont compétents.
|
||||
|
||||
---
|
||||
|
||||
## Article 18 - Dispositions diverses
|
||||
|
||||
### 18.1 Intégralité
|
||||
|
||||
Les CGU constituent l'intégralité de l'accord entre RoadWave et l'utilisateur.
|
||||
|
||||
### 18.2 Nullité partielle
|
||||
|
||||
Si une clause est jugée invalide, les autres clauses restent applicables.
|
||||
|
||||
### 18.3 Non-renonciation
|
||||
|
||||
L'absence d'exercice d'un droit par RoadWave ne constitue pas une renonciation.
|
||||
|
||||
---
|
||||
|
||||
**Contact** :
|
||||
- Email : support@roadwave.fr
|
||||
- Adresse : [ADRESSE SIÈGE SOCIAL]
|
||||
- SIRET : [SIRET]
|
||||
181
docs/legal/cgv-premium.md
Normal file
181
docs/legal/cgv-premium.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# CGV Premium
|
||||
|
||||
**Version** : 1.0
|
||||
**Date d'effet** : [DATE]
|
||||
|
||||
---
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Conditions spécifiques à l'abonnement **RoadWave Premium**.
|
||||
|
||||
Complète les **[CGU](cgu.md)** (Article 11).
|
||||
|
||||
---
|
||||
|
||||
## 2. Offre Premium
|
||||
|
||||
### 2.1 Tarifs
|
||||
|
||||
| Formule | Prix | Économie |
|
||||
|---------|------|----------|
|
||||
| **Mensuel** | 4.99€/mois | - |
|
||||
| **Annuel** | 49.99€/an | ~17% (4.16€/mois effectif) |
|
||||
|
||||
**Tarif IAP (In-App Purchase iOS/Android)** :
|
||||
- Mensuel : 5.99€/mois (+20% commission Apple/Google)
|
||||
- Annuel : 59.99€/an
|
||||
|
||||
### 2.2 Avantages
|
||||
|
||||
- **0 publicité** : aucune pub entre contenus
|
||||
- **Contenus exclusifs** : accès contenus marqués (créateurs Premium)
|
||||
- **Qualité audio supérieure** : 64 kbps Opus (vs 48 kbps gratuit)
|
||||
- **Offline illimité** : téléchargement sans limite (vs 50 max gratuit)
|
||||
|
||||
### 2.3 Pas d'essai gratuit (MVP)
|
||||
|
||||
- Pas de trial 7 jours au MVP (peut être ajouté post-MVP)
|
||||
- Facturation immédiate à la souscription
|
||||
|
||||
### 2.4 Pas de partage familial (MVP)
|
||||
|
||||
- 1 compte = 1 abonnement
|
||||
- Partage familial : fonctionnalité post-MVP
|
||||
|
||||
---
|
||||
|
||||
## 3. Souscription
|
||||
|
||||
### 3.1 Modalités
|
||||
|
||||
**Via web (Mangopay)** :
|
||||
- Carte bancaire (Visa, Mastercard, Amex)
|
||||
- Prélèvement SEPA (si mensuel)
|
||||
- Paiement sécurisé 3D Secure
|
||||
|
||||
**Via mobile (IAP)** :
|
||||
- App Store (iOS) : Apple Pay, carte bancaire
|
||||
- Google Play (Android) : Google Pay, carte bancaire
|
||||
|
||||
### 3.2 Renouvellement automatique
|
||||
|
||||
- **Mensuel** : renouvellement le même jour chaque mois
|
||||
- **Annuel** : renouvellement 1 an après souscription
|
||||
|
||||
**Notification avant renouvellement** :
|
||||
- Email **7 jours avant** renouvellement
|
||||
- Rappel : résiliation possible à tout moment
|
||||
|
||||
---
|
||||
|
||||
## 4. Résiliation
|
||||
|
||||
### 4.1 Par l'utilisateur
|
||||
|
||||
- **À tout moment** : paramètres → "Gérer abonnement" → "Résilier"
|
||||
- **Effet** : fin période en cours (pas de remboursement au prorata)
|
||||
- **Accès** : conservé jusqu'à fin période payée
|
||||
|
||||
**Exemple** :
|
||||
```
|
||||
Souscription : 1er janvier (mensuel 4.99€)
|
||||
Résiliation : 15 janvier
|
||||
→ Accès Premium jusqu'au 31 janvier
|
||||
→ Pas de renouvellement 1er février
|
||||
```
|
||||
|
||||
### 4.2 Par RoadWave
|
||||
|
||||
RoadWave peut résilier si :
|
||||
- Échec paiement (3 tentatives)
|
||||
- Violation grave CGU (Strike 4 = ban)
|
||||
- Fraude détectée (carte volée, etc.)
|
||||
|
||||
**Notification** :
|
||||
- Email **48h avant** résiliation
|
||||
- Possibilité mise à jour moyen paiement
|
||||
|
||||
### 4.3 Remboursement
|
||||
|
||||
**Aucun remboursement** sauf :
|
||||
- Bug empêchant utilisation service >7 jours consécutifs
|
||||
- Résiliation par RoadWave (erreur de notre part)
|
||||
|
||||
**Remboursement au prorata** dans ces cas uniquement.
|
||||
|
||||
---
|
||||
|
||||
## 5. Multi-devices
|
||||
|
||||
### 5.1 Connexion
|
||||
|
||||
- **Illimitée** : connexion sur autant d'appareils que souhaité
|
||||
|
||||
### 5.2 Streaming simultané
|
||||
|
||||
- **1 seul stream actif** à la fois
|
||||
- Détection connexion simultanée :
|
||||
- Message : "Compte déjà utilisé sur un autre appareil"
|
||||
- Bouton "Déconnecter l'autre appareil"
|
||||
|
||||
### 5.3 Offline
|
||||
|
||||
- Téléchargements **illimités**
|
||||
- Synchronisation entre appareils (contenus téléchargés sur app A = visibles sur app B)
|
||||
|
||||
---
|
||||
|
||||
## 6. Modifications tarifaires
|
||||
|
||||
- Notification **30 jours avant** augmentation tarif
|
||||
- Email détaillé : ancien prix → nouveau prix
|
||||
- **Droit de résiliation** sans frais pendant ces 30 jours
|
||||
|
||||
**Protection abonnés annuels** :
|
||||
- Tarif gelé jusqu'à fin période annuelle
|
||||
- Nouveau tarif appliqué au prochain renouvellement annuel
|
||||
|
||||
---
|
||||
|
||||
## 7. Facturation
|
||||
|
||||
### 7.1 Factures
|
||||
|
||||
- Disponibles : paramètres → "Mes factures"
|
||||
- Format : PDF
|
||||
- Envoi email automatique après chaque paiement
|
||||
|
||||
### 7.2 Échec paiement
|
||||
|
||||
**Processus** :
|
||||
1. Tentative 1 : J+0 (échec)
|
||||
2. Email notification : "Échec paiement, mise à jour carte"
|
||||
3. Tentative 2 : J+3
|
||||
4. Tentative 3 : J+7
|
||||
5. Si échec final → **suspension Premium**
|
||||
|
||||
**Suspension** :
|
||||
- Accès Premium désactivé
|
||||
- Passage compte gratuit (avec pubs)
|
||||
- Réactivation possible sous 30 jours (mise à jour carte)
|
||||
|
||||
---
|
||||
|
||||
## 8. Rétractation (14 jours)
|
||||
|
||||
**Droit de rétractation UE** :
|
||||
- **14 jours** après souscription
|
||||
- Demande via : support@roadwave.fr
|
||||
- Remboursement intégral sous 14 jours
|
||||
|
||||
**Exception** :
|
||||
- Si utilisation service pendant 14 jours = renonciation au droit de rétractation
|
||||
- Acceptation explicite à la souscription
|
||||
|
||||
---
|
||||
|
||||
## 9. Contact
|
||||
|
||||
- Email : premium@roadwave.fr
|
||||
- Support : support@roadwave.fr
|
||||
193
docs/legal/cgv-publicites.md
Normal file
193
docs/legal/cgv-publicites.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# CGV Publicités
|
||||
|
||||
**Version** : 1.0
|
||||
**Date d'effet** : [DATE]
|
||||
|
||||
---
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Conditions spécifiques aux **annonceurs publicitaires** RoadWave.
|
||||
|
||||
Complète les **[CGU](cgu.md)** (Article 10).
|
||||
|
||||
---
|
||||
|
||||
## 2. Accès à la plateforme publicitaire
|
||||
|
||||
### 2.1 Inscription
|
||||
|
||||
- Interface self-service : roadwave.fr/pub
|
||||
- Email professionnel requis
|
||||
- SIRET obligatoire (entreprises françaises)
|
||||
|
||||
### 2.2 Validation compte annonceur
|
||||
|
||||
- Vérification SIRET (24-48h)
|
||||
- Email confirmation activation
|
||||
|
||||
---
|
||||
|
||||
## 3. Création campagne
|
||||
|
||||
### 3.1 Paramètres campagne
|
||||
|
||||
| Paramètre | Options | Validation |
|
||||
|-----------|---------|------------|
|
||||
| **Budget** | Min 50€, max illimité | Obligatoire |
|
||||
| **Ciblage géo** | Point GPS / Ville / Département / Région / National | Obligatoire |
|
||||
| **Durée pub** | 10-60 secondes (recommandé 15-30s) | Obligatoire |
|
||||
| **Étalement** | 1 jour à 365 jours | Obligatoire |
|
||||
|
||||
### 3.2 Upload audio
|
||||
|
||||
- Formats : MP3, AAC
|
||||
- Taille max : 10 MB
|
||||
- Qualité recommandée : 128 kbps
|
||||
|
||||
### 3.3 Validation manuelle
|
||||
|
||||
- **Délai** : 24-48h (jours ouvrés)
|
||||
- **Critères** : conformité CGU, pas de contenu trompeur, respect ARPP
|
||||
|
||||
**Refus si** :
|
||||
- Contenu trompeur, illégal
|
||||
- Alcool, tabac (interdits)
|
||||
- Produits santé non autorisés
|
||||
- Arnaque évidente
|
||||
|
||||
---
|
||||
|
||||
## 4. Tarification
|
||||
|
||||
### 4.1 Modèle CPM
|
||||
|
||||
- **Écoute complète** : 0.05€
|
||||
- **Skip après 5s** : 0.02€
|
||||
- **Skip immédiat (<5s)** : 0€
|
||||
|
||||
### 4.2 Budget minimum
|
||||
|
||||
- **50€ minimum** par campagne
|
||||
- Étalement paramétrable (ex: 50€ sur 10 jours = 5€/jour max)
|
||||
|
||||
### 4.3 Prépaiement obligatoire
|
||||
|
||||
- Paiement **avant diffusion** (Mangopay)
|
||||
- Carte bancaire (Visa, Mastercard, Amex)
|
||||
- Virement SEPA (délai 1-2 jours)
|
||||
|
||||
---
|
||||
|
||||
## 5. Diffusion
|
||||
|
||||
### 5.1 Insertion publicitaire
|
||||
|
||||
- **Fréquence** : 1 pub / 5 contenus (auditeurs gratuits uniquement)
|
||||
- **Skippable** après **5 secondes**
|
||||
- Entre deux contenus (jamais d'interruption mid-roll)
|
||||
|
||||
### 5.2 Ciblage géographique
|
||||
|
||||
- Diffusion uniquement aux auditeurs **dans la zone ciblée**
|
||||
- Exemple : pub "Restaurant Paris 15e" → diffusée uniquement dans rayon paramétré
|
||||
|
||||
### 5.3 Priorité diffusion
|
||||
|
||||
- **Enchères automatiques** : plus le budget restant élevé, plus la pub est diffusée
|
||||
- Pas de manipulation manuelle (algorithme équitable)
|
||||
|
||||
---
|
||||
|
||||
## 6. Reporting
|
||||
|
||||
### 6.1 Dashboard annonceur
|
||||
|
||||
Accessible 24/7 : roadwave.fr/pub/dashboard
|
||||
|
||||
**Métriques** :
|
||||
- Impressions totales (nombre diffusions)
|
||||
- Taux d'écoute complète (%)
|
||||
- Taux de skip (%)
|
||||
- Budget consommé / restant
|
||||
- CPM moyen effectif
|
||||
|
||||
### 6.2 Mise à jour
|
||||
|
||||
- Temps réel (rafraîchissement toutes les 5 minutes)
|
||||
|
||||
---
|
||||
|
||||
## 7. Facturation
|
||||
|
||||
### 7.1 Factures
|
||||
|
||||
- PDF disponible : dashboard → "Mes factures"
|
||||
- Envoi email automatique fin campagne
|
||||
|
||||
### 7.2 Budget non consommé
|
||||
|
||||
**Si campagne terminée avec budget restant** :
|
||||
- <10€ : crédit conservé pour prochaine campagne
|
||||
- ≥10€ : remboursement automatique sous 14 jours
|
||||
|
||||
---
|
||||
|
||||
## 8. Modification / Annulation campagne
|
||||
|
||||
### 8.1 Avant validation (24-48h)
|
||||
|
||||
- Modification libre (budget, ciblage, audio)
|
||||
- Annulation gratuite → remboursement intégral
|
||||
|
||||
### 8.2 Après validation (campagne en cours)
|
||||
|
||||
- **Pause** : possible à tout moment
|
||||
- **Annulation** : budget consommé non remboursable, budget restant remboursé
|
||||
- **Modification ciblage** : possible (ex: élargir zone géo)
|
||||
- **Modification audio** : nouvelle validation requise (24-48h)
|
||||
|
||||
---
|
||||
|
||||
## 9. Interdictions
|
||||
|
||||
### 9.1 Contenus prohibés
|
||||
|
||||
Strictement interdits :
|
||||
- Alcool, tabac, cannabis
|
||||
- Jeux d'argent (casinos, paris sportifs)
|
||||
- Produits pharmaceutiques sans autorisation
|
||||
- Arnaques, MLM illégaux
|
||||
- Contenu haineux, discriminatoire
|
||||
- Produits contrefaits
|
||||
|
||||
### 9.2 Sanctions
|
||||
|
||||
- **1ère violation** : suppression pub + avertissement
|
||||
- **2e violation** : suspension compte annonceur 30 jours
|
||||
- **3e violation** : ban définitif + blacklist
|
||||
|
||||
---
|
||||
|
||||
## 10. Responsabilité
|
||||
|
||||
### 10.1 Annonceur
|
||||
|
||||
L'annonceur garantit :
|
||||
- Détenir droits sur contenu publicitaire (musique, voix, marques)
|
||||
- Conformité légale (ARPP, DGCCRF)
|
||||
- Véracité des informations (pas de publicité mensongère)
|
||||
|
||||
### 10.2 RoadWave
|
||||
|
||||
RoadWave n'est pas responsable de :
|
||||
- Performance commerciale de la pub (pas de garantie ventes)
|
||||
- Avis négatifs utilisateurs sur la pub
|
||||
|
||||
---
|
||||
|
||||
## 11. Contact
|
||||
|
||||
- Email annonceurs : pub@roadwave.fr
|
||||
- Support : support@roadwave.fr
|
||||
- Téléphone : [TÉLÉPHONE] (9h-18h jours ouvrés)
|
||||
299
docs/legal/charte-createur.md
Normal file
299
docs/legal/charte-createur.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# Charte Créateur
|
||||
|
||||
**Version** : 1.0
|
||||
**Date d'effet** : [DATE]
|
||||
|
||||
---
|
||||
|
||||
## Préambule
|
||||
|
||||
Cette Charte complète les **[CGU](cgu.md)** (Articles 6 et 7) et détaille les règles spécifiques aux créateurs de contenu.
|
||||
|
||||
**Acceptation obligatoire** avant première publication.
|
||||
|
||||
---
|
||||
|
||||
## 1. Validation des 3 premiers contenus
|
||||
|
||||
### 1.1 Processus
|
||||
|
||||
- Upload contenu → **file d'attente modération**
|
||||
- Modérateur RoadWave écoute 30 secondes
|
||||
- Vérification : qualité audio, métadonnées, respect règles
|
||||
- Délai : **24-48h** (jours ouvrés, peut atteindre 72h le weekend)
|
||||
|
||||
### 1.2 Critères de validation
|
||||
|
||||
**Accepté si** :
|
||||
- Qualité audio compréhensible
|
||||
- Métadonnées cohérentes (zone géo, tags, classification âge)
|
||||
- Pas de contenu prohibé évident
|
||||
|
||||
**Refusé si** :
|
||||
- Audio incompréhensible (grésillement excessif)
|
||||
- Musique protégée évidente en intégrale
|
||||
- Contenu haineux, violent, illégal
|
||||
- Métadonnées incohérentes (ex: "Tour Eiffel" en zone "National")
|
||||
|
||||
### 1.3 Après 3 validations
|
||||
|
||||
- **Statut "Vérifié"** : badge visible sur profil
|
||||
- **Publication immédiate** : contenus futurs publiés sans délai
|
||||
- **Modération a posteriori** : uniquement si signalé
|
||||
|
||||
---
|
||||
|
||||
## 2. Règles de publication
|
||||
|
||||
### 2.1 Formats acceptés
|
||||
|
||||
- **Audio** : MP3, AAC (.mp3, .aac, .m4a)
|
||||
- **Taille max** : 200 MB (~4h podcast 128 kbps)
|
||||
- **Durée max** : 4 heures
|
||||
|
||||
### 2.2 Métadonnées obligatoires
|
||||
|
||||
| Champ | Format | Validation |
|
||||
|-------|--------|------------|
|
||||
| **Titre** | 5-100 caractères | Obligatoire |
|
||||
| **Type géo** | Ancré / Contextuel / Neutre | Obligatoire |
|
||||
| **Zone diffusion** | Point GPS / Ville / Département / Région / National | Obligatoire |
|
||||
| **Tags** | 1 à 3 parmi 12 catégories | Obligatoire |
|
||||
| **Classification âge** | Tout public / 13+ / 16+ / 18+ | Obligatoire |
|
||||
|
||||
**Tags disponibles** :
|
||||
- Automobile, Voyage, Famille, Amour, Musique, Économie, Cryptomonnaie, Politique, Culture générale, Sport, Technologie, Santé
|
||||
|
||||
### 2.3 Cohérence métadonnées
|
||||
|
||||
**Exemples incohérents** :
|
||||
- "Histoire Tour Eiffel" en zone "National" (devrait être "Point GPS Paris")
|
||||
- Podcast enfants 3-6 ans classé "18+" (incohérent)
|
||||
- Contenu politique non tagué "Politique"
|
||||
|
||||
**Exemples cohérents** :
|
||||
- "Visite château Versailles" → Type Ancré, Point GPS Versailles, Tags Voyage + Culture
|
||||
- "Podcast auto route A7" → Type Contextuel, Zone Région Auvergne-Rhône-Alpes, Tag Automobile
|
||||
|
||||
---
|
||||
|
||||
## 3. Droits d'auteur et musique
|
||||
|
||||
### 3.1 Musique autorisée
|
||||
|
||||
**Vous pouvez utiliser** :
|
||||
- Votre propre musique originale (vous êtes compositeur/interprète)
|
||||
- Musique libre de droits (Epidemic Sound, Artlist, YouTube Audio Library, Creative Commons CC0)
|
||||
- Musique domaine public (>70 ans après mort auteur, ex: classique pré-1950)
|
||||
- **Extraits courts ≤30 secondes** pour critique/analyse (fair use)
|
||||
|
||||
### 3.2 Musique interdite
|
||||
|
||||
**Strictement interdit** :
|
||||
- Musique protégée en intégrale (titre complet)
|
||||
- Musique protégée en fond >30 secondes
|
||||
- Compilation DJ sans droits
|
||||
- Karaoké (instrumental protégé + voix)
|
||||
|
||||
### 3.3 Fair use (usage transformatif)
|
||||
|
||||
**Conditions cumulatives** pour extraits ≤30s :
|
||||
1. Durée ≤30 secondes **ET**
|
||||
2. Usage transformatif : commentaire, critique, analyse **ET**
|
||||
3. Pas de substitution à l'œuvre originale **ET**
|
||||
4. Mention titre + artiste recommandée
|
||||
|
||||
**Exemples OK** :
|
||||
- Review album : extrait 20s + commentaire analyse musicale
|
||||
- Podcast histoire musique : extrait 25s + contexte historique
|
||||
|
||||
**Exemples KO** :
|
||||
- Fond musical hit radio 2 minutes (>30s)
|
||||
- Extrait 15s sans commentaire (pas d'usage transformatif)
|
||||
|
||||
### 3.4 Preuves licence
|
||||
|
||||
Si vous utilisez musique payante (Epidemic Sound, Artlist) :
|
||||
- Conservez factures/contrats
|
||||
- En cas de signalement, upload preuve via processus d'appel
|
||||
- Musique sera ajoutée à whitelist interne (évite futures erreurs)
|
||||
|
||||
---
|
||||
|
||||
## 4. Radio live
|
||||
|
||||
### 4.1 Règles spécifiques
|
||||
|
||||
**Interdictions strictes en live** :
|
||||
- Diffusion concert/spectacle depuis la salle → Strike 2 immédiat (7 jours)
|
||||
- Événement sportif payant (match, compétition droits TV) → Strike 2 immédiat
|
||||
- Film, série, musique fond sans droits → Strike 1 (3 jours)
|
||||
- Contenu violent en direct → Strike 3 immédiat (30 jours)
|
||||
- Contenu illégal (terrorisme, etc.) → Strike 4 (ban) + signalement autorités
|
||||
|
||||
### 4.2 Enregistrement automatique
|
||||
|
||||
- Live enregistré automatiquement
|
||||
- Replay publié sous **5-10 minutes** après fin
|
||||
- Option "Ne pas publier replay" désactivable avant démarrage
|
||||
|
||||
---
|
||||
|
||||
## 5. Classification d'âge
|
||||
|
||||
### 5.1 Règles strictes
|
||||
|
||||
| Classification | Contenu autorisé | Exemple |
|
||||
|----------------|------------------|---------|
|
||||
| **Tout public** | Aucune restriction | Contenu familial, éducatif |
|
||||
| **13+** | Langage léger, thèmes adolescents | Podcast ados, gaming |
|
||||
| **16+** | Langage grossier, thèmes matures | Politique, économie, débats |
|
||||
| **18+** | Contenu adulte (non sexuel) | Humour noir, true crime violent |
|
||||
|
||||
### 5.2 Interdit quelle que soit la classification
|
||||
|
||||
- Contenu sexuellement explicite (pornographie) → **Toujours interdit**
|
||||
- Contenu impliquant mineurs → **Toujours interdit + signalement**
|
||||
|
||||
---
|
||||
|
||||
## 6. Modification et suppression
|
||||
|
||||
### 6.1 Modification autorisée
|
||||
|
||||
| Élément | Modifiable après publication |
|
||||
|---------|------------------------------|
|
||||
| Titre | Oui |
|
||||
| Description | Oui (si ajoutée) |
|
||||
| Tags | Oui |
|
||||
| Image couverture | Oui |
|
||||
| **Audio** | Non |
|
||||
| **Zone diffusion** | Non |
|
||||
| **Type géo** | Non |
|
||||
| **Classification âge** | Non |
|
||||
|
||||
**Raison** : éviter fraude (uploader contenu validé → remplacer par spam).
|
||||
|
||||
### 6.2 Si besoin de changer audio/zone/classification
|
||||
|
||||
→ **Supprimer contenu + republier**
|
||||
- Si <3 contenus validés : retourne en file validation
|
||||
- Si ≥3 contenus validés : publication immédiate
|
||||
|
||||
### 6.3 Suppression
|
||||
|
||||
- **Irréversible** : suppression définitive sous 24h
|
||||
- Historique auditeurs : marqué "Contenu supprimé par créateur"
|
||||
- Analytics anonymisés conservés (RGPD compliant)
|
||||
|
||||
---
|
||||
|
||||
## 7. Monétisation
|
||||
|
||||
### 7.1 Conditions d'éligibilité
|
||||
|
||||
- Compte ≥3 mois
|
||||
- ≥500 abonnés
|
||||
- ≥10 000 écoutes totales
|
||||
- **0 strike actif** (strikes passés effacés OK)
|
||||
- ≥5 contenus publiés dans les 90 derniers jours
|
||||
|
||||
### 7.2 Suspension monétisation
|
||||
|
||||
**Suspension immédiate si** :
|
||||
- Strike 3+ actif
|
||||
- Inactivité (aucun contenu publié depuis 6 mois)
|
||||
- Échec virement (RIB invalide, 3 tentatives)
|
||||
|
||||
**Réactivation** :
|
||||
- Après résolution strikes (réhabilitation 6 mois)
|
||||
- Après mise à jour RIB
|
||||
- Après publication nouveau contenu
|
||||
|
||||
### 7.3 Fermeture compte monétisé
|
||||
|
||||
**Solde perdu si** :
|
||||
- <50€ au moment de la fermeture
|
||||
- Ban définitif (Strike 4)
|
||||
- Inactivité 24 mois + solde <50€
|
||||
|
||||
**Virement final si** :
|
||||
- ≥50€ → virement sous 30 jours après fermeture
|
||||
|
||||
---
|
||||
|
||||
## 8. Comportement et éthique
|
||||
|
||||
### 8.1 Spam
|
||||
|
||||
**Interdit** :
|
||||
- Publier 10+ contenus identiques/similaires
|
||||
- Répéter contenu dans zones différentes (ex: même podcast publié 50 fois dans 50 villes)
|
||||
- Auto-promotion excessive (ex: "Abonnez-vous !" répété 20 fois)
|
||||
|
||||
### 8.2 Manipulation
|
||||
|
||||
**Interdit** :
|
||||
- Acheter des abonnés, likes, écoutes
|
||||
- Utiliser bots pour gonfler métriques
|
||||
- S'abonner massivement puis se désabonner (follow/unfollow spam)
|
||||
|
||||
### 8.3 Harcèlement
|
||||
|
||||
**Interdit** :
|
||||
- Créer contenu ciblant nommément une personne (harcèlement)
|
||||
- Publier informations privées d'un tiers (doxxing)
|
||||
- Menaces répétées
|
||||
|
||||
---
|
||||
|
||||
## 9. Sanctions spécifiques créateurs
|
||||
|
||||
### 9.1 Grille sanctions (rappel)
|
||||
|
||||
| Violation | Sanction |
|
||||
|-----------|----------|
|
||||
| **1ère fois (droits d'auteur uniquement)** | Avertissement (pas de strike) |
|
||||
| **1ère violation autre** | Strike 1 (3 jours) |
|
||||
| **2e violation** | Strike 2 (7 jours) |
|
||||
| **3e violation** | Strike 3 (30 jours) |
|
||||
| **4e violation** | Strike 4 (ban définitif) |
|
||||
|
||||
### 9.2 Conséquences strike
|
||||
|
||||
**Suspension upload** :
|
||||
- Strike 1 : **3 jours** sans possibilité de publier
|
||||
- Strike 2 : **7 jours**
|
||||
- Strike 3 : **30 jours**
|
||||
- Strike 4 : **Permanent** (ban définitif)
|
||||
|
||||
**Pas d'impact sur** :
|
||||
- Contenus déjà publiés (restent en ligne sauf si violents/illégaux)
|
||||
- Revenus en cours (virement mensuel maintenu)
|
||||
- Radio live (suspension upload = suspension live aussi)
|
||||
|
||||
---
|
||||
|
||||
## 10. Ressources et aide
|
||||
|
||||
### 10.1 Documentation
|
||||
|
||||
- **Page CGU musique** : roadwave.fr/cgu/musique
|
||||
- **FAQ créateurs** : roadwave.fr/faq-createurs
|
||||
- **Liste musique libre** : roadwave.fr/musique-libre
|
||||
|
||||
### 10.2 Support
|
||||
|
||||
- **Email** : createurs@roadwave.fr
|
||||
- **Délai réponse** : 48-72h (jours ouvrés)
|
||||
|
||||
### 10.3 Signalement problème
|
||||
|
||||
- Si contenu validé à tort (erreur modération)
|
||||
- Si strike injustifié → processus d'appel (7 jours, délai 72h)
|
||||
|
||||
---
|
||||
|
||||
**Contact** :
|
||||
- Email créateurs : createurs@roadwave.fr
|
||||
- Email général : support@roadwave.fr
|
||||
55
docs/legal/mentions-legales.md
Normal file
55
docs/legal/mentions-legales.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Mentions légales
|
||||
|
||||
---
|
||||
|
||||
## Éditeur
|
||||
|
||||
**[NOM SOCIÉTÉ]**
|
||||
[FORME JURIDIQUE] au capital de [CAPITAL]€
|
||||
Siège social : [ADRESSE COMPLÈTE]
|
||||
SIRET : [SIRET]
|
||||
RCS : [VILLE]
|
||||
TVA intracommunautaire : [TVA]
|
||||
|
||||
---
|
||||
|
||||
## Directeur de la publication
|
||||
|
||||
[NOM FONDATEUR]
|
||||
|
||||
---
|
||||
|
||||
## Hébergement
|
||||
|
||||
**OVH SAS**
|
||||
2 rue Kellermann
|
||||
59100 Roubaix
|
||||
France
|
||||
SIRET : 424 761 419 00045
|
||||
Site : https://www.ovhcloud.com
|
||||
|
||||
---
|
||||
|
||||
## Paiements
|
||||
|
||||
**Mangopay SA**
|
||||
2 Avenue Amélie
|
||||
L-1126 Luxembourg
|
||||
Agrément : Établissement de monnaie électronique (EME)
|
||||
Site : https://www.mangopay.com
|
||||
|
||||
---
|
||||
|
||||
## Contact
|
||||
|
||||
- Email : contact@roadwave.fr
|
||||
- Support : support@roadwave.fr
|
||||
- DPO : dpo@roadwave.fr
|
||||
|
||||
---
|
||||
|
||||
## Propriété intellectuelle
|
||||
|
||||
Tous droits réservés © RoadWave 2026
|
||||
Marque déposée : RoadWave ®
|
||||
Logo, design, code source protégés par droit d'auteur.
|
||||
305
docs/legal/politique-confidentialite.md
Normal file
305
docs/legal/politique-confidentialite.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# Politique de confidentialité
|
||||
|
||||
**Version** : 1.0
|
||||
**Date d'effet** : [DATE]
|
||||
**Dernière mise à jour** : [DATE]
|
||||
|
||||
Conforme au **Règlement Général sur la Protection des Données (RGPD)** (UE 2016/679).
|
||||
|
||||
---
|
||||
|
||||
## 1. Responsable de traitement
|
||||
|
||||
**RoadWave**
|
||||
[ADRESSE SIÈGE SOCIAL]
|
||||
SIRET : [SIRET]
|
||||
Email : dpo@roadwave.fr
|
||||
|
||||
**Délégué à la Protection des Données (DPO)** :
|
||||
[NOM DPO]
|
||||
Email : dpo@roadwave.fr
|
||||
|
||||
---
|
||||
|
||||
## 2. Données collectées
|
||||
|
||||
### 2.1 Données d'identification
|
||||
|
||||
| Donnée | Obligatoire | Finalité | Base légale |
|
||||
|--------|-------------|----------|-------------|
|
||||
| **Email** | Oui | Connexion, récupération compte | Exécution du contrat |
|
||||
| **Mot de passe** | Oui | Authentification sécurisée | Exécution du contrat |
|
||||
| **Pseudo** | Oui | Identification publique créateur | Exécution du contrat |
|
||||
| **Date de naissance** | Oui | Vérification âge minimum (13 ans) | Obligation légale |
|
||||
|
||||
### 2.2 Données de géolocalisation
|
||||
|
||||
| Donnée | Obligatoire | Finalité | Base légale |
|
||||
|--------|-------------|----------|-------------|
|
||||
| **Position GPS précise** | Non | Diffusion contenu géolocalisé | **Consentement explicite** |
|
||||
| **Geohash 5 (~5km²)** | Auto | Recommandations après 24h | Intérêt légitime |
|
||||
|
||||
**Durée conservation GPS précis** :
|
||||
- Historique personnel : **24h** puis agrégé en geohash 5
|
||||
- Geohash : **conservé** pour analytics (anonymisé)
|
||||
|
||||
**Révocation consentement** :
|
||||
- Désactivation GPS dans paramètres → effet immédiat
|
||||
- Mode dégradé : contenus nationaux + neutres uniquement
|
||||
|
||||
### 2.3 Données d'utilisation
|
||||
|
||||
| Donnée | Finalité | Base légale |
|
||||
|--------|----------|-------------|
|
||||
| **Historique d'écoute** | Recommandations personnalisées | Intérêt légitime |
|
||||
| **Centres d'intérêt (jauges)** | Algorithme de recommandation | Consentement |
|
||||
| **Interactions** (likes, abonnements, skips) | Amélioration UX, analytics | Intérêt légitime |
|
||||
| **Logs techniques** (IP, user-agent) | Sécurité, anti-fraude | Intérêt légitime |
|
||||
|
||||
**IP anonymisée** :
|
||||
- Dernier octet masqué (ex: 192.168.1.XXX)
|
||||
- Conservation : 7 jours (sécurité) puis suppression
|
||||
|
||||
### 2.4 Données créateurs (monétisation)
|
||||
|
||||
| Donnée | Finalité | Base légale |
|
||||
|--------|----------|-------------|
|
||||
| **SIRET, numéro TVA** | KYC Mangopay, fiscalité | Obligation légale |
|
||||
| **RIB professionnel** | Virements revenus | Exécution du contrat |
|
||||
| **Pièce d'identité** | Vérification identité (KYC) | Obligation légale |
|
||||
| **Kbis <3 mois** | Vérification entreprise | Obligation légale |
|
||||
|
||||
**Traitement KYC** : délégué à **Mangopay** (sous-traitant RGPD).
|
||||
|
||||
### 2.5 Données analytics
|
||||
|
||||
- **Matomo self-hosted** (pas Google Analytics)
|
||||
- IP anonymisées
|
||||
- **0 cookie tiers**
|
||||
- Données agrégées (pas de tracking individuel hors RoadWave)
|
||||
|
||||
---
|
||||
|
||||
## 3. Finalités et bases légales
|
||||
|
||||
| Finalité | Base légale RGPD | Opt-out possible |
|
||||
|----------|------------------|------------------|
|
||||
| **Création et gestion compte** | Exécution du contrat | Non (essentiel) |
|
||||
| **Diffusion contenu géolocalisé** | Consentement (GPS) | Oui (désactiver GPS) |
|
||||
| **Recommandations personnalisées** | Intérêt légitime | Oui (mode anonyme) |
|
||||
| **Modération contenus** | Obligation légale (DSA) | Non |
|
||||
| **Analytics plateforme** | Intérêt légitime | Oui (opposition) |
|
||||
| **Publicité ciblée géo** | Consentement | Oui (désactiver dans paramètres) |
|
||||
| **Paiements créateurs** | Exécution du contrat | Non (si monétisation) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Partage des données
|
||||
|
||||
### 4.1 Sous-traitants RGPD
|
||||
|
||||
| Sous-traitant | Finalité | Localisation données | DPA signé |
|
||||
|---------------|----------|----------------------|-----------|
|
||||
| **Mangopay** | Paiements, KYC créateurs | UE (Luxembourg) | Oui |
|
||||
| **OVH** | Hébergement audio, stockage fichiers | France | Oui |
|
||||
| **Hetzner** | Hébergement serveurs backend | UE (Allemagne) | Oui |
|
||||
|
||||
**DPA** = Data Processing Agreement (accord de sous-traitance RGPD).
|
||||
|
||||
### 4.2 Données publiques
|
||||
|
||||
| Donnée | Visibilité | Modification |
|
||||
|--------|------------|--------------|
|
||||
| **Pseudo créateur** | Publique | Modifiable |
|
||||
| **Bio créateur** | Publique | Modifiable |
|
||||
| **Stats créateur** (abonnés, écoutes) | Publique (arrondies) | Non modifiable |
|
||||
| **Badge vérifié** | Publique | Non modifiable |
|
||||
|
||||
### 4.3 Aucune revente
|
||||
|
||||
RoadWave **ne vend jamais** vos données à des tiers.
|
||||
|
||||
### 4.4 Autorités
|
||||
|
||||
Données transmises uniquement si :
|
||||
- **Obligation légale** (réquisition judiciaire)
|
||||
- **Signalement contenu illégal** (terrorisme, pédopornographie) → autorités compétentes
|
||||
|
||||
---
|
||||
|
||||
## 5. Durées de conservation
|
||||
|
||||
| Donnée | Durée | Justification |
|
||||
|--------|-------|---------------|
|
||||
| **Compte actif** | Tant que compte existe | Exécution contrat |
|
||||
| **Compte supprimé** | Grace period **30 jours** puis suppression | Récupération possible |
|
||||
| **GPS précis** | **24h** puis agrégé geohash 5 | RGPD minimisation |
|
||||
| **Historique écoute** | Tant que compte existe | Recommandations |
|
||||
| **Logs modération** | **3 ans** (DSA) | Obligation légale |
|
||||
| **Logs techniques (IP)** | **7 jours** puis suppression | Sécurité |
|
||||
| **KYC créateurs** | 5 ans après fin relation (obligation fiscale) | Obligation légale |
|
||||
| **Contenus supprimés** | Anonymisés immédiatement, analytics conservés | Analytics plateforme |
|
||||
|
||||
---
|
||||
|
||||
## 6. Droits des utilisateurs (RGPD)
|
||||
|
||||
### 6.1 Droit d'accès
|
||||
|
||||
- **Consulter** toutes ses données personnelles
|
||||
- Interface dédiée : "Mes données" dans paramètres
|
||||
- Export automatique (voir 6.4)
|
||||
|
||||
### 6.2 Droit de rectification
|
||||
|
||||
- **Modifier** : pseudo, bio, email, centres d'intérêt
|
||||
- Demande via : dpo@roadwave.fr
|
||||
- Délai : **1 mois** (RGPD)
|
||||
|
||||
### 6.3 Droit à l'effacement ("droit à l'oubli")
|
||||
|
||||
- **Suppression compte** : paramètres → "Supprimer mon compte"
|
||||
- Grace period : **30 jours** (récupération possible)
|
||||
- Après 30 jours : suppression définitive
|
||||
|
||||
**Exceptions** (conservation nécessaire) :
|
||||
- Logs modération : 3 ans (obligation DSA)
|
||||
- KYC créateurs : 5 ans (obligation fiscale)
|
||||
- Analytics agrégés (anonymisés)
|
||||
|
||||
### 6.4 Droit à la portabilité
|
||||
|
||||
- **Export données** : JSON + HTML + audio
|
||||
- Générateur asynchrone (délai **48h** si volume important)
|
||||
- Lien téléchargement expire après **7 jours**
|
||||
|
||||
**Contenu export** :
|
||||
- Profil (pseudo, bio, stats)
|
||||
- Historique écoute complet
|
||||
- Centres d'intérêt (jauges)
|
||||
- Contenus publiés (métadonnées + fichiers audio)
|
||||
- Abonnements, likes
|
||||
|
||||
### 6.5 Droit d'opposition
|
||||
|
||||
- **Profilage publicitaire** : désactivation dans paramètres
|
||||
- **Analytics** : désactivation tracking Matomo
|
||||
- **Emails marketing** : désinscription lien footer email
|
||||
|
||||
### 6.6 Droit de limitation
|
||||
|
||||
- **Suspension traitement** pendant contestation exactitude données
|
||||
- Demande via : dpo@roadwave.fr
|
||||
|
||||
### 6.7 Réclamation CNIL
|
||||
|
||||
Si désaccord avec RoadWave :
|
||||
- **CNIL** (Commission Nationale de l'Informatique et des Libertés)
|
||||
- Site : https://www.cnil.fr/fr/plaintes
|
||||
- Adresse : 3 Place de Fontenoy, 75007 Paris
|
||||
|
||||
---
|
||||
|
||||
## 7. Sécurité des données
|
||||
|
||||
### 7.1 Mesures techniques
|
||||
|
||||
- **Chiffrement** : HTTPS (TLS 1.3) pour toutes communications
|
||||
- **Stockage** : bases de données chiffrées au repos (AES-256)
|
||||
- **Mots de passe** : hashage bcrypt (salt + iterations)
|
||||
- **Backups** : quotidiens, chiffrés, stockés UE
|
||||
|
||||
### 7.2 Mesures organisationnelles
|
||||
|
||||
- Accès données restreint (équipe RoadWave uniquement)
|
||||
- Authentification 2FA pour admins
|
||||
- Logs d'accès (audit trail)
|
||||
|
||||
### 7.3 Violation de données
|
||||
|
||||
En cas de fuite de données :
|
||||
- Notification CNIL sous **72h**
|
||||
- Notification utilisateurs concernés (email) si risque élevé
|
||||
- Mesures correctives immédiates
|
||||
|
||||
---
|
||||
|
||||
## 8. Cookies
|
||||
|
||||
### 8.1 Cookies essentiels (pas de consentement requis)
|
||||
|
||||
| Cookie | Finalité | Durée |
|
||||
|--------|----------|-------|
|
||||
| `session_token` | Authentification utilisateur | 30 jours |
|
||||
| `refresh_token` | Renouvellement session | 30 jours |
|
||||
|
||||
### 8.2 Cookies analytics (consentement requis)
|
||||
|
||||
- **Matomo** : analytics self-hosted
|
||||
- Bannière **Tarteaucitron.js** au premier lancement
|
||||
- Refus = aucun cookie analytics
|
||||
|
||||
### 8.3 Aucun cookie tiers
|
||||
|
||||
- Pas de Google Analytics
|
||||
- Pas de Facebook Pixel
|
||||
- Pas de trackers publicitaires tiers
|
||||
|
||||
---
|
||||
|
||||
## 9. Mineurs
|
||||
|
||||
### 9.1 Âge minimum
|
||||
|
||||
- **13 ans minimum** (RGPD)
|
||||
- Vérification : date de naissance à l'inscription
|
||||
|
||||
### 9.2 Mineurs 13-15 ans
|
||||
|
||||
- **Autorisation parentale requise**
|
||||
- Email parent vérifié
|
||||
- Parent peut demander suppression compte mineur
|
||||
|
||||
### 9.3 Mode Kids
|
||||
|
||||
- Activation automatique si <13 ans (détecté via date naissance)
|
||||
- Contenus filtrés : uniquement "Tout public" + tranches d'âge adaptées
|
||||
- Pas de publicités ciblées (conformité COPPA US si expansion)
|
||||
|
||||
---
|
||||
|
||||
## 10. Transferts hors UE
|
||||
|
||||
### 10.1 Principe
|
||||
|
||||
Données hébergées **exclusivement UE** :
|
||||
- Serveurs : Hetzner (Allemagne) ou OVH (France)
|
||||
- Storage : OVH Object Storage (France)
|
||||
- Paiements : Mangopay (Luxembourg)
|
||||
|
||||
### 10.2 Exceptions
|
||||
|
||||
**API tierces potentielles** (post-MVP) :
|
||||
- Si transfert hors UE → clauses contractuelles types (CCT)
|
||||
- Notification utilisateurs avant activation
|
||||
|
||||
---
|
||||
|
||||
## 11. Modifications de la politique
|
||||
|
||||
- Notification **14 jours avant** entrée en vigueur (email)
|
||||
- Version datée disponible : roadwave.fr/confidentialite
|
||||
- Changements majeurs → nouveau consentement requis
|
||||
|
||||
---
|
||||
|
||||
## 12. Contact DPO
|
||||
|
||||
**Délégué à la Protection des Données** :
|
||||
- Email : dpo@roadwave.fr
|
||||
- Délai réponse : **1 mois** (RGPD)
|
||||
|
||||
**Pour toute question RGPD** :
|
||||
1. Email dpo@roadwave.fr
|
||||
2. Objet : "[RGPD] Votre demande"
|
||||
3. Joindre : justificatif identité (si accès/suppression données)
|
||||
233
docs/regles-metier/01-authentification-inscription.md
Normal file
233
docs/regles-metier/01-authentification-inscription.md
Normal file
@@ -0,0 +1,233 @@
|
||||
## 1. Authentification & Inscription
|
||||
|
||||
### 1.1 Méthodes d'inscription
|
||||
|
||||
**Décision** : Email/Password uniquement (pas d'OAuth tiers)
|
||||
|
||||
- ❌ Pas de Google, Apple, Facebook OAuth (dépendance services US/Chine)
|
||||
- ✅ Email + mot de passe
|
||||
- ✅ 2FA (Two-Factor Authentication) disponible
|
||||
- ✅ Option "Appareil de confiance" (skip 2FA pour 30 jours)
|
||||
|
||||
**Justification** :
|
||||
- Souveraineté : pas de dépendance externe
|
||||
- RGPD : données 100% contrôlées
|
||||
- Coût : 0€ (Zitadel intégré)
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Vérification email
|
||||
|
||||
**Décision** : Différenciée selon le rôle utilisateur
|
||||
|
||||
#### Pour les auditeurs (écoute uniquement)
|
||||
|
||||
| État | Capacités |
|
||||
|------|-----------|
|
||||
| **Email non vérifié** | Lecture illimitée + création max 5 contenus |
|
||||
| **Email vérifié** | Toutes fonctionnalités débloquées |
|
||||
|
||||
**Paramètres** :
|
||||
- Lien de vérification expire après **7 jours**
|
||||
- Possibilité de renvoyer le lien (max 3 fois/jour)
|
||||
- Rappel in-app après création du 3ème contenu
|
||||
|
||||
**Justification** :
|
||||
- Friction minimale à l'inscription
|
||||
- Anti-spam sans bloquer l'essai du produit
|
||||
- Incitation naturelle à vérifier (déblocage)
|
||||
|
||||
#### Pour les créateurs (monétisation)
|
||||
|
||||
**Vérification obligatoire sous 7 jours** pour :
|
||||
- Accès au programme de monétisation
|
||||
- KYC et reversement des revenus (conformité Mangopay)
|
||||
- Publication illimitée de contenus
|
||||
|
||||
**Justification** :
|
||||
- **Conformité légale** : KYC obligatoire pour transferts financiers
|
||||
- **Anti-fraude** : Vérification identité réelle pour paiements
|
||||
- **Responsabilité** : RoadWave doit pouvoir prouver identité créateurs monétisés
|
||||
|
||||
---
|
||||
|
||||
### 1.3 Données requises à l'inscription
|
||||
|
||||
**Obligatoires** :
|
||||
- ✅ Email (format validé)
|
||||
- ✅ Mot de passe (voir règles ci-dessous)
|
||||
- ✅ Pseudo (3-30 caractères, alphanumérique + underscore)
|
||||
- ✅ Date de naissance (vérification âge minimum)
|
||||
|
||||
**Optionnelles** :
|
||||
- ❌ Nom complet (privacy by design)
|
||||
- ❌ Photo de profil (avatar par défaut généré)
|
||||
- ❌ Bio (ajout ultérieur)
|
||||
|
||||
**Âge minimum** :
|
||||
- **13 ans minimum** (conformité réglementation réseaux sociaux EU)
|
||||
- Vérification à l'inscription via date de naissance
|
||||
- Blocage inscription si <13 ans avec message explicite
|
||||
|
||||
**Justification** :
|
||||
- RGPD minimal data
|
||||
- Friction réduite (4 champs max)
|
||||
- Protection mineurs (obligation légale)
|
||||
|
||||
---
|
||||
|
||||
### 1.4 Tranches d'âge des contenus
|
||||
|
||||
**Décision** : Classification obligatoire des contenus
|
||||
|
||||
**Catégories** :
|
||||
- 🟢 **Tout public** (défaut)
|
||||
- 🟡 **13+** : contenu mature léger (débats, actualité sensible)
|
||||
- 🟠 **16+** : contenu mature (violence verbale, sujets sensibles)
|
||||
- 🔴 **18+** : contenu adulte (langage explicite, sujets réservés)
|
||||
|
||||
**Règles de diffusion** :
|
||||
- Utilisateur 13-15 ans → contenus 🟢 uniquement
|
||||
- Utilisateur 16-17 ans → contenus 🟢 🟡
|
||||
- Utilisateur 18+ → tous contenus
|
||||
|
||||
**Modération** :
|
||||
- Vérification obligatoire de la classification lors de la validation
|
||||
- Reclassification possible par modérateurs
|
||||
- Strike si classification volontairement incorrecte
|
||||
|
||||
**Justification** :
|
||||
- Protection mineurs (obligation légale)
|
||||
- Responsabilité plateforme
|
||||
- Coût : champ supplémentaire + règle algo
|
||||
|
||||
---
|
||||
|
||||
### 1.5 Validation mot de passe
|
||||
|
||||
**Règles** :
|
||||
- ✅ Minimum **8 caractères**
|
||||
- ✅ Au moins **1 majuscule**
|
||||
- ✅ Au moins **1 chiffre**
|
||||
- ❌ Pas de symbole obligatoire (simplicité)
|
||||
|
||||
**Validation** :
|
||||
- Côté client (feedback temps réel)
|
||||
- Côté backend (sécurité)
|
||||
- Message d'erreur explicite par règle non respectée
|
||||
|
||||
**Justification** :
|
||||
- Standard industrie
|
||||
- Bloque 95% des mots de passe faibles
|
||||
- UX acceptable (pas trop restrictif)
|
||||
|
||||
---
|
||||
|
||||
### 1.6 Two-Factor Authentication (2FA)
|
||||
|
||||
**Décision** : Optionnel mais recommandé
|
||||
|
||||
**Méthodes disponibles** :
|
||||
- ✅ TOTP (Time-based One-Time Password) via app (Google Authenticator, Authy)
|
||||
- ✅ Email (code 6 chiffres, expire 10 min)
|
||||
- ❌ SMS (coût élevé ~0.05€/SMS)
|
||||
|
||||
**Appareil de confiance** :
|
||||
- Option "Ne plus demander sur cet appareil" → bypass 2FA pendant **30 jours**
|
||||
- Révocable depuis paramètres compte
|
||||
- Liste des appareils de confiance visible
|
||||
|
||||
**Justification** :
|
||||
- Sécurité renforcée sans coût SMS
|
||||
- UX : appareil de confiance évite friction quotidienne
|
||||
- Zitadel natif (0€)
|
||||
|
||||
---
|
||||
|
||||
### 1.7 Tentatives de connexion
|
||||
|
||||
**Règles** :
|
||||
- Maximum **5 tentatives** par période de **15 minutes**
|
||||
- Blocage temporaire après 5 échecs
|
||||
- Compteur reset automatique après 15 min
|
||||
- Notification email si blocage (tentative suspecte)
|
||||
|
||||
**Déblocage** :
|
||||
- Automatique après 15 min
|
||||
- Ou via lien "Mot de passe oublié"
|
||||
|
||||
**Justification** :
|
||||
- Anti brute-force
|
||||
- Standard industrie (équilibre sécurité/UX)
|
||||
- Zitadel natif (0€)
|
||||
|
||||
---
|
||||
|
||||
### 1.8 Sessions et refresh tokens
|
||||
|
||||
**Durée de vie** :
|
||||
- **Access token** : 15 minutes
|
||||
- **Refresh token** : 30 jours
|
||||
|
||||
**Rotation** :
|
||||
- Refresh token rotatif (nouveau token à chaque refresh)
|
||||
- Ancien token invalidé immédiatement
|
||||
- Détection token replay attack
|
||||
|
||||
**Extension automatique** :
|
||||
- Si app utilisée, session prolongée automatiquement
|
||||
- Inactivité 30 jours → déconnexion
|
||||
|
||||
**Justification** :
|
||||
- Sécurité (token court-vie)
|
||||
- UX (pas de reconnexion fréquente)
|
||||
- Standard OAuth2/OIDC
|
||||
|
||||
---
|
||||
|
||||
### 1.9 Multi-device
|
||||
|
||||
**Décision** : Sessions simultanées illimitées
|
||||
|
||||
**Gestion** :
|
||||
- Liste des devices connectés visible (OS, navigateur, dernière connexion, IP/ville)
|
||||
- Révocation individuelle possible
|
||||
- Révocation globale "Déconnecter tous les appareils"
|
||||
|
||||
**Alertes** :
|
||||
- Notification push + email si connexion depuis nouveau device
|
||||
- Détection localisation suspecte (IP pays différent)
|
||||
|
||||
**Justification** :
|
||||
- UX maximale (écoute voiture + tablette maison + web)
|
||||
- Sécurité via transparence (utilisateur voit tout)
|
||||
- Coût : table sessions PostgreSQL
|
||||
|
||||
---
|
||||
|
||||
### 1.10 Récupération de compte
|
||||
|
||||
**Méthode** : Email uniquement
|
||||
|
||||
**Processus** :
|
||||
1. Utilisateur clique "Mot de passe oublié"
|
||||
2. Email avec lien de reset envoyé
|
||||
3. Lien expire après **1 heure**
|
||||
4. Page de reset : nouveau mot de passe (validation règles)
|
||||
5. Confirmation + déconnexion tous devices (sauf celui en cours)
|
||||
|
||||
**Notifications** :
|
||||
- Email immédiat si changement mot de passe
|
||||
- Push si changement depuis appareil non reconnu
|
||||
|
||||
**Limite** :
|
||||
- Maximum **3 demandes/heure** (anti-spam)
|
||||
|
||||
**Justification** :
|
||||
- Standard sécurité
|
||||
- Pas de coût SMS
|
||||
- Protection contre attaque sociale
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 1
|
||||
336
docs/regles-metier/02-conformite-rgpd.md
Normal file
336
docs/regles-metier/02-conformite-rgpd.md
Normal file
@@ -0,0 +1,336 @@
|
||||
## 13. Conformité RGPD
|
||||
|
||||
### 13.1 Gestion du consentement
|
||||
|
||||
**Décision** : Tarteaucitron.js + PostgreSQL backend
|
||||
|
||||
**Implémentation web** :
|
||||
- ✅ Tarteaucitron.js (opensource, self-hosted)
|
||||
- ✅ Banner RGPD français, customisable
|
||||
- ✅ Granularité : fonctionnel / analytique / marketing
|
||||
|
||||
**Implémentation backend** :
|
||||
- Table `user_consents` avec versioning
|
||||
- Champs : user_id, consent_type, version, accepted, timestamp
|
||||
- Historique complet conservé (preuve légale)
|
||||
|
||||
**Consentements requis** :
|
||||
- **Géolocalisation précise** : obligatoire (banner + permission OS)
|
||||
- **Analytics** : optionnel (Matomo)
|
||||
- **Notifications push** : optionnel (permission OS)
|
||||
|
||||
**Justification** :
|
||||
- Opensource, 0€, conformité RGPD garantie
|
||||
- Historique backend = preuve légale en cas de contrôle
|
||||
- Granularité conforme recommandations CNIL
|
||||
|
||||
---
|
||||
|
||||
### 13.2 Anonymisation des données GPS
|
||||
|
||||
**Décision** : Geohash après 24h
|
||||
|
||||
**Processus** :
|
||||
1. Données précises conservées **24h** (recommandation personnalisée)
|
||||
2. Après 24h : conversion en geohash précision 5 (~5km²)
|
||||
3. Coordonnées originales supprimées définitivement
|
||||
|
||||
**Implémentation PostGIS** :
|
||||
```sql
|
||||
-- Job quotidien
|
||||
UPDATE location_history
|
||||
SET location = ST_SetSRID(ST_GeomFromGeoHash(ST_GeoHash(location::geography, 5)), 4326)::geography,
|
||||
anonymized = true
|
||||
WHERE created_at < NOW() - INTERVAL '24 hours' AND anonymized = false;
|
||||
```
|
||||
|
||||
**Exceptions** :
|
||||
- ✅ Historique personnel visible (liste trajets) : conservation intégrale tant que compte actif
|
||||
- ❌ Analytics globales : uniquement geohash anonyme
|
||||
|
||||
**Justification** :
|
||||
- Vraie anonymisation RGPD (CNIL compliant)
|
||||
- Permet analytics agrégées (heatmaps trafic)
|
||||
- PostGIS natif, 0€
|
||||
|
||||
---
|
||||
|
||||
### 13.3 Export des données (portabilité)
|
||||
|
||||
**Décision** : JSON + HTML + ZIP, génération asynchrone
|
||||
|
||||
**Contenu de l'export** :
|
||||
```
|
||||
export-roadwave-[user_id]-[date].zip
|
||||
├── export.json # Machine-readable
|
||||
├── index.html # Human-readable (stylé)
|
||||
├── audio/
|
||||
│ ├── content-123.opus
|
||||
│ ├── content-456.opus
|
||||
│ └── ...
|
||||
└── README.txt # Instructions
|
||||
```
|
||||
|
||||
**Données exportées** :
|
||||
- Profil utilisateur (email, pseudo, date inscription, bio)
|
||||
- Historique d'écoute (titres, dates, durées)
|
||||
- Contenus créés (audio + métadonnées)
|
||||
- Abonnements et likes
|
||||
- Centres d'intérêt (jauges)
|
||||
- Historique consentements
|
||||
|
||||
**Processus** :
|
||||
1. Demande via paramètres compte
|
||||
2. Génération asynchrone (worker background)
|
||||
3. Email avec lien download (expire **7 jours**)
|
||||
4. Délai : **48h maximum** (conformité RGPD)
|
||||
|
||||
**Limite** :
|
||||
- Maximum **1 export/mois** (anti-abus)
|
||||
|
||||
**Justification** :
|
||||
- Conformité article 20 RGPD (portabilité)
|
||||
- Double format (human + machine)
|
||||
- Worker asynchrone évite timeout
|
||||
|
||||
---
|
||||
|
||||
### 13.4 Suppression du compte
|
||||
|
||||
**Décision** : Grace period 30j + anonymisation contenus
|
||||
|
||||
**Processus** :
|
||||
1. Utilisateur clique "Supprimer mon compte"
|
||||
2. Compte désactivé immédiatement (login impossible)
|
||||
3. Contenus cachés pendant 30 jours (non diffusés)
|
||||
4. Email confirmation + lien annulation (valide 30j)
|
||||
5. Après 30j sans annulation : suppression effective
|
||||
|
||||
**Suppression effective** :
|
||||
- ✅ Compte utilisateur supprimé (données personnelles)
|
||||
- ✅ Historique d'écoute supprimé
|
||||
- ✅ GPS historique supprimé
|
||||
- ✅ Sessions et tokens révoqués
|
||||
- ⚠️ Contenus créés **anonymisés** (créateur = "Utilisateur supprimé")
|
||||
- ⚠️ Likes et abonnements supprimés (mais compteurs préservés)
|
||||
|
||||
**Contenus conservés anonymement** :
|
||||
- Audio files (CDN)
|
||||
- Métadonnées (titre, description, tags, géolocalisation)
|
||||
- Statistiques d'écoute
|
||||
|
||||
**Justification** :
|
||||
- Grace period évite suppressions impulsives
|
||||
- Anonymisation contenus = intérêt légitime communauté
|
||||
- Conforme RGPD si créateur = donnée supprimée
|
||||
|
||||
---
|
||||
|
||||
### 13.5 Mode dégradé (sans GPS précis)
|
||||
|
||||
**Décision** : GeoIP par défaut, GPS optionnel
|
||||
|
||||
**Niveaux de précision** :
|
||||
|
||||
| Niveau | Technologie | Contenus accessibles | Consentement |
|
||||
|--------|-------------|---------------------|--------------|
|
||||
| **Pays** | Aucune géoloc | Contenus nationaux uniquement | ❌ Non requis |
|
||||
| **Ville** | GeoIP (MaxMind) | Contenus régionaux/ville | ❌ Non requis |
|
||||
| **Précis** | GPS | Tous contenus (hyperlocaux inclus) | ✅ Requis |
|
||||
|
||||
**Implémentation** :
|
||||
- Démarrage app : GeoIP automatique (IP → ville)
|
||||
- Banner in-app : "Activez la géolocalisation pour découvrir du contenu près de chez vous"
|
||||
- Upgrade volontaire vers GPS
|
||||
|
||||
**API GeoIP** :
|
||||
- MaxMind GeoLite2 (gratuit, self-hosted)
|
||||
- Update DB mensuelle automatique
|
||||
- Précision ~80% au niveau ville
|
||||
|
||||
**Justification** :
|
||||
- RGPD : pas de consentement requis pour GeoIP (pas de donnée personnelle)
|
||||
- UX dégradée acceptable (contenus disponibles)
|
||||
- Progressive disclosure (upgrade optionnel)
|
||||
|
||||
---
|
||||
|
||||
### 13.6 Durée de conservation des données
|
||||
|
||||
**Décision** : 5 ans inactivité → purge automatique
|
||||
|
||||
**Règles** :
|
||||
|
||||
| Type de compte | Seuil inactivité | Action |
|
||||
|----------------|------------------|--------|
|
||||
| **Auditeur uniquement** | 5 ans sans connexion | Suppression automatique |
|
||||
| **Créateur avec contenus actifs** | Jamais (tant qu'écoutes) | Conservation indéfinie |
|
||||
| **Créateur inactif** | 5 ans sans connexion + 2 ans sans écoute | Suppression automatique |
|
||||
|
||||
**Notifications avant suppression** :
|
||||
- Email + push : **90 jours** avant
|
||||
- Email + push : **30 jours** avant
|
||||
- Email + push : **7 jours** avant
|
||||
- Toute connexion = reset compteur inactivité
|
||||
|
||||
**Contenu conservé** :
|
||||
- Contenus créés par comptes supprimés (anonymisés) : conservation indéfinie
|
||||
|
||||
**Justification** :
|
||||
- Conformité principe minimisation RGPD
|
||||
- 5 ans = équilibre raisonnable (standard industrie)
|
||||
- Exception créateurs actifs = intérêt légitime plateforme
|
||||
|
||||
---
|
||||
|
||||
### 13.7 Cookies et trackers web
|
||||
|
||||
**Décision** : Matomo self-hosted, zéro cookie tiers
|
||||
|
||||
**Cookies utilisés** :
|
||||
|
||||
| Cookie | Type | Durée | Finalité | Consentement |
|
||||
|--------|------|-------|----------|--------------|
|
||||
| `session` | Technique | 30j | Authentification | ❌ Non requis |
|
||||
| `refresh_token` | Technique | 30j | Session persistante | ❌ Non requis |
|
||||
| `_pk_id` | Analytique | 13 mois | Matomo (IP anonyme) | ✅ Requis |
|
||||
|
||||
**Analytics : Matomo self-hosted** :
|
||||
- Hébergé sur nos serveurs (Docker)
|
||||
- IP anonymisées automatiquement (2 derniers octets)
|
||||
- Pas de cookie si consentement refusé
|
||||
- Alternative : Plausible (SaaS EU, 9€/mois)
|
||||
|
||||
**Trackers interdits** :
|
||||
- ❌ Google Analytics
|
||||
- ❌ Facebook Pixel
|
||||
- ❌ Hotjar, Mixpanel, etc.
|
||||
|
||||
**Justification** :
|
||||
- Souveraineté données (pas de transfert US)
|
||||
- Conformité RGPD max (CNIL compatible)
|
||||
- Matomo = opensource, 0€ infra
|
||||
|
||||
---
|
||||
|
||||
### 13.8 Registre des traitements
|
||||
|
||||
**Décision** : Document Markdown versionné Git (MVP)
|
||||
|
||||
**Emplacement** :
|
||||
- `docs/rgpd/registre-traitements.md`
|
||||
- Versionné Git (historique modifications)
|
||||
|
||||
**Contenu obligatoire par traitement** :
|
||||
- Nom et finalité du traitement
|
||||
- Catégories de données collectées
|
||||
- Base légale (consentement / contrat / intérêt légitime)
|
||||
- Durée de conservation
|
||||
- Destinataires (sous-traitants, CDN, etc.)
|
||||
- Transferts hors UE (aucun prévu)
|
||||
|
||||
**Responsable** :
|
||||
- DPO / Fondateur
|
||||
- Review trimestrielle obligatoire
|
||||
- Update immédiate si nouveau traitement
|
||||
|
||||
**Migration future** :
|
||||
- Si > 100K utilisateurs : interface admin PostgreSQL
|
||||
|
||||
**Justification** :
|
||||
- Obligation RGPD Article 30
|
||||
- Markdown = simple, versionné, auditable
|
||||
- 0€
|
||||
|
||||
---
|
||||
|
||||
### 13.9 Notification violations de données (breach)
|
||||
|
||||
**Décision** : Monitoring + alertes + runbook
|
||||
|
||||
**Détection automatique** :
|
||||
|
||||
| Événement | Outil | Alerte |
|
||||
|-----------|-------|--------|
|
||||
| Erreurs backend critiques | Sentry | Discord/Slack immédiat |
|
||||
| Pic requêtes anormal | Grafana | Email équipe |
|
||||
| Accès non autorisé DB | PostgreSQL logs | SMS fondateur |
|
||||
| Authentification suspecte | Zitadel alerts | Email équipe |
|
||||
|
||||
**Procédure breach** :
|
||||
- Runbook : `docs/rgpd/procedure-breach.md`
|
||||
- Checklist 72h CNIL :
|
||||
1. H+0 : Détection et confinement
|
||||
2. H+24 : Évaluation gravité (données concernées, utilisateurs impactés)
|
||||
3. H+48 : Notification CNIL si risque pour utilisateurs
|
||||
4. H+72 : Notification utilisateurs si risque élevé
|
||||
|
||||
**Contact CNIL** :
|
||||
- Email pré-rédigé (template)
|
||||
- Formulaire en ligne (account CNIL créé)
|
||||
|
||||
**Justification** :
|
||||
- Obligation RGPD Article 33 (notification 72h)
|
||||
- Monitoring proactif évite découverte tardive
|
||||
- Sentry gratuit < 5K events/mois
|
||||
|
||||
---
|
||||
|
||||
### 13.10 DPO (Délégué à la Protection des Données)
|
||||
|
||||
**Décision** : Fondateur = DPO temporaire (MVP)
|
||||
|
||||
**Raison légale** :
|
||||
- Non obligatoire si :
|
||||
- < 250 employés
|
||||
- Pas de traitement à grande échelle de données sensibles
|
||||
- RoadWave : données localisation = sensible MAIS échelle MVP
|
||||
|
||||
**Formation** :
|
||||
- CNIL : formation gratuite en ligne (4h)
|
||||
- Certification CNIL "Atelier RGPD" (gratuit)
|
||||
|
||||
**Contact** :
|
||||
- Email : dpo@roadwave.fr
|
||||
- Publié dans CGU et mentions légales
|
||||
- Délai réponse : **1 mois** (RGPD)
|
||||
|
||||
**Migration future** :
|
||||
- Si > 100K utilisateurs : DPO externe mutualisé (~200€/mois)
|
||||
- Ou recrutement DPO interne si > 10 employés
|
||||
|
||||
**Justification** :
|
||||
- Conforme RGPD (non obligatoire en phase MVP)
|
||||
- 0€, contrôle total
|
||||
- Bonne pratique : avoir un contact identifié
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 13
|
||||
|
||||
| Mesure | Implémentation | Coût |
|
||||
|--------|----------------|------|
|
||||
| **Consentement** | Tarteaucitron.js + PostgreSQL | 0€ |
|
||||
| **Anonymisation GPS** | Geohash PostGIS (24h) | 0€ |
|
||||
| **Export données** | JSON+HTML+ZIP asynchrone | 0€ |
|
||||
| **Suppression compte** | Grace period 30j + anonymisation | 0€ |
|
||||
| **Mode dégradé** | GeoIP MaxMind + GPS optionnel | 0€ |
|
||||
| **Conservation** | Purge auto 5 ans inactivité | 0€ |
|
||||
| **Analytics** | Matomo self-hosted | ~5€/mois |
|
||||
| **Registre traitements** | Markdown Git | 0€ |
|
||||
| **Breach detection** | Sentry + Grafana + runbook | 0€ |
|
||||
| **DPO** | Fondateur formé CNIL | 0€ |
|
||||
|
||||
**Coût total RGPD : ~5€/mois**
|
||||
|
||||
---
|
||||
|
||||
## Points d'attention pour Gherkin
|
||||
|
||||
- Tester consentement géolocalisation (accept/refuse → contenus différents)
|
||||
- Tester anonymisation GPS après 24h (job cron)
|
||||
- Tester export données (génération complète + vérification contenu)
|
||||
- Tester grace period suppression (annulation possible)
|
||||
- Tester mode GeoIP (ville détectée correctement)
|
||||
- Tester purge automatique (5 ans inactivité)
|
||||
- Tester notifications avant purge (90j/30j/7j)
|
||||
141
docs/regles-metier/03-centres-interet-jauges.md
Normal file
141
docs/regles-metier/03-centres-interet-jauges.md
Normal file
@@ -0,0 +1,141 @@
|
||||
## 3. Centres d'intérêt et jauges
|
||||
|
||||
### 3.1 Évolution des jauges
|
||||
|
||||
**Décision** : Système simple avec valeurs fixes
|
||||
|
||||
| Action | Impact jauge | Justification |
|
||||
|--------|--------------|---------------|
|
||||
| **Like automatique renforcé (≥80% écoute)** | +2% | Signal fort d'intérêt (écoute quasi-complète) |
|
||||
| **Like automatique standard (30-79% écoute)** | +1% | Signal modéré d'intérêt |
|
||||
| **Like explicite (manuel)** | +2% | Signal fort, cumulable avec auto |
|
||||
| **Abonnement créateur** | +5% sur tous ses tags | Signal très fort d'affinité |
|
||||
| **Skip rapide (<10s)** | -0.5% | Désintérêt marqué |
|
||||
| **Skip tardif (≥30%)** | 0% | Neutre (contenu essayé suffisamment) |
|
||||
|
||||
**Paramètres techniques** :
|
||||
- Les jauges sont bornées strictement entre **0% et 100%**
|
||||
- Calcul immédiat à chaque action (pas de batch différé)
|
||||
- Les tags du contenu sont définis par le créateur à la publication
|
||||
- Si un contenu a plusieurs tags, chaque jauge correspondante est impactée
|
||||
|
||||
**Exemple de calcul** :
|
||||
```
|
||||
Contenu de 5 minutes tagué "Automobile" + "Voyage"
|
||||
|
||||
Scénario 1 : Écoute 4min30 (90%)
|
||||
→ Like automatique renforcé (+2%)
|
||||
→ Jauge Automobile : 45% → 47%
|
||||
→ Jauge Voyage : 60% → 62%
|
||||
|
||||
Scénario 2 : Écoute 2min30 (50%)
|
||||
→ Like automatique standard (+1%)
|
||||
→ Jauge Automobile : 45% → 46%
|
||||
→ Jauge Voyage : 60% → 61%
|
||||
|
||||
Scénario 3 : Écoute 2min30 (50%) + Like manuel
|
||||
→ Like auto +1% puis like manuel +2% = +3% total
|
||||
→ Jauge Automobile : 45% → 48%
|
||||
→ Jauge Voyage : 60% → 63%
|
||||
|
||||
Scénario 4 : Skip après 5s
|
||||
→ Signal négatif (-0.5%)
|
||||
→ Jauge Automobile : 45% → 44.5%
|
||||
→ Jauge Voyage : 60% → 59.5%
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- **Like automatique** : Reflète l'engagement réel (voir [ADR-010](../adr/010-commandes-volant.md))
|
||||
- **Sécurité routière** : Pas d'action complexe en conduite
|
||||
- **Prévisibilité** : Règles claires et déterministes
|
||||
- **Coût minimal** : Calculs simples en backend
|
||||
- **Fiabilité** : Pas d'edge cases complexes
|
||||
- **Ajustable** : Valeurs modifiables via dashboard admin si besoin
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Jauge initiale
|
||||
|
||||
**Décision** : Démarrage neutre à 50%, pas de questionnaire
|
||||
|
||||
**À l'inscription** :
|
||||
- Toutes les jauges d'intérêt sont initialisées à **50%**
|
||||
- Pas de questionnaire onboarding (friction zéro)
|
||||
- L'algorithme apprend naturellement via les premières écoutes
|
||||
|
||||
**Catégories disponibles** :
|
||||
- Automobile
|
||||
- Voyage
|
||||
- Famille
|
||||
- Amour
|
||||
- Musique
|
||||
- Économie
|
||||
- Cryptomonnaie
|
||||
- Politique
|
||||
- Culture générale
|
||||
- Sport
|
||||
- Technologie
|
||||
- Santé
|
||||
- *... (extensible)*
|
||||
|
||||
**Cold start (premiers jours)** :
|
||||
1. Nouvel utilisateur s'inscrit → toutes jauges à 50%
|
||||
2. Écoute premier podcast "Automobile" → jauge Auto monte à 51%
|
||||
3. Skip un contenu "Économie" → jauge Éco descend à 48%
|
||||
4. Après 10-15 écoutes, profil commence à se dessiner clairement
|
||||
|
||||
**Alternative optionnelle (post-MVP)** :
|
||||
- Questionnaire **optionnel** proposé après 3 écoutes (in-app)
|
||||
- Message : "Améliorez vos recommandations en sélectionnant vos centres d'intérêt"
|
||||
- Si rempli : jauges sélectionnées passent à 70%, non sélectionnées à 30%
|
||||
- Si skip : conserve 50% partout
|
||||
|
||||
**Justification** :
|
||||
- **Inscription ultra-rapide** : pas de questionnaire = moins de churn
|
||||
- **Découverte naturelle** : l'algorithme apprend en quelques écoutes
|
||||
- **Équitable** : pas de biais initial vers certains créateurs
|
||||
- **Comportement déterministe** : facile à tester et débugger
|
||||
- **Cold start acceptable** : à 50%, tous les contenus ont une chance égale initialement
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Dégradation temporelle
|
||||
|
||||
**Décision** : Pas de dégradation automatique
|
||||
|
||||
Les jauges **ne diminuent jamais** avec le temps de manière automatique.
|
||||
|
||||
**Règle** :
|
||||
- Une jauge ne change **que par les actions utilisateur** (like, écoute, skip)
|
||||
- Pas de cron job de dégradation périodique
|
||||
- Pas de "rafraîchissement" artificiel
|
||||
|
||||
**Scénario illustratif** :
|
||||
```
|
||||
Utilisateur aimait "Économie" (jauge 80%) il y a 1 an
|
||||
→ Depuis, skip tous les contenus Éco
|
||||
→ Jauge descend naturellement à 40% via les skips
|
||||
→ Pas besoin de dégradation temporelle
|
||||
```
|
||||
|
||||
**Si utilisateur inactif longtemps** :
|
||||
- Utilisateur part en vacances 6 mois → jauges conservées
|
||||
- Au retour : ses jauges reflètent toujours ses goûts d'avant
|
||||
- Comportement cohérent et prévisible
|
||||
|
||||
**Alternative utilisateur (contrôle explicite)** :
|
||||
- Bouton "Réinitialiser mes centres d'intérêt" dans paramètres
|
||||
- Action manuelle : remet toutes les jauges à 50%
|
||||
- Permet nouveau départ si souhaité (changement de vie, etc.)
|
||||
|
||||
**Justification** :
|
||||
- **Principe KISS** (Keep It Simple, Stupid)
|
||||
- **Coût 0** : pas de batch nocturne, pas de calculs temporels
|
||||
- **Fiabilité maximale** : pas de bugs de fuseaux horaires, dates, etc.
|
||||
- **UX prévisible** : jauge = reflet des actions, pas d'automatisme caché
|
||||
- **Respect historique** : si utilisateur aimait X depuis 2 ans, pourquoi "oublier" ?
|
||||
- **Évolution naturelle** : les actions récentes suffisent à faire évoluer les jauges
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 3
|
||||
336
docs/regles-metier/04-algorithme-recommandation.md
Normal file
336
docs/regles-metier/04-algorithme-recommandation.md
Normal file
@@ -0,0 +1,336 @@
|
||||
## 2. Algorithme de recommandation
|
||||
|
||||
### 2.1 Classification de géo-pertinence
|
||||
|
||||
**Décision** : 3 types de contenus selon leur pertinence géographique
|
||||
|
||||
| Type | Description | Exemple | Pondération géo |
|
||||
|------|-------------|---------|-----------------|
|
||||
| **Géo-ancré** | Contenu lié à un lieu précis | Audio-guide monument, pub restaurant local | 70% |
|
||||
| **Géo-contextuel** | Pertinent dans une zone | Actualité régionale, événement local | 50% |
|
||||
| **Géo-neutre** | Universel, pas de lien géo | Podcast philosophie, musique | 20% |
|
||||
|
||||
**Qui décide** :
|
||||
- ✅ Créateur choisit le type à la publication
|
||||
- ✅ Modération peut reclassifier après validation
|
||||
- ✅ Modification possible après publication (tout le monde a le droit de se tromper)
|
||||
|
||||
**Justification** :
|
||||
- Différencie audio-guide (hyper-local) des podcasts génériques
|
||||
- Algorithme adapte automatiquement la pondération
|
||||
- Coût : champ supplémentaire en DB + règle algo
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Formule de scoring
|
||||
|
||||
**Décision** : Score combiné dynamique selon type de contenu
|
||||
|
||||
```
|
||||
score_final = (score_geo * poids_geo_type)
|
||||
+ (score_interets * poids_interets_type)
|
||||
+ (score_engagement * 0.2)
|
||||
+ (bonus_aleatoire)
|
||||
|
||||
où :
|
||||
- score_geo = 1 - (distance_km / distance_max_km)
|
||||
- score_interets = moyenne des jauges utilisateur pour les tags du contenu
|
||||
- score_engagement = (taux_completion * 0.5) + (ratio_likes * 0.3) + (ratio_abonnements * 0.2)
|
||||
- bonus_aleatoire = 10% des recommandations tirées aléatoirement
|
||||
```
|
||||
|
||||
**Pondérations par type** :
|
||||
|
||||
| Type | Poids géo | Poids intérêts |
|
||||
|------|-----------|----------------|
|
||||
| Géo-ancré | 0.7 | 0.1 |
|
||||
| Géo-contextuel | 0.5 | 0.3 |
|
||||
| Géo-neutre | 0.2 | 0.6 |
|
||||
|
||||
**Paramètres** :
|
||||
- Distance max recommandée : **200 km**
|
||||
- Dégradation : **linéaire** (1 - distance/200km)
|
||||
- Rayon point GPS : **500m** (adapté au volume de contenu local)
|
||||
|
||||
**Tous ces paramètres sont configurables à chaud via interface admin.**
|
||||
|
||||
**Justification** :
|
||||
- Flexibilité totale selon type de contenu
|
||||
- Linéaire = rattrapage naturel du contenu viral ancien
|
||||
- Auditable via métriques engagement (moyenne/médiane)
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Score d'engagement et popularité
|
||||
|
||||
**Décision** : Intégration popularité avec poids 0.2
|
||||
|
||||
**Métriques** :
|
||||
- **Taux de complétion** : écoutes >80% / total écoutes (poids 0.5)
|
||||
- **Ratio likes** : likes / écoutes (poids 0.3)
|
||||
- **Ratio abonnements** : nouveaux abonnés après écoute / écoutes (poids 0.2)
|
||||
|
||||
**Seuil minimum** :
|
||||
- Minimum **50 écoutes** avant de considérer l'engagement
|
||||
- Contenu <50 écoutes : score engagement = 0.5 (neutre)
|
||||
|
||||
**Contenu viral** :
|
||||
- Un contenu viral à Paris **peut** être proposé à Marseille
|
||||
- Score géo faible compensé par score engagement élevé
|
||||
- Paramétrable admin
|
||||
|
||||
**Dépréciation temporelle** :
|
||||
- Pas de dépréciation automatique
|
||||
- Ratio linéaire = contenu ancien mais toujours apprécié reste pertinent
|
||||
|
||||
**Justification** :
|
||||
- Équilibre découverte / qualité
|
||||
- Pas de pénalisation arbitraire des contenus anciens
|
||||
- Coût : calculs sur métriques existantes
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Part d'aléatoire (exploration)
|
||||
|
||||
**Décision** : 10% par défaut, paramétrable utilisateur
|
||||
|
||||
**Fonctionnement** :
|
||||
- 1 contenu sur 10 = tirage aléatoire (hors historique déjà écouté)
|
||||
- Utilisateur peut ajuster : curseur 0% (aucun aléatoire) à 50% (exploration max)
|
||||
|
||||
**Curseur utilisateur** :
|
||||
- 🎯 **0%** : Personnalisé max (recommandations strictes)
|
||||
- ⚖️ **10%** : Équilibré (défaut)
|
||||
- 🎲 **30%** : Découverte élevée
|
||||
- 🌍 **50%** : Découverte max (équivaut à national = découverte)
|
||||
|
||||
**Justification** :
|
||||
- Évite la bulle de filtre
|
||||
- Laisse l'utilisateur maître de son expérience
|
||||
- Coût : variable aléatoire en algo
|
||||
|
||||
---
|
||||
|
||||
### 2.5 Contenu politique (version MVP simplifiée)
|
||||
|
||||
> ⚠️ **Note** : La classification politique avancée (échelle gauche/droite, équilibrage imposé) a été reportée post-MVP. Voir [ANNEXE-POST-MVP.md](ANNEXE-POST-MVP.md) pour la version complète.
|
||||
|
||||
**Décision MVP** : Tag simple "Politique" sans classification idéologique
|
||||
|
||||
**Tagging** :
|
||||
- Créateur peut taguer son contenu comme "Politique" (optionnel)
|
||||
- Tag "Politique" au même niveau que "Économie", "Sport", "Culture", etc.
|
||||
- **Pas de classification gauche/droite**
|
||||
- **Pas d'équilibrage imposé**
|
||||
|
||||
**Filtrage utilisateur** :
|
||||
- Option paramètres : **"Masquer contenu politique"**
|
||||
- Si activé → 0% de contenus tagués "Politique" dans le feed
|
||||
- Par défaut : désactivé (tous contenus visibles)
|
||||
|
||||
**Justification MVP** :
|
||||
- **Simplicité** : Pas de modération politique coûteuse (~2000€/mois économisés)
|
||||
- **Neutralité technique** : Aucun jugement éditorial sur orientation
|
||||
- **Risque minimal** : Évite controverses et contentieux DSA au lancement
|
||||
- **Fonctionnel** : Utilisateurs peuvent filtrer si souhaité
|
||||
|
||||
**Post-MVP** :
|
||||
- Classification avancée possible si forte demande utilisateurs
|
||||
- Nécessite ressources modération dédiées et audit DSA
|
||||
|
||||
---
|
||||
|
||||
### 2.6 Mode Kids (13-15 ans)
|
||||
|
||||
**Décision** : Mode optionnel pour adolescents 13-15 ans uniquement
|
||||
|
||||
> ⚠️ **Note** : Âge minimum d'inscription = **13 ans** (obligation légale EU). Pas d'utilisateurs <13 ans sur la plateforme.
|
||||
|
||||
**Tranche concernée** :
|
||||
|
||||
| Tranche | Description | Contenus autorisés | Restrictions |
|
||||
|---------|-------------|-------------------|--------------|
|
||||
| **13-15 ans** | Collège | Contenus "Tous publics" uniquement | Filtrage 16+ et 18+ |
|
||||
|
||||
**Activation** :
|
||||
- ❌ **Pas d'activation automatique** (tous les utilisateurs ont ≥13 ans)
|
||||
- ✅ **Activation manuelle** via toggle paramètres
|
||||
- ✅ Parents peuvent activer pour leurs enfants 13-15 ans
|
||||
- ✅ Utilisateur peut désactiver à tout moment
|
||||
|
||||
**Filtrage quand Mode Kids activé** :
|
||||
- ✅ Contenus "Tous publics" uniquement
|
||||
- ❌ Exclusion contenus 16+ et 18+
|
||||
- ❌ Pas de contenu politique (automatiquement filtré)
|
||||
- ❌ Pas de publicité (ou uniquement pub validée manuellement)
|
||||
|
||||
**Interface** :
|
||||
- Interface standard (pas d'interface dédiée enfants pour MVP)
|
||||
- Filtrage algorithmique des contenus inappropriés
|
||||
|
||||
**Justification** :
|
||||
- **Conformité légale** : Âge minimum 13 ans (RGPD, DSA)
|
||||
- **Simplicité MVP** : Un seul mode optionnel vs 4 tranches d'âge
|
||||
- **Protection mineurs** : Filtrage contenus adultes pour 13-15 ans
|
||||
- **Flexibilité** : Parents décident d'activer ou non
|
||||
|
||||
---
|
||||
|
||||
### 2.7 Déclenchement géographique
|
||||
|
||||
**Décision** : Notification au passage, pas d'anticipation
|
||||
|
||||
**Fonctionnement** :
|
||||
1. Utilisateur passe à <500m d'un point GPS (contenu géo-ancré)
|
||||
2. **Notification sonore** (bip court) + **visuelle** (logo selon type)
|
||||
3. Types de logos : 📍 Info, 🏛️ Culturel, 🍴 Commercial, 🎭 Événement
|
||||
4. Délai réaction utilisateur : **5 secondes** pour accepter (bouton volant ou commande vocale)
|
||||
5. Si accepté → lecture immédiate
|
||||
6. Si ignoré → contenu proposé normalement en file d'attente
|
||||
|
||||
**Publicités** :
|
||||
- ⚠️ **Jamais d'interruption** de contenu en cours
|
||||
- Pub s'intercale **entre deux séquences** uniquement
|
||||
- Notification pub : son différent (facultatif selon paramètres)
|
||||
|
||||
**Gestion demi-tour** :
|
||||
- Si utilisateur repart du point après notification → pas de nouvelle notification (déjà proposé)
|
||||
- Réinitialisation après 24h
|
||||
|
||||
**Justification** :
|
||||
- Respect écoute en cours (pas de coupure brutale)
|
||||
- UX fluide (utilisateur garde contrôle)
|
||||
- Simplicité technique (pas de prédiction trajectoire)
|
||||
|
||||
---
|
||||
|
||||
### 2.8 Historique et repropositon
|
||||
|
||||
**Décision** : Pas de reproposition sauf contenu partiel
|
||||
|
||||
**Règles** :
|
||||
|
||||
| État écoute | Completion | Action |
|
||||
|-------------|------------|--------|
|
||||
| **Écouté complètement** | >80% | ❌ Ne jamais reproposer (sauf flag `replayable = true` pour audio-guides) |
|
||||
| **Skippé rapidement** | <10s | ❌ Ne pas reproposer |
|
||||
| **Partiellement écouté** | 10-80% | ✅ Reproposer avec reprise position (`last_position_seconds`) |
|
||||
|
||||
**Stockage historique** :
|
||||
- Table `user_content_history` (user_id, content_id, completion_rate, last_position, listened_at)
|
||||
- Historique **illimité** (PostgreSQL)
|
||||
- Algorithme considère les **100 derniers** pour optimisation requêtes
|
||||
- Export complet disponible (RGPD)
|
||||
|
||||
**Justification** :
|
||||
- Découverte maximale (pas de redites)
|
||||
- Respect erreurs de clic (contenu partiel = 2nde chance)
|
||||
- Coût stockage négligeable (PostgreSQL scalable)
|
||||
|
||||
---
|
||||
|
||||
### 2.9 Paramétrabilité admin (interface dashboard)
|
||||
|
||||
**Décision** : Tous paramètres scoring exposés + A/B testing
|
||||
|
||||
**Paramètres configurables à chaud** :
|
||||
|
||||
| Paramètre | Plage | Défaut | Unité |
|
||||
|-----------|-------|--------|-------|
|
||||
| `poids_geo_ancre` | 0.5 - 1.0 | 0.7 | % |
|
||||
| `poids_geo_contextuel` | 0.3 - 0.7 | 0.5 | % |
|
||||
| `poids_geo_neutre` | 0.0 - 0.4 | 0.2 | % |
|
||||
| `poids_engagement` | 0.0 - 0.5 | 0.2 | % |
|
||||
| `part_aleatoire_global` | 0.0 - 0.3 | 0.1 | % |
|
||||
| `distance_max_km` | 50 - 500 | 200 | km |
|
||||
| `rayon_gps_point_m` | 100 - 2000 | 500 | m |
|
||||
| `seuil_min_ecoutes_engagement` | 10 - 200 | 50 | nb |
|
||||
|
||||
**Application changements** :
|
||||
- Immédiat : nouveaux calculs utilisent nouvelle config
|
||||
- Aucun recalcul batch (coût CPU)
|
||||
- Version config trackée (git-like)
|
||||
- Rollback 1 clic
|
||||
|
||||
**A/B Testing** :
|
||||
- Création variantes (Config A vs Config B)
|
||||
- Split utilisateurs 50/50 aléatoire
|
||||
- Métriques comparatives : taux complétion, engagement, session duration
|
||||
- Dashboard graphique temps réel
|
||||
|
||||
**Audit engagement** :
|
||||
- Métriques clés : moyenne/médiane temps d'écoute par session
|
||||
- Graphiques : évolution engagement selon config
|
||||
- Export CSV pour analyse externe
|
||||
|
||||
**Justification** :
|
||||
- Optimisation continue sans redéploiement
|
||||
- Data-driven decisions (métriques objectives)
|
||||
- Coût : dashboard admin à développer (one-time)
|
||||
|
||||
---
|
||||
|
||||
### 2.10 Paramétrabilité utilisateur
|
||||
|
||||
**Décision** : Curseurs avancés avec profils sauvegardables
|
||||
|
||||
**Niveaux de personnalisation** :
|
||||
|
||||
**Curseurs disponibles** :
|
||||
- 📍 **Géolocalisation** : Local ← slider → National (découverte = national)
|
||||
- 🎲 **Découverte** : 0% ← slider → 50% (part aléatoire)
|
||||
- ⚖️ **Politique** : Masquer / Équilibré / Mes préférences
|
||||
|
||||
**Profils sauvegardables** :
|
||||
- 🚗 Trajet quotidien (boulot) : géo local, découverte 5%, politique masqué
|
||||
- 🛣️ Road trip : géo régional, découverte 30%, politique équilibré
|
||||
- 👶 Enfants : Mode Kids activé
|
||||
|
||||
**Synchronisation** :
|
||||
- ✅ Sync profils entre devices (cloud PostgreSQL)
|
||||
- ❌ Pas de partage profils entre utilisateurs (famille)
|
||||
- Auto-switch selon context (détection trajet récurrent via GPS)
|
||||
|
||||
**Sécurité conduite** :
|
||||
- ⚠️ **Blocage modification si vitesse GPS >10 km/h**
|
||||
- Warning au lancement app : "Configurez avant de prendre la route"
|
||||
- Modifications uniquement app arrêtée/passager
|
||||
|
||||
**Justification** :
|
||||
- Utilisateur maître de son expérience
|
||||
- Contextes d'usage différents (quotidien vs voyage)
|
||||
- Sécurité routière (pas de distraction)
|
||||
|
||||
---
|
||||
|
||||
### 2.11 Médias traditionnels
|
||||
|
||||
**Décision** : Ouverture aux médias établis
|
||||
|
||||
**Médias autorisés** :
|
||||
- Presse nationale : Le Monde, Le Parisien, Libération, Le Figaro, etc.
|
||||
- Radios : France Inter, RTL, Europe 1, etc.
|
||||
- Médias régionaux : Ouest-France, Sud-Ouest, etc.
|
||||
|
||||
**Format contenus** :
|
||||
- Flashs info géolocalisés (actualité régionale)
|
||||
- Chroniques thématiques (culture, économie, sport)
|
||||
- Éditos et débats (classification politique appliquée)
|
||||
|
||||
**Validation** :
|
||||
- Compte média vérifié (badge ✓)
|
||||
- Pas de validation 3 premiers contenus (confiance établie)
|
||||
- Modération a posteriori uniquement
|
||||
|
||||
**Monétisation** :
|
||||
- Partage revenus pub standard (même conditions créateurs)
|
||||
- Possibilité sponsoring direct (pas via plateforme)
|
||||
|
||||
**Justification** :
|
||||
- Crédibilité plateforme (contenus professionnels)
|
||||
- Diversité éditoriale
|
||||
- Attractivité grand public (noms reconnus)
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 2
|
||||
516
docs/regles-metier/05-interactions-navigation.md
Normal file
516
docs/regles-metier/05-interactions-navigation.md
Normal file
@@ -0,0 +1,516 @@
|
||||
## 5. Interactions et navigation
|
||||
|
||||
### 5.1 File d'attente et commande "Suivant"
|
||||
|
||||
**Décision** : Pré-calcul 5 contenus avec insertion prioritaire pour points géographiques
|
||||
|
||||
**File d'attente** :
|
||||
- **5 contenus pré-calculés** en cache (Redis)
|
||||
- Recalcul automatique si :
|
||||
- Déplacement >10km
|
||||
- Toutes les 10 minutes (rafraîchissement contenu)
|
||||
- File d'attente <3 contenus restants
|
||||
|
||||
**Insertion prioritaire géo-ancrée (mode voiture uniquement)** :
|
||||
|
||||
**Détection** :
|
||||
- Calcul ETA (Estimated Time of Arrival) via API GPS native iOS/Android
|
||||
- Notification déclenchée **7 secondes avant** d'arriver au point GPS
|
||||
- Si vitesse < 5 km/h ET distance < 50m → notification immédiate
|
||||
- ⚠️ **App doit être ouverte** (pas de détection en arrière-plan en mode voiture)
|
||||
|
||||
**Notification** :
|
||||
- **Sonore uniquement** : bip court ou son personnalisé RoadWave
|
||||
- **Visuelle minimale** : icône selon type de contenu (🏛️ culture, 👨👩👧 famille, 🎵 musique, etc.)
|
||||
- **Compteur visible** : 7...6...5...4...3...2...1 (décompte des secondes)
|
||||
- **Pas de texte affiché** (éviter distraction conducteur)
|
||||
- **Pas de bouton "Annuler"** : seul le bouton "Suivant" permet validation
|
||||
|
||||
**Actions utilisateur** :
|
||||
1. User entend notification sonore + voit icône et compteur
|
||||
2. User appuie "Suivant" dans les 7 secondes → décompte 5s démarre
|
||||
3. Pendant décompte : contenu actuel continue, compteur visible (5...4...3...2...1)
|
||||
4. Si contenu actuel se termine pendant décompte → contenu suivant du buffer démarre
|
||||
5. À la fin du décompte → contenu géolocalisé démarre (fade out/in 0.3s)
|
||||
|
||||
**Si user n'appuie pas sur "Suivant"** :
|
||||
- Notification disparaît après 7 secondes
|
||||
- Contenu géolocalisé est perdu (pas d'insertion dans file)
|
||||
- Pas de nouveau contenu géolocalisé pendant **10 minutes** (éviter spam)
|
||||
|
||||
**Limitation anti-spam** :
|
||||
- Maximum **6 contenus géolocalisés par heure**
|
||||
- Timer reset toutes les heures (rolling window)
|
||||
- Exception : séquences d'un même audio-guide multi-séquences (comptent comme 1)
|
||||
- Si quota atteint : notifications suivantes ignorées jusqu'à libération du quota
|
||||
|
||||
**Invalidation immédiate** :
|
||||
- Utilisateur change ses préférences (curseurs géo/découverte/politique)
|
||||
- ⚠️ **Modification bloquée si vitesse GPS >10 km/h** (sécurité routière)
|
||||
- Live démarre d'un créateur suivi dans la zone
|
||||
|
||||
**Implémentation** :
|
||||
```
|
||||
Redis cache :
|
||||
- Clé : user:{user_id}:queue
|
||||
- Structure : [content_1, content_2, ..., content_5]
|
||||
- Métadonnées : {last_lat, last_lon, computed_at, mode: "voiture"|"pieton"}
|
||||
- TTL : 15 minutes
|
||||
|
||||
Tracking GPS temps réel (mobile) :
|
||||
- Vérification toutes les 1 seconde
|
||||
- Calcul ETA vers points géolocalisés proches (rayon 500m)
|
||||
- Si ETA ≤ 7s → trigger notification
|
||||
- Historique GPS : 30 derniers points pour calcul vitesse moyenne
|
||||
|
||||
Quota anti-spam (Redis) :
|
||||
- Clé : user:{user_id}:geo_quota
|
||||
- Structure : sorted set avec timestamps des 6 derniers contenus
|
||||
- TTL : 1 heure
|
||||
- Vérification avant notification : ZCOUNT pour compter contenus dernière heure
|
||||
|
||||
Cooldown après ignorance (Redis) :
|
||||
- Clé : user:{user_id}:geo_cooldown
|
||||
- TTL : 10 minutes
|
||||
- Set après notification ignorée
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- **Expérience fluide** : pas de latence au clic "Suivant"
|
||||
- **Réactivité géo** : contenu local inséré immédiatement
|
||||
- **Coût optimisé** : recalcul uniquement si nécessaire
|
||||
- **Sécurité** : pas de modification en conduite
|
||||
|
||||
---
|
||||
|
||||
### 5.1.2 Mode piéton (audio-guides)
|
||||
|
||||
**Décision** : Notifications push en arrière-plan avec rayon large
|
||||
|
||||
**Contexte** :
|
||||
- Mode piéton détecté automatiquement si vitesse moyenne < 5 km/h
|
||||
- Cas d'usage : visites à pied, musées, monuments, quartiers historiques
|
||||
- User n'a pas besoin d'avoir l'app ouverte
|
||||
- ⚠️ **Fonctionnalité optionnelle** : requiert permission "localisation en arrière-plan" (activée par user)
|
||||
|
||||
**Détection** :
|
||||
- App peut être en arrière-plan (si permission accordée)
|
||||
- Rayon de détection : **200 mètres** autour du point GPS
|
||||
- Geofencing iOS/Android pour minimiser consommation batterie
|
||||
- Permission demandée uniquement si user active "Notifications audio-guides piéton" dans settings
|
||||
|
||||
**Notification push système** :
|
||||
|
||||
Format :
|
||||
```
|
||||
Titre : "Audio-guide à proximité"
|
||||
Body : "[Nom du contenu] - [Nom créateur]"
|
||||
Action : Tap → ouvre app sur le contenu
|
||||
```
|
||||
|
||||
Exemple :
|
||||
```
|
||||
Audio-guide à proximité
|
||||
Musée du Louvre : La Joconde - @paris_museum
|
||||
```
|
||||
|
||||
**Permissions requises** :
|
||||
|
||||
⚠️ **Important** : Permission "Always Location" est **optionnelle** et demandée uniquement si user active le mode piéton dans settings.
|
||||
|
||||
iOS (`Info.plist`) :
|
||||
```xml
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>RoadWave utilise votre position pour vous proposer des contenus audio géolocalisés adaptés à votre trajet en temps réel.</string>
|
||||
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>Si vous activez les notifications audio-guides piéton, RoadWave peut vous alerter lorsque vous passez près d'un monument ou musée, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée à tout moment dans les réglages.</string>
|
||||
```
|
||||
|
||||
Android (`AndroidManifest.xml`) :
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
```
|
||||
|
||||
**Disclosure avant demande permission** (Android requis, iOS recommandé) :
|
||||
|
||||
Écran affiché avant demande permission "Always Location" :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ 📍 Notifications audio-guides piéton │
|
||||
├────────────────────────────────────────┤
|
||||
│ Pour vous alerter d'audio-guides à │
|
||||
│ proximité même quand vous marchez avec │
|
||||
│ l'app fermée, RoadWave a besoin de │
|
||||
│ votre position en arrière-plan. │
|
||||
│ │
|
||||
│ Votre position sera utilisée pour : │
|
||||
│ ✅ Détecter monuments à 200m │
|
||||
│ ✅ Vous envoyer une notification │
|
||||
│ │
|
||||
│ Votre position ne sera jamais : │
|
||||
│ ❌ Vendue à des tiers │
|
||||
│ ❌ Utilisée pour de la publicité │
|
||||
│ │
|
||||
│ Cette fonctionnalité est optionnelle. │
|
||||
│ Vous pouvez utiliser RoadWave sans │
|
||||
│ cette permission. │
|
||||
│ │
|
||||
│ [Continuer] [Non merci] │
|
||||
│ │
|
||||
│ Plus d'infos : Politique confidentialité│
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Si user refuse** :
|
||||
- Mode piéton désactivé (uniquement mode voiture disponible)
|
||||
- App fonctionne normalement avec permission "When In Use"
|
||||
- Audio-guides accessibles en mode manuel (user ouvre app, sélectionne contenu)
|
||||
|
||||
**Comportement après tap sur notification** :
|
||||
1. User tap notification push
|
||||
2. App s'ouvre sur la page du contenu
|
||||
3. User peut démarrer la lecture manuellement
|
||||
4. Navigation libre (voir section 16.2 pour audio-guides piéton)
|
||||
|
||||
**Basculement automatique voiture ↔ piéton** :
|
||||
|
||||
Détection par vitesse GPS moyenne sur 30 secondes :
|
||||
- Vitesse < 5 km/h (stable 10s) → mode piéton
|
||||
- Vitesse ≥ 5 km/h (stable 10s) → mode voiture
|
||||
|
||||
Changements de mode :
|
||||
|
||||
| Mode actuel | Vitesse détectée | Nouveau mode | Effet |
|
||||
|-------------|------------------|--------------|-------|
|
||||
| Piéton | ≥ 5 km/h | Voiture | Notifications push → sonores + icône (app ouverte requise) |
|
||||
| Voiture | < 5 km/h | Piéton | Notifications sonores → push arrière-plan |
|
||||
|
||||
**Pas de popup confirmation** :
|
||||
- Basculement transparent et automatique
|
||||
- User n'a rien à faire
|
||||
- Hysteresis (10s) pour éviter basculements intempestifs
|
||||
|
||||
**Quota anti-spam mode piéton** :
|
||||
- Même limitation que mode voiture : **6 contenus/heure**
|
||||
- Cooldown 10 min si notification ignorée (app pas ouverte après tap)
|
||||
|
||||
**Justification** :
|
||||
- ✅ Expérience adaptée aux visites à pied (rayon large, pas de timing précis)
|
||||
- ✅ Économie batterie (geofencing natif iOS/Android)
|
||||
- ✅ User peut garder téléphone en poche
|
||||
- ✅ Basculement automatique = pas de friction
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Commande "Précédent"
|
||||
|
||||
**Décision** : Comportement smart selon progression écoute
|
||||
|
||||
**Règles** :
|
||||
|
||||
| Situation | Temps écouté | Action "Précédent" |
|
||||
|-----------|--------------|-------------------|
|
||||
| **Début de contenu** | <10 secondes | Retour au contenu précédent (position exacte) |
|
||||
| **Milieu/fin** | ≥10 secondes | Replay contenu actuel depuis le début |
|
||||
| **Premier de session** | N/A | Replay depuis début (rien avant) |
|
||||
|
||||
**Historique de navigation** :
|
||||
- **10 contenus maximum** en mémoire (Redis List)
|
||||
- Structure : `[{content_id, position_seconds, listened_at}, ...]`
|
||||
- FIFO : au-delà de 10, suppression du plus ancien
|
||||
|
||||
**Exemple scénario** :
|
||||
```
|
||||
Utilisateur écoute :
|
||||
1. Contenu A → écoute 5s → "Suivant"
|
||||
2. Contenu B → écoute 2min30 → "Suivant"
|
||||
3. Contenu C → écoute 5s → "Précédent"
|
||||
→ Retour Contenu B à 2min30 (car >10s)
|
||||
4. Sur Contenu B → "Précédent"
|
||||
→ Retour Contenu A à 5s (position exacte)
|
||||
```
|
||||
|
||||
**Interface (responsabilité front)** :
|
||||
- ❌ Pas de message UI
|
||||
- ✅ Progress bar revient au début ou à position exacte
|
||||
- ✅ Animation fluide (transition 0.3s)
|
||||
|
||||
**Justification** :
|
||||
- **UX intuitive** : comportement standard Spotify/YouTube
|
||||
- **Pas de frustration** : si début, vraiment revenir en arrière
|
||||
- **Simplicité** : règle unique (seuil 10s)
|
||||
|
||||
---
|
||||
|
||||
### 5.3 Interactions au volant : Like automatique et engagement
|
||||
|
||||
> ⚠️ **Architecture Decision Record** : Voir [ADR-010](../adr/010-commandes-volant.md) pour les détails techniques complets
|
||||
|
||||
**Décision** : Like automatique basé sur le temps d'écoute
|
||||
|
||||
**Problème technique identifié** :
|
||||
- iOS et Android ne supportent **pas nativement** les appuis longs ou doubles-appuis sur les commandes média
|
||||
- Les commandes physiques au volant varient selon les véhicules (pas de bouton "Pause" dédié sur beaucoup de modèles)
|
||||
- Système de double-appui/appui long = **non-intuitif** et **risques sécurité** (regarder écran pour feedback)
|
||||
|
||||
---
|
||||
|
||||
#### Commandes au volant simplifiées
|
||||
|
||||
**Actions disponibles** (100% compatibles tous véhicules) :
|
||||
|
||||
| Commande physique | Action RoadWave |
|
||||
|-------------------|-----------------|
|
||||
| **Suivant** | Passer au contenu suivant |
|
||||
| **Précédent** | Revenir au contenu précédent (règle 10s, voir section 5.2) |
|
||||
| **Play/Pause** | Pause/reprise lecture (fade out 0.3s) |
|
||||
|
||||
**Aucune action complexe au volant** → Sécurité routière maximale.
|
||||
|
||||
---
|
||||
|
||||
#### Like automatique implicite
|
||||
|
||||
**Principe** : Le système détecte automatiquement l'intérêt utilisateur selon le temps d'écoute.
|
||||
|
||||
**Règles d'attribution** :
|
||||
|
||||
| Durée écoutée | Action automatique | Points jauge | Justification |
|
||||
|---------------|-------------------|--------------|---------------|
|
||||
| **≥ 80% du contenu** | Like renforcé | +2.0 | Écoute quasi-complète = fort intérêt |
|
||||
| **30-79% du contenu** | Like standard | +1.0 | Écoute significative = intérêt |
|
||||
| **< 30% du contenu** | Pas de like | 0 | Écoute trop courte |
|
||||
| **Skip après <10s** | Signal négatif | -0.5 | Désintérêt marqué |
|
||||
|
||||
**Exemples concrets** :
|
||||
```
|
||||
Contenu de 3 minutes (180s) :
|
||||
- Écoute 2min30 (83%) → Like renforcé (+2 points)
|
||||
- Écoute 1min15 (42%) → Like standard (+1 point)
|
||||
- Écoute 30s (17%) puis skip → Pas de like
|
||||
- Skip après 5s → Signal négatif (-0.5 point)
|
||||
|
||||
Contenu de 15 minutes (900s) :
|
||||
- Écoute 13min (87%) → Like renforcé (+2 points)
|
||||
- Écoute 6min (40%) → Like standard (+1 point)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Actions complémentaires (mode piéton uniquement)
|
||||
|
||||
**Interface mobile** (vitesse < 5 km/h) :
|
||||
|
||||
| Action | Moyen | Effet |
|
||||
|--------|-------|-------|
|
||||
| **Like explicite** | Bouton cœur | +2 points jauge (même si déjà liké auto) |
|
||||
| **Unlike** | Re-clic cœur (toggle) | -2 points jauge |
|
||||
| **Abonnement** | Bouton "S'abonner" profil créateur | +5 points toutes jauges tags créateur |
|
||||
| **Désabonnement** | Bouton "Se désabonner" | -5 points |
|
||||
| **Signalement** | Menu contextuel "⋮" | Ouverture flux modération |
|
||||
|
||||
**Feedback visuel** :
|
||||
- **Like automatique** : Badge discret "♥ Ajouté à vos favoris" (2s, bas de l'écran)
|
||||
- **Like explicite** : Animation cœur rouge + vibration courte
|
||||
- **Abonnement** : Animation étoile dorée + badge "Abonné ✓"
|
||||
|
||||
**Disponibilité** :
|
||||
- ✅ Mode piéton (vitesse < 5 km/h) : toutes les actions disponibles
|
||||
- ❌ Mode voiture (vitesse ≥ 5 km/h) : aucune de ces actions (sauf like automatique)
|
||||
|
||||
---
|
||||
|
||||
#### Gestion impacts jauges (algorithme)
|
||||
|
||||
**Like automatique** :
|
||||
- Like renforcé (≥80%) → **+2% jauges** de tous les tags du contenu
|
||||
- Like standard (30-79%) → **+1% jauges** des tags du contenu
|
||||
- Signal négatif (skip <10s) → **-0.5% jauges** des tags du contenu
|
||||
|
||||
**Actions explicites** :
|
||||
- Like manuel → **+2% jauges** (cumulable avec like auto)
|
||||
- Unlike → **-2% jauges**
|
||||
- Abonnement → **+5% toutes jauges** tags créateur
|
||||
- Désabonnement → **-5% toutes jauges**
|
||||
|
||||
**Persistance** :
|
||||
- Événements stockés en base (table `listen_events`)
|
||||
- Mise à jour jauges : **immédiate** (Redis) + **async batch** (PostgreSQL)
|
||||
|
||||
---
|
||||
|
||||
#### Implémentation technique
|
||||
|
||||
**Backend** (Go) :
|
||||
|
||||
```go
|
||||
type ListenEvent struct {
|
||||
UserID string
|
||||
ContentID string
|
||||
StartedAt time.Time
|
||||
StoppedAt time.Time
|
||||
Duration int // secondes écoutées
|
||||
ContentTotal int // durée totale contenu
|
||||
Percentage float64 // duration / contentTotal * 100
|
||||
Action string // "completed", "skipped", "paused"
|
||||
}
|
||||
|
||||
func ProcessListenEvent(event ListenEvent) {
|
||||
percentage := event.Percentage
|
||||
|
||||
// Signal négatif fort
|
||||
if event.Action == "skipped" && event.Duration < 10 {
|
||||
UpdateJauges(event.UserID, event.ContentID, -0.5)
|
||||
return
|
||||
}
|
||||
|
||||
// Like automatique
|
||||
if percentage >= 80 {
|
||||
AutoLike(event.UserID, event.ContentID, 2.0) // Renforcé
|
||||
} else if percentage >= 30 {
|
||||
AutoLike(event.UserID, event.ContentID, 1.0) // Standard
|
||||
}
|
||||
// < 30% : pas de like
|
||||
}
|
||||
```
|
||||
|
||||
**Mobile** (iOS/Android) :
|
||||
|
||||
```swift
|
||||
// iOS - Tracking écoute
|
||||
class AudioPlayerManager {
|
||||
var startTime: Date?
|
||||
let contentDuration: TimeInterval
|
||||
|
||||
func onPlay() {
|
||||
startTime = Date()
|
||||
}
|
||||
|
||||
func onStop(action: String) { // "completed" | "skipped" | "paused"
|
||||
guard let start = startTime else { return }
|
||||
let duration = Date().timeIntervalSince(start)
|
||||
let percentage = (duration / contentDuration) * 100
|
||||
|
||||
// API call
|
||||
API.track(ListenEvent(
|
||||
contentId: currentContentId,
|
||||
duration: Int(duration),
|
||||
percentage: percentage,
|
||||
action: action
|
||||
))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Justification
|
||||
|
||||
**Avantages** :
|
||||
- ✅ **Sécurité routière maximale** : aucune action complexe au volant
|
||||
- ✅ **UX intuitive** : comportement standard industrie (Spotify, YouTube Music, Deezer)
|
||||
- ✅ **Compatibilité 100%** : fonctionne sur tous véhicules, tous OS
|
||||
- ✅ **Engagement amélioré** : tous les contenus écoutés génèrent des signaux
|
||||
- ✅ **Algorithme plus précis** : données granulaires (30%, 50%, 80%, 100%)
|
||||
- ✅ **Simplicité développement** : pas de workarounds complexes iOS/Android
|
||||
|
||||
**Inconvénients mitigés** :
|
||||
- ⚠️ Pas de like explicite en conduite → **Mitigation** : like automatique + vocal (CarPlay/Android Auto)
|
||||
- ⚠️ Pas d'abonnement en conduite → **Mitigation** : liste "Créateurs à découvrir" dans app
|
||||
- ⚠️ Like automatique peut surprendre → **Mitigation** : onboarding clair + unlike possible
|
||||
|
||||
---
|
||||
|
||||
#### Communication utilisateurs (onboarding)
|
||||
|
||||
**Écran onboarding 1** :
|
||||
```
|
||||
🚗 Conduite sécurisée
|
||||
|
||||
RoadWave détecte automatiquement vos goûts
|
||||
selon vos écoutes.
|
||||
|
||||
Plus vous écoutez longtemps, plus
|
||||
l'algorithme s'améliore !
|
||||
|
||||
[Suivant]
|
||||
```
|
||||
|
||||
**Écran onboarding 2** :
|
||||
```
|
||||
❤️ Likes automatiques
|
||||
|
||||
Pas besoin de liker manuellement :
|
||||
si vous écoutez >50% d'un contenu,
|
||||
on comprend que vous aimez !
|
||||
|
||||
[Suivant]
|
||||
```
|
||||
|
||||
**Écran onboarding 3** :
|
||||
```
|
||||
⏸️ Commandes simples
|
||||
|
||||
Utilisez les boutons au volant :
|
||||
• Suivant → Prochain contenu
|
||||
• Précédent → Contenu d'avant
|
||||
• Pause → Mettre en pause
|
||||
|
||||
[Commencer]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.4 Lecture en boucle et enchaînement
|
||||
|
||||
**Décision** : Passage automatique après 2s + insertion pub paramétrable
|
||||
|
||||
**Fin de contenu** :
|
||||
1. Audio termine → **Timer 2 secondes** démarre
|
||||
2. UI overlay : "Contenu suivant dans 2s..." + barre décompte
|
||||
3. Possibilité annuler : bouton "Rester sur ce contenu" (optionnel)
|
||||
4. Timer atteint 0 → passage automatique au contenu suivant
|
||||
|
||||
**Délai selon contexte** :
|
||||
|
||||
| Mode | Délai | Justification |
|
||||
|------|-------|---------------|
|
||||
| **Standard** | 2 secondes | Temps réaction confortable |
|
||||
| **Mode Kids** | 1 seconde | Attention courte enfants |
|
||||
| **Live** | 0 seconde | Enchaînement immédiat |
|
||||
|
||||
**Insertion publicité** :
|
||||
- Pub s'insère **pendant le délai de 2s** (transition naturelle)
|
||||
- Fréquence : **paramétrable admin** (défaut : 1 pub / 5 contenus)
|
||||
- Message : "Publicité (15s)" puis lecture pub
|
||||
- ⚠️ **Jamais d'interruption** d'un contenu en cours
|
||||
|
||||
**Publicité skippable** :
|
||||
- Durée minimale visionnage : **paramétrable** (défaut : 5 secondes)
|
||||
- Bouton "Passer" apparaît après délai
|
||||
- Métriques engagement : taux skip, durée écoute moyenne
|
||||
- **Like et abonnement autorisés sur pub** (engagement créateur pub)
|
||||
|
||||
**Si aucun contenu disponible** :
|
||||
1. Message : "Aucun contenu disponible dans cette zone"
|
||||
2. Proposition : "Élargir la zone de recherche ?" (bouton)
|
||||
3. Si accepté → relance algo avec rayon +50km
|
||||
4. Sinon → lecture en pause, attente action utilisateur
|
||||
|
||||
**Gestion erreurs** :
|
||||
- Échec chargement contenu suivant → **retry 3× avec backoff exponentiel**
|
||||
- Si 3 échecs → message "Connexion instable, basculement mode offline"
|
||||
- Mode offline → lecture contenus téléchargés uniquement
|
||||
|
||||
**Justification** :
|
||||
- **Fluidité** : enchaînement naturel sans action utilisateur
|
||||
- **Contrôle** : possibilité annuler pendant délai
|
||||
- **Paramétrabilité pub** : évite frustration excès publicité
|
||||
- **Engagement pub** : like/abonnement autorisé = monétisation créateurs pub
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 5
|
||||
736
docs/regles-metier/06-audio-guides-multi-sequences.md
Normal file
736
docs/regles-metier/06-audio-guides-multi-sequences.md
Normal file
@@ -0,0 +1,736 @@
|
||||
## 16. Audio-guides multi-séquences
|
||||
|
||||
### 16.1 Types d'audio-guides et modes de déplacement
|
||||
|
||||
**Décision** : 4 modes distincts avec détection automatique
|
||||
|
||||
#### 16.1.1 Classification par mode
|
||||
|
||||
| Mode | Vitesse détection | Déclenchement | Use case |
|
||||
|------|-------------------|---------------|----------|
|
||||
| **🚶 Piéton** | <5 km/h | Manuel (bouton "Suivant") | Musées, visites urbaines, monuments |
|
||||
| **🚗 Voiture** | >10 km/h | Auto GPS + Manuel possible | Safari-parc, routes touristiques, circuits auto |
|
||||
| **🚴 Vélo** | 5-25 km/h | Auto GPS + Manuel possible | Pistes cyclables, circuits vélo, parcours nature |
|
||||
| **🚌 Transport** | Variable | Auto GPS + Manuel possible | Bus touristiques, trains panoramiques |
|
||||
|
||||
**Détection automatique** :
|
||||
- Vitesse moyenne calculée sur 30 secondes
|
||||
- Suggestion mode au démarrage : "Détection : 🚗 Voiture. Est-ce correct ? [Oui] [Changer]"
|
||||
- User peut forcer mode manuellement (settings)
|
||||
|
||||
**Justification** :
|
||||
- Flexibilité maximale créateurs et utilisateurs
|
||||
- Expériences optimisées par type de déplacement
|
||||
- Gestion cas limites (vélo lent vs piéton rapide)
|
||||
|
||||
---
|
||||
|
||||
#### 16.1.2 Création d'un audio-guide (côté créateur)
|
||||
|
||||
**Formulaire création** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ Nouvel audio-guide multi-séquences │
|
||||
├────────────────────────────────────────┤
|
||||
│ Titre : [Safari du Paugre] │
|
||||
│ Description : [Découvrez les animaux │
|
||||
│ du parc en voiture...] │
|
||||
│ │
|
||||
│ Mode de déplacement : *obligatoire │
|
||||
│ ○ 🚶 Piéton (navigation manuelle) │
|
||||
│ ● 🚗 Voiture (GPS auto + manuel) │
|
||||
│ ○ 🚴 Vélo (GPS auto + manuel) │
|
||||
│ ○ 🚌 Transport (GPS auto + manuel) │
|
||||
│ │
|
||||
│ Vitesse recommandée : 30-50 km/h │
|
||||
│ (si voiture/vélo/transport) │
|
||||
│ │
|
||||
│ ──────────────────────────────────── │
|
||||
│ │
|
||||
│ Séquences (ordre lecture) : │
|
||||
│ │
|
||||
│ 1. [📍] Introduction - Point d'accueil │
|
||||
│ Lat: 43.1234, Lon: 2.5678 │
|
||||
│ Rayon déclenchement : 30m │
|
||||
│ Durée : 2:15 │
|
||||
│ [🎵 Audio uploadé] [✏️] [🗑️] │
|
||||
│ │
|
||||
│ 2. [📍] Enclos des lions │
|
||||
│ Lat: 43.1245, Lon: 2.5690 │
|
||||
│ Rayon déclenchement : 30m │
|
||||
│ Durée : 3:42 │
|
||||
│ [📤 Upload audio] [✏️] [🗑️] │
|
||||
│ │
|
||||
│ 3. [📍] Enclos des girafes │
|
||||
│ [+ Ajouter point GPS] │
|
||||
│ │
|
||||
│ [+ Ajouter séquence] │
|
||||
│ │
|
||||
│ 📊 Statistiques : │
|
||||
│ · 2 séquences complètes │
|
||||
│ · 5:57 durée totale │
|
||||
│ · 320m distance totale │
|
||||
│ │
|
||||
│ [🗺️ Aperçu sur carte] │
|
||||
│ [✅ Publier audio-guide] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Métadonnées obligatoires** :
|
||||
|
||||
| Champ | Requis | Détails |
|
||||
|-------|--------|---------|
|
||||
| **Titre audio-guide** | ✅ | 5-100 caractères |
|
||||
| **Description** | ✅ | 10-500 caractères |
|
||||
| **Mode déplacement** | ✅ | Piéton / Voiture / Vélo / Transport |
|
||||
| **Nombre séquences** | ✅ | Minimum 2, maximum 50 |
|
||||
| **Point GPS par séquence** | ✅ (sauf piéton) | Latitude, longitude (WGS84) |
|
||||
| **Rayon déclenchement** | ✅ (sauf piéton) | 10-100m selon mode |
|
||||
| **Vitesse recommandée** | ❌ | Optionnel, affichée utilisateur |
|
||||
| **Tags** | ✅ | 1-3 parmi liste prédéfinie |
|
||||
| **Classification âge** | ✅ | Tout public / 13+ / 16+ / 18+ |
|
||||
| **Zone diffusion** | ✅ | Polygon géographique |
|
||||
|
||||
**Wizard de création** :
|
||||
- Étape 1 : Infos générales (titre, description, mode)
|
||||
- Étape 2 : Ajout séquences une par une
|
||||
- Étape 3 : Preview carte (trace + points)
|
||||
- Étape 4 : Validation modération (3 premiers audio-guides)
|
||||
|
||||
**Justification** :
|
||||
- Contrôle total créateur sur expérience
|
||||
- Carte preview aide visualiser parcours
|
||||
- Wizard guidé = réduction friction création
|
||||
|
||||
---
|
||||
|
||||
### 16.2 Mode Piéton (manuel)
|
||||
|
||||
**Décision** : Navigation manuelle avec pub auto-play
|
||||
|
||||
#### 16.2.1 Passage entre séquences
|
||||
|
||||
**Séquence normale (sans pub)** :
|
||||
|
||||
1. Séquence 1 se termine
|
||||
2. Player se met en **pause automatique**
|
||||
3. Message affiché : "Séquence 1 terminée. Appuyez sur Suivant quand vous êtes prêt."
|
||||
4. User appuie sur [▶|] → Séquence 2 démarre immédiatement
|
||||
|
||||
**Séquence avec publicité** (1 pub / 5 séquences) :
|
||||
|
||||
1. Séquence 2 se termine
|
||||
2. **Publicité s'enchaîne automatiquement** (pas d'attente bouton)
|
||||
3. Pub se lit (skippable après 5s)
|
||||
4. Pub se termine → Player se met en **pause automatique**
|
||||
5. Message : "Séquence 3 prête. Appuyez sur Suivant."
|
||||
6. User appuie sur [▶|] → Séquence 3 démarre
|
||||
|
||||
**Schéma flux** :
|
||||
```
|
||||
Séquence 1 [fin] → PAUSE → User clique → Séquence 2 [fin] → PUB AUTO-PLAY → PAUSE → User clique → Séquence 3
|
||||
```
|
||||
|
||||
**Fréquence pub** :
|
||||
- Gratuits : 1 pub toutes les 5 séquences (paramétrable admin 1/3 à 1/10)
|
||||
- Premium : 0 pub
|
||||
|
||||
**Justification** :
|
||||
- Pub s'insère naturellement (pas d'attente utilisateur pour déclencher)
|
||||
- User garde contrôle rythme visite (pause après pub)
|
||||
- Monétisation effective créateurs
|
||||
- Premium reste attractif (0 interruption)
|
||||
|
||||
---
|
||||
|
||||
#### 16.2.2 Navigation et contrôles
|
||||
|
||||
**Décision** : Liberté totale utilisateur
|
||||
|
||||
**Contrôles disponibles** :
|
||||
|
||||
| Bouton | Fonction | Comportement |
|
||||
|--------|----------|--------------|
|
||||
| **[▶\|] Suivant** | Passe séquence suivante | Immédiat, même si séquence actuelle pas terminée |
|
||||
| **[\|◀] Précédent** | Retour séquence précédente | Saut direct séquence avant (pas de logique "replay si >10s") |
|
||||
| **[⏸️] Pause** | Pause temporaire | Reprend à position exacte |
|
||||
| **[▶️] Play** | Reprend lecture | Continue position actuelle |
|
||||
| **Liste séquences** | Navigation libre | Tap séquence → saut direct (même séquences non écoutées) |
|
||||
|
||||
**Interface liste séquences** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ 🚶 Audio-guide Piéton │
|
||||
│ Musée du Louvre │
|
||||
├────────────────────────────────────────┤
|
||||
│ [Cover image] │
|
||||
│ │
|
||||
│ ▶️ 0:00 ──●────────── 3:42 │
|
||||
│ │
|
||||
│ Séquence 3/12 : La Joconde │
|
||||
│ │
|
||||
│ [|◀] [⏸️] [▶|] │
|
||||
│ │
|
||||
│ ──────────────────────────────────── │
|
||||
│ │
|
||||
│ 📋 Liste des séquences │
|
||||
│ │
|
||||
│ ✅ 1. Introduction (2:15) │
|
||||
│ Écouté le 15/01/2026 │
|
||||
│ │
|
||||
│ ✅ 2. Pyramide du Louvre (1:48) │
|
||||
│ Écouté le 15/01/2026 │
|
||||
│ │
|
||||
│ ▶️ 3. La Joconde (3:42) - EN COURS │
|
||||
│ ──●──────────── 1:22/3:42 │
|
||||
│ │
|
||||
│ ⭕ 4. Vénus de Milo (2:58) │
|
||||
│ │
|
||||
│ ⭕ 5. Code d'Hammurabi (4:12) │
|
||||
│ │
|
||||
│ ⭕ 6. Victoire de Samothrace (3:25) │
|
||||
│ │
|
||||
│ ... +6 séquences │
|
||||
│ │
|
||||
│ [Tout afficher ▼] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Navigation libre** :
|
||||
- User peut sauter séquences déjà connues
|
||||
- User peut revenir en arrière à tout moment
|
||||
- User peut aller directement à séquence 8 (même si 4-7 non écoutées)
|
||||
|
||||
**Sauvegarde progression** :
|
||||
- Checkmarks ✅ sur séquences écoutées >80%
|
||||
- Position exacte sauvegardée dans séquence en cours
|
||||
|
||||
**Justification** :
|
||||
- Utilisateur contrôle 100% son rythme
|
||||
- Adapté musées : visitor peut voir physiquement une œuvre lointaine et vouloir écouter sa description
|
||||
- Pas de frustration (liberté totale)
|
||||
|
||||
---
|
||||
|
||||
### 16.3 Mode Voiture (GPS automatique)
|
||||
|
||||
**Décision** : GPS auto avec navigation manuelle conservée
|
||||
|
||||
#### 16.3.1 Déclenchement et contrôles
|
||||
|
||||
**Distinction audio-guides vs contenus géolocalisés simples** :
|
||||
|
||||
⚠️ **Important** : Les audio-guides multi-séquences fonctionnent différemment des contenus géolocalisés simples.
|
||||
|
||||
| Type | Séquences | Déclenchement | Notification | Enchaînement | Comptabilité quota |
|
||||
|------|-----------|---------------|--------------|--------------|-------------------|
|
||||
| **Contenu géolocalisé simple** | 1 séquence unique | Notification 7s avant (temps ETA) | Sonore + icône | Fin → retour buffer normal | 1 contenu = 1 quota |
|
||||
| **Audio-guide multi-séquences** | 2 à 50 séquences | Au point GPS exact (distance 30m) | Ding + toast 2s | Séquences s'enchaînent auto | 1 audio-guide = 1 quota (toutes séquences) |
|
||||
|
||||
**Fonctionnement GPS automatique** :
|
||||
|
||||
1. User démarre audio-guide en voiture (voir section 16.1 pour démarrage)
|
||||
2. Séquence 1 démarre automatiquement au point GPS défini (rayon 30m)
|
||||
3. Séquence 1 se termine
|
||||
4. **Affichage progress bar** : distance temps réel + ETA jusqu'au prochain point
|
||||
5. User roule vers point GPS suivant
|
||||
6. Arrivée au point GPS suivant (rayon 30m) → **déclenchement automatique** séquence suivante
|
||||
7. Notification sonore discrète : "Ding" (0.3s) + toast 2s : "Enclos des girafes"
|
||||
8. Séquence suivante démarre immédiatement (pas de décompte)
|
||||
|
||||
**Pas de système "7 secondes avant" pour les audio-guides** :
|
||||
- Contrairement aux contenus géolocalisés simples (voir [05-interactions-navigation.md](05-interactions-navigation.md#511-file-dattente-et-commande-suivant))
|
||||
- Les séquences se déclenchent **au point GPS exact** (rayon 30m)
|
||||
- Raison : expérience guidée continue, user sait qu'il suit un parcours
|
||||
|
||||
**Navigation manuelle CONSERVÉE** :
|
||||
|
||||
| Bouton | État | Comportement |
|
||||
|--------|------|--------------|
|
||||
| **[▶\|] Suivant** | ✅ Toujours actif | Passe séquence suivante immédiatement (même hors point GPS) |
|
||||
| **[\|◀] Précédent** | ✅ Toujours actif | Retour séquence précédente (même hors point GPS) |
|
||||
| **[⏸️] Pause** | ✅ | Pause temporaire |
|
||||
| **Liste séquences** | ✅ | Saut direct possible |
|
||||
|
||||
**Use cases navigation manuelle** :
|
||||
|
||||
| Situation | Solution manuelle |
|
||||
|-----------|-------------------|
|
||||
| Embouteillage (séquence finie, point GPS loin) | User clique Suivant → avance manuellement |
|
||||
| Point GPS inaccessible (route fermée) | User clique Suivant → skip point |
|
||||
| Envie réécouter séquence précédente | User clique Précédent → retour |
|
||||
| Passager manipule l'app | Passager navigue librement |
|
||||
|
||||
**Avertissement sécurité** :
|
||||
|
||||
- Si vitesse **>10 km/h** ET user clique bouton (Suivant/Précédent) :
|
||||
- Toast 3 secondes : "⚠️ Manipulation en conduite détectée. Pour votre sécurité, demandez à un passager."
|
||||
- **Action quand même exécutée** (pas de blocage)
|
||||
- Justification : sensibilisation sans bloquer (passager peut légitimement manipuler)
|
||||
|
||||
**Schéma flux** :
|
||||
```
|
||||
Point GPS 1 (30m) → Séquence 1 AUTO → User roule → Distance affichée → Point GPS 2 (30m) → Séquence 2 AUTO
|
||||
↓
|
||||
User clique Suivant (manuel) → Séquence 2 immédiate
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- Flexibilité maximale : GPS optimise expérience MAIS user garde contrôle
|
||||
- Gestion cas limites : routes fermées, détours, embouteillages
|
||||
- Sécurité : warning sensibilise sans bloquer (passager légitime)
|
||||
|
||||
---
|
||||
|
||||
#### 16.3.2 Affichage distance et guidage
|
||||
|
||||
**Décision** : Distance + direction (PAS de carte miniature)
|
||||
|
||||
**Interface en conduite** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ 🚗 Audio-guide Voiture │
|
||||
│ Safari du Paugre │
|
||||
├────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ▶️ 0:00 ──●────────── 2:15 │
|
||||
│ │
|
||||
│ Séquence 2/8 : Les lions │
|
||||
│ │
|
||||
│ ──────────────────────────────────── │
|
||||
│ │
|
||||
│ 📍 Prochain point │
|
||||
│ │
|
||||
│ Enclos des girafes │
|
||||
│ │
|
||||
│ ┌────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ ↗️ │ │
|
||||
│ │ (direction) │ │
|
||||
│ │ │ │
|
||||
│ │ 320 mètres │ │
|
||||
│ │ ≈ 40 secondes │ │
|
||||
│ │ │ │
|
||||
│ └────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Vitesse actuelle : 28 km/h │
|
||||
│ Vitesse recommandée : 20-30 km/h │
|
||||
│ │
|
||||
│ [|◀] [⏸️] [▶|] [📋 Liste] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Affichage entre deux séquences** :
|
||||
|
||||
Quand une séquence se termine et qu'il reste un point GPS suivant, l'interface bascule en mode "attente prochain point" :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ 🚗 Audio-guide Voiture │
|
||||
│ Safari du Paugre │
|
||||
├────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ✅ Séquence 2/8 terminée │
|
||||
│ Les lions │
|
||||
│ │
|
||||
│ ──────────────────────────────────── │
|
||||
│ │
|
||||
│ 📍 Prochain point │
|
||||
│ │
|
||||
│ Enclos des girafes │
|
||||
│ │
|
||||
│ ┌────────────────────────────────┐ │
|
||||
│ │ [Progress bar] │ │
|
||||
│ │ ████████░░░░░░░░░ 65% │ │
|
||||
│ │ │ │
|
||||
│ │ ↗️ │ │
|
||||
│ │ (direction) │ │
|
||||
│ │ │ │
|
||||
│ │ 320 mètres │ │
|
||||
│ │ ≈ 40 secondes │ │
|
||||
│ │ │ │
|
||||
│ └────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Vitesse actuelle : 28 km/h │
|
||||
│ │
|
||||
│ [|◀] [▶️ Rejouer séq.] [▶|] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Progress bar dynamique** :
|
||||
- Se remplit au fur et à mesure qu'on se rapproche du point
|
||||
- Calcul : `progress = 100 - (distance_actuelle / distance_initiale * 100)`
|
||||
- Exemple : distance initiale 500m, distance actuelle 175m → progress = 65%
|
||||
- Couleur : vert (#4CAF50) pour la partie remplie, gris (#E0E0E0) pour le reste
|
||||
|
||||
**Bouton "Rejouer séq."** :
|
||||
- Permet de réécouter la séquence qui vient de se terminer
|
||||
- User clique → séquence actuelle redémarre depuis 0:00
|
||||
- Utile si distraction pendant l'écoute
|
||||
|
||||
---
|
||||
|
||||
**Informations affichées** :
|
||||
|
||||
| Info | Mise à jour | Format |
|
||||
|------|-------------|--------|
|
||||
| **Distance** | Chaque seconde | "320 m" / "1.2 km" |
|
||||
| **ETA** | Chaque seconde | "≈ 40 secondes" / "≈ 2 minutes" |
|
||||
| **Direction** | Chaque 5s | Flèche indique direction (8 directions : ↑ ↗ → ↘ ↓ ↙ ← ↖) |
|
||||
| **Vitesse actuelle** | Chaque seconde | "28 km/h" |
|
||||
| **Vitesse recommandée** | Statique | "20-30 km/h" (définie par créateur) |
|
||||
| **Progress bar** | Chaque seconde | Pourcentage parcouru vers prochain point |
|
||||
|
||||
**Calcul direction** :
|
||||
|
||||
```javascript
|
||||
// Calcul angle entre position actuelle et prochain point
|
||||
const currentGPS = getCurrentLocation();
|
||||
const nextPoint = audioGuide.sequences[currentIndex + 1].location;
|
||||
|
||||
const angle = calculateBearing(currentGPS, nextPoint); // 0-360°
|
||||
|
||||
// Conversion en flèche (8 directions)
|
||||
const arrows = ['↑', '↗', '→', '↘', '↓', '↙', '←', '↖'];
|
||||
const index = Math.round(angle / 45) % 8;
|
||||
const direction = arrows[index];
|
||||
```
|
||||
|
||||
**Calcul ETA** :
|
||||
|
||||
```javascript
|
||||
const distance = calculateDistance(currentGPS, nextPoint); // mètres
|
||||
const currentSpeed = getCurrentSpeed(); // km/h
|
||||
|
||||
if (currentSpeed > 5) {
|
||||
const eta = (distance / 1000) / currentSpeed * 3600; // secondes
|
||||
return formatETA(eta); // "≈ 40 secondes" ou "≈ 2 minutes"
|
||||
} else {
|
||||
return "En attente de déplacement";
|
||||
}
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- Distance + ETA = info essentielle sans surcharge visuelle
|
||||
- Direction (flèche) = aide se repérer sans carte complexe
|
||||
- Simplicité = moins distraction conducteur
|
||||
- Économie batterie (pas de rendu carte)
|
||||
|
||||
---
|
||||
|
||||
#### 16.3.3 Rayon de déclenchement et tolérance
|
||||
|
||||
**Décision** : Rayon configurable créateur avec défauts intelligents
|
||||
|
||||
**Rayons par défaut** :
|
||||
|
||||
| Mode | Rayon déclenchement | Rayon "point manqué" | Justification |
|
||||
|------|---------------------|----------------------|---------------|
|
||||
| **🚗 Voiture** | 30 mètres | 100 mètres | Vitesse élevée = anticipation |
|
||||
| **🚴 Vélo** | 50 mètres | 75 mètres | Vitesse variable, arrêts fréquents |
|
||||
| **🚌 Transport** | 100 mètres | 150 mètres | Arrêts bus/train, moins précis |
|
||||
|
||||
**Configuration créateur** :
|
||||
|
||||
- Curseur rayon : **10m → 200m**
|
||||
- Défaut pré-sélectionné selon mode choisi
|
||||
- Preview visuel : cercle sur carte (lors création)
|
||||
- Suggestion auto : "Recommandé : 30m pour voiture à 30 km/h"
|
||||
|
||||
**Gestion point manqué** :
|
||||
|
||||
```
|
||||
User passe à 110m du point GPS
|
||||
(hors rayon déclenchement 30m MAIS dans rayon tolérance 100m)
|
||||
↓
|
||||
Toast : "⚠️ Point manqué : Enclos des girafes"
|
||||
↓
|
||||
Popup 5 secondes :
|
||||
┌────────────────────────────────────┐
|
||||
│ Point manqué │
|
||||
│ │
|
||||
│ "Enclos des girafes" │
|
||||
│ Vous êtes passé à 110m du point │
|
||||
│ │
|
||||
│ [🔊 Écouter quand même] │
|
||||
│ [⏭️ Passer au suivant] │
|
||||
│ [🔙 Faire demi-tour] │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Actions popup** :
|
||||
|
||||
| Bouton | Comportement |
|
||||
|--------|--------------|
|
||||
| **Écouter quand même** | Lance séquence immédiatement (même hors zone) |
|
||||
| **Passer au suivant** | Skip séquence, continue vers prochain point |
|
||||
| **Faire demi-tour** | Lance navigation GPS externe (Google Maps / Waze) vers point manqué |
|
||||
|
||||
**Si user au-delà rayon tolérance (>100m)** :
|
||||
- Aucun popup (point trop loin, probablement hors itinéraire)
|
||||
- User peut naviguer manuellement (bouton Suivant)
|
||||
|
||||
**Justification** :
|
||||
- Flexibilité créateur (ajuste selon terrain, vitesse prévue)
|
||||
- Gestion intelligente imprévus (détours, routes fermées)
|
||||
- User pas bloqué (toujours moyen avancer)
|
||||
|
||||
---
|
||||
|
||||
### 16.4 Modes Vélo et Transport
|
||||
|
||||
**Décision** : Même logique voiture avec tolérances ajustées
|
||||
|
||||
**Différences par rapport à mode voiture** :
|
||||
|
||||
| Paramètre | Voiture | Vélo | Transport |
|
||||
|-----------|---------|------|-----------|
|
||||
| **Rayon déclenchement** | 30m | 50m | 100m |
|
||||
| **Rayon tolérance "point manqué"** | 100m | 75m | 150m |
|
||||
| **Vitesse recommandée affichée** | 20-50 km/h | 10-25 km/h | Variable (selon ligne) |
|
||||
| **Warning sécurité** | >10 km/h | >5 km/h | Désactivé |
|
||||
|
||||
**Mode Vélo spécificités** :
|
||||
|
||||
- Rayon plus large : vitesse variable, nombreux arrêts (feux, piétons)
|
||||
- Warning sécurité dès 5 km/h (vélo en mouvement)
|
||||
- Tolérance GPS moins stricte (tracé moins prévisible qu'auto)
|
||||
|
||||
**Mode Transport spécificités** :
|
||||
|
||||
- Rayon très large : arrêts fréquents (bus, train), ligne fixe
|
||||
- Pas de warning sécurité (user = passager, pas conducteur)
|
||||
- Vitesse recommandée = "Selon ligne" (pas de valeur fixe)
|
||||
- Tolérance horaire : si bus en retard, point peut se déclencher avec 2-3 min de délai
|
||||
|
||||
**Comportement identique voiture** :
|
||||
|
||||
- Navigation manuelle conservée (boutons actifs)
|
||||
- Affichage distance + ETA + direction
|
||||
- Gestion point manqué
|
||||
- Pub entre séquences
|
||||
|
||||
**Justification** :
|
||||
- Vélo : moins de contrôle qu'auto (obstacles, arrêts), nécessite tolérance
|
||||
- Transport : moins de contrôle utilisateur (suit ligne fixe), rayon large compense
|
||||
- Même UX globale = cohérence
|
||||
|
||||
---
|
||||
|
||||
### 16.5 Publicités dans audio-guides
|
||||
|
||||
**Décision** : Pub auto-play entre séquences TOUS modes
|
||||
|
||||
#### 16.5.1 Règles universelles
|
||||
|
||||
**Insertion publicité** :
|
||||
|
||||
- Fréquence : **1 pub toutes les 5 séquences** (paramétrable admin 1/3 à 1/10)
|
||||
- Gratuits uniquement, **Premium 0 pub**
|
||||
- Pub s'enchaîne **automatiquement** après séquence
|
||||
- Skippable après **5 secondes** (règle standard RoadWave)
|
||||
- Volume normalisé -14 LUFS (comme pubs normales)
|
||||
|
||||
**Comportement MODE PIÉTON** :
|
||||
|
||||
```
|
||||
Séquence 2 [fin]
|
||||
→ Pub AUTO-PLAY
|
||||
→ Pub se termine
|
||||
→ PAUSE AUTO
|
||||
→ Message "Séquence 3 prête. Appuyez sur Suivant."
|
||||
→ User clique [▶|]
|
||||
→ Séquence 3 démarre
|
||||
```
|
||||
|
||||
**Comportement MODE VOITURE/VÉLO/TRANSPORT** :
|
||||
|
||||
```
|
||||
Séquence 2 [fin]
|
||||
→ Pub AUTO-PLAY
|
||||
→ Pub se termine
|
||||
→ ATTENTE point GPS suivant OU user clique Suivant
|
||||
→ Séquence 3 démarre
|
||||
```
|
||||
|
||||
**Schéma complet** :
|
||||
|
||||
| Mode | Après séquence normale | Après pub |
|
||||
|------|------------------------|-----------|
|
||||
| **Piéton** | Pause + attente user | Pause + attente user |
|
||||
| **Voiture** | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant |
|
||||
| **Vélo** | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant |
|
||||
| **Transport** | Attente GPS OU user clique Suivant | Attente GPS OU user clique Suivant |
|
||||
|
||||
**Justification** :
|
||||
- Monétisation équitable créateurs (tous modes participent)
|
||||
- Pub s'insère naturellement (auto-play, pas d'attente utilisateur)
|
||||
- User garde contrôle : piéton clique Suivant, voiture peut skip manuel
|
||||
- Premium reste attractif (expérience 0 interruption)
|
||||
- Modèle économique viable
|
||||
|
||||
---
|
||||
|
||||
#### 16.5.2 Métriques pub audio-guides
|
||||
|
||||
**Dashboard créateur** :
|
||||
|
||||
| Métrique | Affichage |
|
||||
|----------|-----------|
|
||||
| **Impressions pub** | Nombre de pubs insérées dans audio-guides |
|
||||
| **Écoutes complètes pub** | Nombre de pubs écoutées >80% |
|
||||
| **Taux skip pub** | % pubs skippées avant 5s vs après |
|
||||
| **Revenus pub audio-guides** | 3€ / 1000 écoutes complètes (6% CA pub) |
|
||||
|
||||
**Distinction contenus normaux vs audio-guides** :
|
||||
- Dashboard sépare : "Revenus contenus classiques" / "Revenus audio-guides"
|
||||
- Permet créateur voir performance par type
|
||||
|
||||
**Justification** :
|
||||
- Transparence créateur (comprend revenus)
|
||||
- Incite création audio-guides (nouvelle source revenus)
|
||||
|
||||
---
|
||||
|
||||
### 16.6 Reprise et sauvegarde progression
|
||||
|
||||
**Décision** : Sauvegarde complète automatique avec popup intelligente
|
||||
|
||||
#### 16.6.1 Sauvegarde automatique
|
||||
|
||||
**Données sauvegardées** :
|
||||
|
||||
| Info | Détail | Utilité |
|
||||
|------|--------|---------|
|
||||
| **Audio-guide ID** | Identifiant unique | Retrouver audio-guide |
|
||||
| **Séquence actuelle** | Index (ex: 3/12) | Reprise position |
|
||||
| **Position dans séquence** | Timestamp exact (ex: 1:42/3:20) | Reprise exacte |
|
||||
| **Séquences écoutées** | Liste avec checkmarks ✅ | Historique progression |
|
||||
| **Date dernière écoute** | Timestamp | Proposer reprise si <30j |
|
||||
| **GPS dernière position** | Coordonnées optionnelles | Info contextuelle (non utilisée pour reprise) |
|
||||
|
||||
**Stockage** :
|
||||
|
||||
| Environnement | Technologie | Utilité |
|
||||
|---------------|-------------|---------|
|
||||
| **Local** | SQLite mobile | Fonctionnement offline |
|
||||
| **Cloud** | PostgreSQL (sync auto) | Multi-device (reprendre sur autre appareil) |
|
||||
|
||||
**Synchronisation** :
|
||||
- Sauvegarde locale : chaque fin de séquence + chaque 30s
|
||||
- Sync cloud : à la reconnexion réseau (batch)
|
||||
|
||||
**Justification** :
|
||||
- Expérience fluide (pas de perte progression)
|
||||
- Multi-device (démarrer sur iPhone, continuer sur iPad)
|
||||
- Offline-first (fonctionne sans réseau)
|
||||
|
||||
---
|
||||
|
||||
#### 16.6.2 Interface de reprise
|
||||
|
||||
**Conditions popup** :
|
||||
- Dernière écoute **<30 jours**
|
||||
- Progression **>0%** et **<100%** (pas terminé)
|
||||
|
||||
**Popup reprise** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ Reprendre l'audio-guide ? │
|
||||
├────────────────────────────────────────┤
|
||||
│ 🚗 Safari du Paugre │
|
||||
│ @safari_createur │
|
||||
│ │
|
||||
│ Progression : 3/8 séquences écoutées │
|
||||
│ Dernière écoute : il y a 2 jours │
|
||||
│ │
|
||||
│ Vous étiez à : │
|
||||
│ "Les lions" (1:42/3:20) │
|
||||
│ │
|
||||
│ [▶️ Reprendre] [🔄 Recommencer] │
|
||||
│ [📋 Voir toutes les séquences] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Actions** :
|
||||
|
||||
| Bouton | Comportement |
|
||||
|--------|--------------|
|
||||
| **Reprendre** | Continue séquence 3 à position 1:42 exacte |
|
||||
| **Recommencer** | Reset progression, démarre séquence 1 depuis 0:00 |
|
||||
| **Voir séquences** | Affiche liste complète, user choisit séquence départ |
|
||||
|
||||
**Expiration progression** :
|
||||
- Progression conservée **30 jours**
|
||||
- Après 30j : popup "Audio-guide expiré. Recommencez depuis le début ?"
|
||||
- Suppression données progression (mais historique "écouté" préservé)
|
||||
|
||||
**Justification** :
|
||||
- Contexte clair : user sait exactement où il en est
|
||||
- Flexibilité : reprendre OU recommencer (choix utilisateur)
|
||||
- 30 jours = raisonnable pour tourisme multi-jours ou retour ultérieur
|
||||
|
||||
---
|
||||
|
||||
#### 16.6.3 Multi-device
|
||||
|
||||
**Scénario** :
|
||||
|
||||
1. User démarre audio-guide sur iPhone (séquences 1-3)
|
||||
2. Progression sync cloud
|
||||
3. Lendemain : user ouvre app sur iPad
|
||||
4. Popup : "Reprendre Safari du Paugre sur cet appareil ?"
|
||||
5. User clique Reprendre → continue séquence 4
|
||||
|
||||
**Conflit de version** :
|
||||
- Si modifications simultanées 2 appareils (rare) : **dernière modification gagne**
|
||||
- Toast : "Progression mise à jour depuis votre autre appareil"
|
||||
|
||||
**Justification** :
|
||||
- Confort utilisateur (change d'appareil librement)
|
||||
- Use case réel : planning trajet sur tablette, écoute sur smartphone en voiture
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 16
|
||||
|
||||
| Point | Décision | Coût | Complexité |
|
||||
|-------|----------|------|------------|
|
||||
| **16.1** Types audio-guides | 4 modes (piéton/voiture/vélo/transport) avec détection auto | 0€ | Moyenne |
|
||||
| **16.1.2** Création | Formulaire séquences + GPS + rayon + wizard guidé | 0€ | Moyenne |
|
||||
| **16.2.1** Piéton - Passages | Manuel AVEC pub auto-play entre séquences, pause après | 0€ | Faible |
|
||||
| **16.2.2** Piéton - Navigation | Liberté totale (skip, retour, saut direct liste) | 0€ | Faible |
|
||||
| **16.3.1** Voiture - Déclenchement | GPS auto + boutons manuels actifs (warning sécurité si >10 km/h) | 0€ | Moyenne |
|
||||
| **16.3.2** Voiture - Affichage | Distance + ETA + direction (flèche) + vitesse (PAS de carte) | 0€ | Faible |
|
||||
| **16.3.3** Voiture - Rayon | Configurable créateur (défauts 30m/50m/100m selon mode) | 0€ | Faible |
|
||||
| **16.4** Vélo & Transport | Mêmes règles avec tolérances ajustées + warning adapté | 0€ | Faible |
|
||||
| **16.5** Publicités | 1/5 séquences, auto-play TOUS modes, skippable 5s | 0€ | Faible |
|
||||
| **16.6.1** Sauvegarde | Complète (séquence + position + historique) local + cloud | 0€ | Faible |
|
||||
| **16.6.2** Reprise | Popup intelligente avec choix (reprendre/recommencer), expiration 30j | 0€ | Faible |
|
||||
| **16.6.3** Multi-device | Sync cloud PostgreSQL (reprendre sur autre appareil) | 0€ | Faible |
|
||||
|
||||
**Coût total MVP : 0€** (GPS natif, calcul distance PostGIS)
|
||||
|
||||
---
|
||||
|
||||
## Points d'attention pour Gherkin
|
||||
|
||||
- Tester 4 modes audio-guides (détection vitesse auto)
|
||||
- Tester création séquences avec points GPS + rayon configurable
|
||||
- Tester mode piéton : pause après séquence + pub auto-play + pause après pub + clic Suivant
|
||||
- Tester navigation libre piéton (skip, retour, saut direct liste)
|
||||
- Tester mode voiture : déclenchement GPS auto rayon 30m
|
||||
- Tester navigation manuelle voiture : boutons actifs + warning si vitesse >10 km/h
|
||||
- Tester affichage distance + ETA + direction (flèche 8 directions)
|
||||
- Tester rayon tolérance "point manqué" (popup 3 actions)
|
||||
- Tester mode vélo (rayon 50m) et transport (rayon 100m)
|
||||
- Tester insertion pub 1/5 séquences tous modes avec auto-play
|
||||
- Tester sauvegarde progression locale + sync cloud
|
||||
- Tester popup reprise (3 boutons : reprendre/recommencer/voir liste)
|
||||
- Tester expiration progression 30 jours
|
||||
- Tester multi-device : démarrer iPhone, continuer iPad
|
||||
- Tester gestion conflit progression simultanée 2 appareils
|
||||
757
docs/regles-metier/07-contenus-geolocalises-voiture.md
Normal file
757
docs/regles-metier/07-contenus-geolocalises-voiture.md
Normal file
@@ -0,0 +1,757 @@
|
||||
## 17. Contenus géolocalisés en mode voiture
|
||||
|
||||
### 17.1 Principe général
|
||||
|
||||
**Objectif** : Proposer des contenus audio au moment précis où l'utilisateur passe devant un point d'intérêt géographique, pour enrichir son trajet avec des informations contextuelles liées au paysage.
|
||||
|
||||
**Contrainte principale** : **Sécurité routière**
|
||||
- Aucune distraction visuelle (pas de texte à lire)
|
||||
- Notification sonore uniquement + icône minimale
|
||||
- Validation par un seul bouton physique au volant ("Suivant")
|
||||
- App doit être ouverte (premier plan)
|
||||
|
||||
**Distinction avec audio-guides** :
|
||||
|
||||
| Type | Description | Use case |
|
||||
|------|-------------|----------|
|
||||
| **Contenu géolocalisé simple** (cette section) | 1 point GPS unique, 1 contenu audio | Monument, panneau historique, point de vue |
|
||||
| **Audio-guide multi-séquences** (section 16) | Plusieurs points GPS, séquences enchaînées | Safari-parc, circuit touristique, parcours urbain |
|
||||
|
||||
---
|
||||
|
||||
### 17.2 Détection et notification
|
||||
|
||||
#### 17.2.1 Calcul ETA (Estimated Time of Arrival)
|
||||
|
||||
**Méthode** : API GPS native iOS/Android
|
||||
|
||||
**Principe** :
|
||||
- Calcul temps d'arrivée au point GPS basé sur vitesse actuelle et distance
|
||||
- Notification déclenchée **7 secondes avant** d'atteindre le point
|
||||
- Permet au conducteur d'avoir le temps de réagir (2s) + décompte (5s)
|
||||
|
||||
**Implémentation iOS (Swift)** :
|
||||
|
||||
```swift
|
||||
import CoreLocation
|
||||
|
||||
class GeoContentDetector {
|
||||
let locationManager = CLLocationManager()
|
||||
var geoContents: [GeoContent] = [] // Points GPS proches
|
||||
|
||||
func calculateETA(to targetLocation: CLLocation) -> TimeInterval? {
|
||||
guard let currentLocation = locationManager.location,
|
||||
let currentSpeed = currentLocation.speed, // m/s
|
||||
currentSpeed > 0 else { return nil }
|
||||
|
||||
let distance = currentLocation.distance(from: targetLocation) // mètres
|
||||
let eta = distance / currentSpeed // secondes
|
||||
return eta
|
||||
}
|
||||
|
||||
func checkTriggers() {
|
||||
// Appelé toutes les 1 seconde
|
||||
for geoContent in geoContents {
|
||||
let targetLocation = CLLocation(
|
||||
latitude: geoContent.latitude,
|
||||
longitude: geoContent.longitude
|
||||
)
|
||||
|
||||
// Cas particulier : vitesse très faible
|
||||
if let speed = locationManager.location?.speed,
|
||||
speed < 1.4, // < 5 km/h
|
||||
let distance = locationManager.location?.distance(from: targetLocation),
|
||||
distance < 50 {
|
||||
triggerNotification(for: geoContent)
|
||||
continue
|
||||
}
|
||||
|
||||
// Cas normal : calcul ETA
|
||||
if let eta = calculateETA(to: targetLocation),
|
||||
eta <= 7.0 && eta > 0 {
|
||||
triggerNotification(for: geoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Implémentation Android (Kotlin)** :
|
||||
|
||||
```kotlin
|
||||
import android.location.Location
|
||||
import com.google.android.gms.location.FusedLocationProviderClient
|
||||
|
||||
class GeoContentDetector(private val fusedLocationClient: FusedLocationProviderClient) {
|
||||
private var geoContents: List<GeoContent> = emptyList()
|
||||
|
||||
fun calculateETA(targetLocation: Location, currentLocation: Location): Double? {
|
||||
val speed = currentLocation.speed // m/s
|
||||
if (speed <= 0) return null
|
||||
|
||||
val distance = currentLocation.distanceTo(targetLocation) // mètres
|
||||
return distance / speed // secondes
|
||||
}
|
||||
|
||||
fun checkTriggers(currentLocation: Location) {
|
||||
for (geoContent in geoContents) {
|
||||
val targetLocation = Location("").apply {
|
||||
latitude = geoContent.latitude
|
||||
longitude = geoContent.longitude
|
||||
}
|
||||
|
||||
// Cas particulier : vitesse très faible (< 5 km/h)
|
||||
if (currentLocation.speed < 1.4 &&
|
||||
currentLocation.distanceTo(targetLocation) < 50) {
|
||||
triggerNotification(geoContent)
|
||||
continue
|
||||
}
|
||||
|
||||
// Cas normal : calcul ETA
|
||||
val eta = calculateETA(targetLocation, currentLocation)
|
||||
if (eta != null && eta <= 7.0 && eta > 0) {
|
||||
triggerNotification(geoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Cas particulier : vitesse nulle ou très faible** :
|
||||
- Si vitesse < 5 km/h (1.4 m/s) ET distance < 50m → notification immédiate
|
||||
- Exemple : user arrêté à un feu rouge à 30m du point
|
||||
- Évite notification trop tardive quand user redémarre
|
||||
|
||||
**Fréquence de vérification** :
|
||||
- GPS updates : toutes les 1 seconde (balance batterie/précision)
|
||||
- Calcul ETA : à chaque update GPS
|
||||
- Notification : déclenchée immédiatement quand ETA ≤ 7s
|
||||
|
||||
---
|
||||
|
||||
#### 17.2.2 Format de la notification
|
||||
|
||||
**Philosophie** : **Minimalisme absolu** pour sécurité routière
|
||||
|
||||
❌ **Pas de** :
|
||||
- Titre texte à lire
|
||||
- Description longue
|
||||
- Bouton "Annuler" ou "Plus tard"
|
||||
- Popup bloquante
|
||||
- Image/cover du contenu
|
||||
|
||||
✅ **Uniquement** :
|
||||
- Son bref (notification)
|
||||
- Icône selon tag du contenu
|
||||
- Compteur chiffres (7→1)
|
||||
|
||||
**Icônes par tag** :
|
||||
|
||||
| Tag | Icône | Couleur |
|
||||
|-----|-------|---------|
|
||||
| Culture générale | 🏛️ | Bleu |
|
||||
| Histoire | 📜 | Marron |
|
||||
| Voyage | ✈️ | Vert |
|
||||
| Famille | 👨👩👧 | Orange |
|
||||
| Musique | 🎵 | Rose |
|
||||
| Sport | ⚽ | Rouge |
|
||||
| Technologie | 💻 | Gris |
|
||||
| Automobile | 🚗 | Noir |
|
||||
| Politique | 🏛️ | Bleu foncé |
|
||||
| Économie | 📈 | Vert foncé |
|
||||
| Cryptomonnaie | ₿ | Or |
|
||||
| Santé | 🏥 | Rouge clair |
|
||||
| Amour | ❤️ | Rose vif |
|
||||
|
||||
**Interface visuelle (minimaliste)** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ [Player actuel] │
|
||||
│ ▶️ Podcast en cours... │
|
||||
│ 0:00 ────●──────────── 12:34 │
|
||||
│ │
|
||||
│ [|◀] [⏸️] [▶|] │
|
||||
├────────────────────────────────────────┤
|
||||
│ 🏛️ │
|
||||
│ 7 │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Évolution du compteur** :
|
||||
- Affichage pendant 7 secondes
|
||||
- Compteur décrémente : 7 → 6 → 5 → 4 → 3 → 2 → 1 → disparaît
|
||||
- Police grande (72pt), bold, couleur blanche
|
||||
- Background semi-transparent (noir 50% opacity)
|
||||
|
||||
**Notification sonore** :
|
||||
- **Son** : bip court (0.5s) ou "ding" doux personnalisé RoadWave
|
||||
- **Volume** : suit le volume système notification (indépendant du volume media)
|
||||
- **Pas de vibration** : inutile en voiture (téléphone sur support)
|
||||
- **Configurable** : user peut choisir dans settings :
|
||||
- Bip système
|
||||
- Son RoadWave personnalisé
|
||||
- Muet (visuel uniquement)
|
||||
|
||||
---
|
||||
|
||||
#### 17.2.2b Conformité CarPlay / Android Auto
|
||||
|
||||
**Décision** : Notification sonore uniquement en mode CarPlay/Android Auto
|
||||
|
||||
**Contexte** :
|
||||
- [CarPlay Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/carplay) interdisent les overlays qui "takeover" l'écran
|
||||
- [Android Auto Media Apps Guidelines](https://developer.android.com/training/cars/media) imposent des interactions minimales
|
||||
- Sécurité routière maximale = pas de distraction visuelle
|
||||
|
||||
**Comportement en mode CarPlay/Android Auto** :
|
||||
|
||||
❌ **Désactivé** :
|
||||
- Icône overlay
|
||||
- Compteur visuel (7...6...5...)
|
||||
- Tout élément graphique supplémentaire
|
||||
|
||||
✅ **Activé uniquement** :
|
||||
- Notification sonore (bip ou ding)
|
||||
- Bouton "Suivant" standard (déjà présent)
|
||||
|
||||
**Détection automatique** :
|
||||
|
||||
```swift
|
||||
// iOS - Détection CarPlay
|
||||
import MediaPlayer
|
||||
|
||||
class NotificationManager {
|
||||
var isCarPlayActive: Bool {
|
||||
if #available(iOS 14.0, *) {
|
||||
return MPNowPlayingInfoCenter.default().playbackState == .playing &&
|
||||
MPPlayableContentManager.shared().context != nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func showGeoNotification(content: GeoContent) {
|
||||
if isCarPlayActive {
|
||||
// CarPlay : sonore uniquement
|
||||
AudioServicesPlaySystemSound(1052) // Notification beep
|
||||
// Pas d'overlay visuel
|
||||
} else {
|
||||
// Mode normal : sonore + icône + compteur
|
||||
showFullNotification(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
// Android - Détection Android Auto
|
||||
import android.car.Car
|
||||
|
||||
class NotificationManager(private val context: Context) {
|
||||
private fun isAndroidAutoActive(): Boolean {
|
||||
val carManager = context.getSystemService(Context.CAR_SERVICE) as? Car
|
||||
return carManager?.isConnected == true
|
||||
}
|
||||
|
||||
fun showGeoNotification(content: GeoContent) {
|
||||
if (isAndroidAutoActive()) {
|
||||
// Android Auto : sonore uniquement
|
||||
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setSound(defaultSoundUri)
|
||||
.setCategory(NotificationCompat.CATEGORY_NAVIGATION)
|
||||
.build()
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
} else {
|
||||
// Mode normal : sonore + overlay visuel
|
||||
showFullNotification(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Expérience utilisateur en CarPlay/Android Auto** :
|
||||
|
||||
1. User roule, app connectée à CarPlay/Android Auto
|
||||
2. ETA 7s atteint → notification sonore uniquement (bip)
|
||||
3. User entend le bip, sait qu'un contenu géolocalisé est disponible
|
||||
4. User appuie sur bouton "Suivant" physique au volant
|
||||
5. Décompte 5s démarre (audio continue normalement)
|
||||
6. Contenu géolocalisé démarre après 5s
|
||||
|
||||
**Justification** :
|
||||
- ✅ Conformité maximale CarPlay/Android Auto guidelines
|
||||
- ✅ Sécurité routière (pas de distraction visuelle)
|
||||
- ✅ User peut toujours valider via bouton "Suivant" standard
|
||||
- ✅ Apps comparables (Waze, Apple Maps) utilisent alertes sonores similaires
|
||||
|
||||
**Alternative envisagée** : Mini-badge sur bouton "Suivant"
|
||||
- Moins invasif qu'un compteur
|
||||
- Mais toujours considéré comme overlay (zone grise)
|
||||
- **Décision** : Privilégier conformité maximale (sonore uniquement)
|
||||
|
||||
---
|
||||
|
||||
#### 17.2.3 Décompte après validation
|
||||
|
||||
**User appuie sur "Suivant"** :
|
||||
|
||||
**Étape 1 : Transition visuelle (0.3s)**
|
||||
- Icône + compteur "7" disparaissent avec fade out
|
||||
- Nouveau compteur "5" apparaît (plus grand, centré)
|
||||
|
||||
**Étape 2 : Décompte 5 secondes**
|
||||
- Compteur décrémente : 5 → 4 → 3 → 2 → 1
|
||||
- Contenu actuel continue de jouer **normalement** (pas de baisse volume)
|
||||
- User entend le contenu en cours pendant le décompte
|
||||
|
||||
**Étape 3 : Fin du décompte**
|
||||
- Compteur atteint "0"
|
||||
- Fade out 0.3s du contenu actuel
|
||||
- Fade in 0.3s du contenu géolocalisé
|
||||
- Transition audio fluide (pas de silence)
|
||||
|
||||
**Cas particulier : contenu actuel se termine pendant le décompte**
|
||||
|
||||
Exemple : User clique "Suivant", décompte démarre à 5s, mais contenu actuel se termine après 2s.
|
||||
|
||||
```
|
||||
Timeline :
|
||||
T+0s : User clique "Suivant", décompte démarre (5...4...3...)
|
||||
T+2s : Contenu actuel se termine
|
||||
→ Contenu suivant du buffer démarre immédiatement
|
||||
→ Décompte continue (2...1...)
|
||||
T+5s : Décompte atteint 0
|
||||
→ Fade out du contenu buffer (celui qui a démarré à T+2s)
|
||||
→ Fade in du contenu géolocalisé
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- Évite silence inconfortable pendant décompte
|
||||
- User continue d'écouter du contenu intéressant
|
||||
- Transition naturelle
|
||||
|
||||
**Interface pendant décompte** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ [Player actuel] │
|
||||
│ ▶️ Podcast en cours... │
|
||||
│ 0:00 ────●──────────── 12:34 │
|
||||
│ │
|
||||
│ [|◀] [⏸️] [▶|] │
|
||||
├────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 3 │
|
||||
│ │
|
||||
│ Contenu géolocalisé arrive │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 17.3 Limitation anti-spam
|
||||
|
||||
**Problème identifié** :
|
||||
- Routes riches en points d'intérêt (parcours touristiques)
|
||||
- Risque de notifier toutes les 30 secondes
|
||||
- Fatigue utilisateur + distraction conducteur
|
||||
|
||||
**Solution : quota horaire + cooldown**
|
||||
|
||||
#### 17.3.1 Quota : 6 contenus géolocalisés par heure
|
||||
|
||||
**Règle** :
|
||||
- Maximum **6 contenus géolocalisés** notifiés par heure
|
||||
- Fenêtre glissante : calcul sur les 60 dernières minutes (pas réinitialisation à 0h)
|
||||
- Si quota atteint : notifications suivantes ignorées silencieusement
|
||||
|
||||
**Exemple** :
|
||||
```
|
||||
10h05 : Contenu 1 notifié ✅ (quota : 1/6)
|
||||
10h12 : Contenu 2 notifié ✅ (quota : 2/6)
|
||||
10h18 : Contenu 3 notifié ✅ (quota : 3/6)
|
||||
10h25 : Contenu 4 notifié ✅ (quota : 4/6)
|
||||
10h33 : Contenu 5 notifié ✅ (quota : 5/6)
|
||||
10h41 : Contenu 6 notifié ✅ (quota : 6/6)
|
||||
10h52 : Contenu 7 détecté mais ignoré ❌ (quota atteint)
|
||||
11h06 : Contenu 1 expire (>60 min) → quota libéré (5/6)
|
||||
11h08 : Contenu 8 notifié ✅ (quota : 6/6)
|
||||
```
|
||||
|
||||
**Exception : audio-guides multi-séquences** :
|
||||
- Un audio-guide avec N séquences compte comme **1 seul contenu** dans le quota
|
||||
- Une fois démarré, toutes ses séquences sont jouables (pas de limite)
|
||||
- Exemple : audio-guide 8 séquences = 1 quota, contenus simples restants = 5
|
||||
|
||||
**Implémentation backend (Redis)** :
|
||||
|
||||
```go
|
||||
// Structure quota (sorted set avec timestamps)
|
||||
// Clé : user:{user_id}:geo_quota
|
||||
// Valeur : ZADD avec timestamp
|
||||
|
||||
func CanNotify(userID string) bool {
|
||||
key := fmt.Sprintf("user:%s:geo_quota", userID)
|
||||
now := time.Now().Unix()
|
||||
oneHourAgo := now - 3600
|
||||
|
||||
// Supprimer entrées > 1h
|
||||
redis.ZRemRangeByScore(key, "-inf", oneHourAgo)
|
||||
|
||||
// Compter entrées restantes
|
||||
count := redis.ZCard(key)
|
||||
|
||||
return count < 6
|
||||
}
|
||||
|
||||
func RecordNotification(userID string, contentID string) {
|
||||
key := fmt.Sprintf("user:%s:geo_quota", userID)
|
||||
now := time.Now().Unix()
|
||||
|
||||
// Ajouter entrée avec timestamp actuel
|
||||
redis.ZAdd(key, now, contentID)
|
||||
|
||||
// TTL 1h (sécurité)
|
||||
redis.Expire(key, 3600)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 17.3.2 Cooldown : 10 minutes après notification ignorée
|
||||
|
||||
**Règle** :
|
||||
- Si user ne clique pas sur "Suivant" pendant les 7 secondes
|
||||
- → Cooldown de **10 minutes** activé
|
||||
- → Aucune nouvelle notification pendant ce délai
|
||||
- → Même si quota non atteint
|
||||
|
||||
**Justification** :
|
||||
- User a probablement une raison de ne pas vouloir de contenu géolocalisé maintenant
|
||||
- Évite harcèlement (notifications répétées ignorées)
|
||||
- Respecte choix implicite de l'utilisateur
|
||||
|
||||
**Timeline exemple** :
|
||||
```
|
||||
10h00 : Notification contenu A (7s)
|
||||
10h00+7s : User n'a pas cliqué → cooldown activé
|
||||
10h05 : Contenu B détecté → ignoré (cooldown actif)
|
||||
10h08 : Contenu C détecté → ignoré (cooldown actif)
|
||||
10h10 : Cooldown expire
|
||||
10h11 : Contenu D détecté → notification affichée ✅
|
||||
```
|
||||
|
||||
**Implémentation (Redis)** :
|
||||
|
||||
```go
|
||||
func IsInCooldown(userID string) bool {
|
||||
key := fmt.Sprintf("user:%s:geo_cooldown", userID)
|
||||
exists := redis.Exists(key)
|
||||
return exists == 1
|
||||
}
|
||||
|
||||
func ActivateCooldown(userID string) {
|
||||
key := fmt.Sprintf("user:%s:geo_cooldown", userID)
|
||||
redis.Set(key, "1", 10*time.Minute)
|
||||
}
|
||||
```
|
||||
|
||||
**Exception : notification validée (user a cliqué)** :
|
||||
- Pas de cooldown si user a cliqué sur "Suivant"
|
||||
- Même si user skip le contenu ensuite (pendant le décompte ou après)
|
||||
- Cooldown = pénalité uniquement pour notification complètement ignorée
|
||||
|
||||
---
|
||||
|
||||
### 17.4 Navigation avec contenus géolocalisés
|
||||
|
||||
#### 17.4.1 Historique de navigation
|
||||
|
||||
**Structure** (voir [05-interactions-navigation.md](05-interactions-navigation.md#52-commande-précédent)) :
|
||||
|
||||
- **10 contenus maximum** en mémoire (Redis List)
|
||||
- Structure : `[{content_id, position_seconds, listened_at, type}, ...]`
|
||||
- FIFO : au-delà de 10, suppression du plus ancien
|
||||
|
||||
**Comportement avec contenus géolocalisés** :
|
||||
|
||||
Les contenus géolocalisés s'intègrent dans l'historique comme des contenus normaux :
|
||||
|
||||
```
|
||||
Historique :
|
||||
[
|
||||
{id: "buffer_1", position: 180, type: "contextuel"},
|
||||
{id: "geo_tour_eiffel", position: 42, type: "geo_anchored"},
|
||||
{id: "buffer_2", position: 90, type: "neutre"},
|
||||
{id: "buffer_3", position: 0, type: "contextuel"}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 17.4.2 Commande "Précédent" avec contenu géolocalisé
|
||||
|
||||
**Cas A : User écoute contenu géolocalisé, puis skip**
|
||||
|
||||
Timeline :
|
||||
1. Contenu buffer_1 joue (position 3:00)
|
||||
2. Notification contenu géolocalisé
|
||||
3. User clique "Suivant" → décompte 5s → contenu géolocalisé démarre
|
||||
4. User écoute 42 secondes du contenu géolocalisé
|
||||
5. User appuie "Suivant" (skip) → contenu buffer_2 démarre
|
||||
6. User appuie "Précédent" → **retour au contenu géolocalisé à 42s**
|
||||
|
||||
**Règle** : Comme décrit dans [05-interactions-navigation.md](05-interactions-navigation.md#52-commande-précédent) :
|
||||
- Si temps écouté ≥ 10 secondes → replay contenu actuel depuis début
|
||||
- Si temps écouté < 10 secondes → retour contenu précédent (position exacte)
|
||||
|
||||
Dans ce cas : 42s ≥ 10s → le contenu géolocalisé reprend à 42s (où user l'avait arrêté)
|
||||
|
||||
---
|
||||
|
||||
**Cas B : User ne clique pas pendant notification (ignore)**
|
||||
|
||||
Timeline :
|
||||
1. Contenu buffer_1 joue
|
||||
2. Notification contenu géolocalisé (7s)
|
||||
3. User ne clique pas → notification disparaît
|
||||
4. Contenu buffer_1 continue
|
||||
5. User appuie "Précédent" → **retour contenu avant buffer_1**
|
||||
|
||||
**Règle** : Contenu géolocalisé ignoré n'entre PAS dans l'historique.
|
||||
|
||||
---
|
||||
|
||||
**Cas C : User skip pendant le décompte**
|
||||
|
||||
Timeline :
|
||||
1. Notification contenu géolocalisé
|
||||
2. User clique "Suivant" → décompte 5s démarre (5...4...3...)
|
||||
3. User re-clique "Suivant" pendant le décompte → **annule le décompte**
|
||||
4. Contenu suivant du buffer démarre
|
||||
5. Contenu géolocalisé n'entre PAS dans l'historique
|
||||
|
||||
**Justification** : User a explicitement annulé, pas d'intérêt pour ce contenu.
|
||||
|
||||
---
|
||||
|
||||
### 17.5 Basculement automatique voiture ↔ piéton
|
||||
|
||||
**Principe** : Détection automatique du mode selon vitesse GPS, sans action utilisateur.
|
||||
|
||||
#### 17.5.1 Calcul vitesse moyenne
|
||||
|
||||
**Méthode** : Moyenne glissante sur 30 secondes
|
||||
|
||||
```javascript
|
||||
// Historique GPS : 30 derniers points (1 point/seconde)
|
||||
const gpsHistory = [
|
||||
{lat: 48.8584, lon: 2.2945, speed: 8.3, timestamp: 1642861200},
|
||||
{lat: 48.8585, lon: 2.2946, speed: 8.5, timestamp: 1642861201},
|
||||
// ... 28 autres points
|
||||
];
|
||||
|
||||
// Calcul vitesse moyenne
|
||||
const avgSpeed = gpsHistory
|
||||
.reduce((sum, point) => sum + point.speed, 0) / gpsHistory.length;
|
||||
|
||||
// km/h = m/s × 3.6
|
||||
const avgSpeedKmh = avgSpeed * 3.6;
|
||||
|
||||
if (avgSpeedKmh < 5) {
|
||||
mode = "piéton";
|
||||
} else {
|
||||
mode = "voiture";
|
||||
}
|
||||
```
|
||||
|
||||
**Hysteresis (éviter basculements intempestifs)** :
|
||||
- Nouvelle vitesse doit être stable pendant **10 secondes** avant basculement
|
||||
- Exemple : user passe de 20 km/h à 3 km/h (arrêt feu rouge)
|
||||
- Si vitesse remonte à 20 km/h après 8s → pas de basculement
|
||||
- Si vitesse reste à 3 km/h pendant 10s → basculement piéton
|
||||
|
||||
---
|
||||
|
||||
#### 17.5.2 Effets du basculement
|
||||
|
||||
**Passage voiture → piéton** :
|
||||
|
||||
| Paramètre | Avant (voiture) | Après (piéton) |
|
||||
|-----------|----------------|----------------|
|
||||
| **App requise** | Premier plan | Arrière-plan OK |
|
||||
| **Notification** | Sonore + icône + compteur | Push système standard |
|
||||
| **Rayon détection** | ETA 7s (distance variable) | 200 mètres fixes |
|
||||
| **Type contenu** | Tous contenus géolocalisés | Audio-guides uniquement |
|
||||
|
||||
**Passage piéton → voiture** :
|
||||
|
||||
| Paramètre | Avant (piéton) | Après (voiture) |
|
||||
|-----------|---------------|----------------|
|
||||
| **App requise** | Arrière-plan OK | Premier plan |
|
||||
| **Notification** | Push système standard | Sonore + icône + compteur |
|
||||
| **Rayon détection** | 200 mètres fixes | ETA 7s (distance variable) |
|
||||
| **Type contenu** | Audio-guides uniquement | Tous contenus géolocalisés |
|
||||
|
||||
**Transition fluide** :
|
||||
- Pas de popup ou message à l'utilisateur
|
||||
- Basculement invisible et automatique
|
||||
- Permissions ajustées automatiquement (si déjà accordées)
|
||||
|
||||
---
|
||||
|
||||
### 17.6 Edge cases
|
||||
|
||||
#### 17.6.1 User passe trop vite devant le point (>130 km/h)
|
||||
|
||||
**Scénario** : Autoroute A6, vitesse 130 km/h, contenu géolocalisé détecté.
|
||||
|
||||
**Calcul** :
|
||||
- Vitesse : 130 km/h = 36.1 m/s
|
||||
- ETA 7s → distance notification : 7 × 36.1 = **252 mètres** avant le point
|
||||
- User a 7s pour cliquer "Suivant"
|
||||
- Décompte 5s démarre
|
||||
- À 130 km/h, user parcourt : 5 × 36.1 = 180m pendant décompte
|
||||
- Distance restante au démarrage contenu : 252 - 180 = **72 mètres avant le point**
|
||||
|
||||
**Conclusion** : Le système fonctionne même à très haute vitesse ✅
|
||||
|
||||
**Cas extrême : 180 km/h** (illégal mais théoriquement possible) :
|
||||
- Vitesse : 180 km/h = 50 m/s
|
||||
- ETA 7s → distance notification : 350m avant le point
|
||||
- Décompte 5s : user parcourt 250m
|
||||
- Distance restante : 350 - 250 = **100m avant le point**
|
||||
- Contenu démarre encore avant le point ✅
|
||||
|
||||
---
|
||||
|
||||
#### 17.6.2 Multiples points géolocalisés proches
|
||||
|
||||
**Scénario** : Route départementale avec 3 châteaux espacés de 800m chacun.
|
||||
|
||||
```
|
||||
Château A ----800m---- Château B ----800m---- Château C
|
||||
```
|
||||
|
||||
À 50 km/h (13.9 m/s), user met 57 secondes pour parcourir 800m.
|
||||
|
||||
**Timeline** :
|
||||
```
|
||||
T+0s : Notification Château A (7s avant)
|
||||
T+7s : User clique "Suivant" → décompte 5s
|
||||
T+12s : Contenu Château A démarre
|
||||
T+30s : Contenu Château A se termine (durée 18s)
|
||||
T+30s : Retour buffer normal (contenu contextuel)
|
||||
T+50s : Notification Château B devrait se déclencher
|
||||
MAIS : quota 1/6 utilisé, pas de cooldown (user a validé A)
|
||||
→ Notification affichée ✅
|
||||
T+57s : User clique "Suivant" → contenu Château B démarre
|
||||
...
|
||||
```
|
||||
|
||||
**Problème** : Si user ignore une notification, cooldown 10 min bloque les suivantes.
|
||||
|
||||
**Solution proposée** : **Ajuster le cooldown selon validation précédente**
|
||||
|
||||
Nouvelle règle :
|
||||
- Notification validée (user a cliqué) : pas de cooldown
|
||||
- Notification ignorée (user n'a pas cliqué) : cooldown 10 min
|
||||
- Exception : si 2+ notifications validées consécutives, cooldown réduit à 5 min
|
||||
|
||||
**Implémentation** :
|
||||
|
||||
```go
|
||||
func CalculateCooldown(userID string) time.Duration {
|
||||
// Récupérer historique dernières notifications
|
||||
history := getNotificationHistory(userID, 3) // 3 dernières
|
||||
|
||||
validatedCount := 0
|
||||
for _, notif := range history {
|
||||
if notif.Validated {
|
||||
validatedCount++
|
||||
}
|
||||
}
|
||||
|
||||
if validatedCount >= 2 {
|
||||
return 5 * time.Minute // Cooldown réduit
|
||||
}
|
||||
return 10 * time.Minute // Cooldown standard
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 17.6.3 User arrêté longtemps près d'un point (parking)
|
||||
|
||||
**Scénario** : User se gare à 30m d'un château, sort de voiture, visite 1h, revient.
|
||||
|
||||
**Problème** :
|
||||
- Vitesse < 5 km/h + distance < 50m → notification immédiate
|
||||
- Mais user en mode "stationnement", pas en mode "conduite"
|
||||
|
||||
**Solution** : **Détection mode stationnement**
|
||||
|
||||
Règle :
|
||||
- Si vitesse < 1 km/h pendant **2 minutes** consécutives
|
||||
- → Mode "stationnement" activé
|
||||
- → Pas de notification de contenus géolocalisés
|
||||
- → Basculement automatique en mode piéton (push arrière-plan)
|
||||
|
||||
**Reprise conduite** :
|
||||
- Vitesse > 5 km/h pendant 10s
|
||||
- → Mode "voiture" réactivé
|
||||
- → Notifications reprennent (si quota non atteint)
|
||||
|
||||
---
|
||||
|
||||
### 17.7 Récapitulatif
|
||||
|
||||
| Point | Décision | Justification |
|
||||
|-------|----------|---------------|
|
||||
| **Détection** | ETA 7s avant point (API GPS native) | Précis, fiable, adapté à toutes vitesses |
|
||||
| **Notification** | Sonore + icône + compteur (pas de texte) | Sécurité routière, minimalisme |
|
||||
| **Validation** | Bouton "Suivant" uniquement | Un seul geste, simple, pas de distraction |
|
||||
| **Décompte** | 5s avec contenu actuel qui continue | Évite silence, fluidité |
|
||||
| **Quota** | 6 contenus/heure, fenêtre glissante | Anti-spam efficace |
|
||||
| **Cooldown** | 10 min après notification ignorée | Respecte choix utilisateur |
|
||||
| **Exception audio-guides** | Toutes séquences comptent comme 1 | Encourage création audio-guides |
|
||||
| **Basculement auto** | Vitesse < 5 km/h = piéton, ≥ 5 km/h = voiture | Transparent, pas de friction |
|
||||
| **Mode stationnement** | Vitesse < 1 km/h pendant 2 min → piéton | Évite notifications inutiles |
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 17
|
||||
|
||||
| Point | Décision | Coût | Complexité |
|
||||
|-------|----------|------|------------|
|
||||
| **17.1** Principe général | Notification 7s avant, app ouverte requise | 0€ | Faible |
|
||||
| **17.2.1** Calcul ETA | API GPS native iOS/Android | 0€ | Faible |
|
||||
| **17.2.2** Format notification | Sonore + icône + compteur (minimaliste) | 0€ | Faible |
|
||||
| **17.2.3** Décompte 5s | Contenu actuel continue, transition fluide | 0€ | Faible |
|
||||
| **17.3.1** Quota 6/h | Redis sorted set, fenêtre glissante | 0€ | Moyenne |
|
||||
| **17.3.2** Cooldown 10 min | Redis TTL après ignorance | 0€ | Faible |
|
||||
| **17.4** Historique navigation | 10 contenus max, FIFO, comme contenus normaux | 0€ | Faible |
|
||||
| **17.5** Basculement auto | Vitesse moyenne 30s, hysteresis 10s | 0€ | Moyenne |
|
||||
| **17.6** Edge cases | Haute vitesse, points multiples, stationnement | 0€ | Moyenne |
|
||||
|
||||
**Coût total MVP : 0€** (GPS natif, Redis existant)
|
||||
|
||||
---
|
||||
|
||||
## Points d'attention pour Gherkin
|
||||
|
||||
- Tester calcul ETA à différentes vitesses (10 km/h, 50 km/h, 130 km/h)
|
||||
- Tester cas vitesse < 5 km/h ET distance < 50m (notification immédiate)
|
||||
- Tester affichage notification (icône + compteur, pas de texte)
|
||||
- Tester validation "Suivant" → décompte 5s → contenu démarre
|
||||
- Tester contenu actuel se termine pendant décompte → buffer démarre
|
||||
- Tester quota 6/h : 7e notification ignorée
|
||||
- Tester cooldown 10 min après notification ignorée
|
||||
- Tester exception audio-guides (toutes séquences = 1 quota)
|
||||
- Tester navigation "Précédent" après skip contenu géolocalisé (position sauvegardée)
|
||||
- Tester basculement voiture → piéton (vitesse < 5 km/h stable 10s)
|
||||
- Tester basculement piéton → voiture (vitesse ≥ 5 km/h stable 10s)
|
||||
- Tester mode stationnement (vitesse < 1 km/h pendant 2 min)
|
||||
- Tester multiples points proches (notifications successives si quota OK)
|
||||
- Tester haute vitesse (130 km/h) : notification 252m avant, contenu démarre au bon moment
|
||||
150
docs/regles-metier/08-mode-offline.md
Normal file
150
docs/regles-metier/08-mode-offline.md
Normal file
@@ -0,0 +1,150 @@
|
||||
## 11. Mode offline
|
||||
|
||||
### 11.1 Téléchargement
|
||||
|
||||
**Zone géographique** : Choix manuel utilisateur
|
||||
|
||||
**Options prédéfinies** :
|
||||
- "Autour de moi" (rayon 50 km position actuelle)
|
||||
- "Ma ville" (limite administrative détectée)
|
||||
- "Mon département" (sélection liste)
|
||||
- "Ma région" (sélection liste)
|
||||
- Recherche manuelle : "Paris", "Lyon", "Marseille", etc.
|
||||
|
||||
**Nombre de contenus téléchargeables** :
|
||||
|
||||
| Statut | Limite | Affichage |
|
||||
|--------|--------|-----------|
|
||||
| **Gratuit** | 50 contenus max | "12/50 contenus téléchargés" |
|
||||
| **Premium** | Illimité | "245 contenus (3.2 GB)" |
|
||||
|
||||
**Calcul temps disponible** :
|
||||
- 50 contenus × 5 min moyenne = 250 min = **4h d'écoute** (suffisant pour gratuits)
|
||||
- Premium illimité = limité uniquement par espace disque device
|
||||
|
||||
**Connexion WiFi/Mobile** :
|
||||
|
||||
**Par défaut** : WiFi uniquement
|
||||
|
||||
**Sur données mobiles** :
|
||||
1. User clique "Télécharger"
|
||||
2. Détection : pas de WiFi
|
||||
3. Popup : "Vous n'êtes pas connecté en WiFi. Télécharger via données mobiles consommera environ **X MB**. Continuer ?"
|
||||
4. Boutons : "Attendre WiFi" / "Continuer"
|
||||
|
||||
**Calcul estimation** :
|
||||
```
|
||||
Nombre contenus × durée moyenne × bitrate qualité
|
||||
Exemple : 20 contenus × 5 min × 48 kbps = ~72 MB
|
||||
```
|
||||
|
||||
**Qualité audio téléchargement** :
|
||||
|
||||
| Qualité | Bitrate | Taille | Disponibilité |
|
||||
|---------|---------|--------|---------------|
|
||||
| **Basse** | 24 kbps | ~10 MB/h | Gratuit + Premium |
|
||||
| **Standard** | 48 kbps | ~20 MB/h | Gratuit + Premium (défaut) |
|
||||
| **Haute** | 64 kbps | ~30 MB/h | **Premium uniquement** |
|
||||
|
||||
**Justification** :
|
||||
- Standard = bon compromis qualité/taille (Opus 48 kbps = très correct pour voix)
|
||||
- Haute réservée Premium = incitation upgrade
|
||||
- User peut réduire à "basse" si espace limité
|
||||
|
||||
---
|
||||
|
||||
### 11.2 Validité et renouvellement
|
||||
|
||||
**Durée de validité** : 30 jours après téléchargement
|
||||
|
||||
**Standard industrie** :
|
||||
- Spotify : 30 jours
|
||||
- YouTube Music : 30 jours
|
||||
- Deezer : 30 jours
|
||||
|
||||
**Renouvellement automatique** :
|
||||
|
||||
```
|
||||
App détecte WiFi + contenus >25 jours
|
||||
→ Requête API : GET /offline/contents/refresh
|
||||
→ Backend vérifie pour chaque contenu :
|
||||
- Abonnement Premium toujours actif ?
|
||||
- Contenu pas modéré/supprimé ?
|
||||
- Métadonnées à jour ?
|
||||
→ Renouvelle validité à 30 jours supplémentaires
|
||||
→ Mise à jour métadonnées (titre, créateur, statut)
|
||||
→ Pas de re-téléchargement audio (sauf si fichier corrompu)
|
||||
```
|
||||
|
||||
**Notification avant expiration** :
|
||||
- **J-3** : "X contenus expirent dans 3 jours. Connectez-vous en WiFi pour les renouveler"
|
||||
- **J-0** : Suppression automatique
|
||||
- **J+0** : Toast "15 contenus expirés ont été supprimés"
|
||||
|
||||
**Justification** :
|
||||
- **Force reconnexion** : vérifier abonnement actif, contenus légaux
|
||||
- **Évite stockage obsolète** : contenus supprimés/modérés ne restent pas
|
||||
- **UX transparente** : renouvellement silencieux si WiFi régulier
|
||||
|
||||
---
|
||||
|
||||
### 11.3 Synchronisation actions offline
|
||||
|
||||
**Actions stockées localement (SQLite)** :
|
||||
- Likes/unlikes
|
||||
- Abonnements/désabonnements
|
||||
- Signalements
|
||||
- Progression audio-guides
|
||||
|
||||
**Sync automatique à la reconnexion** :
|
||||
|
||||
```
|
||||
1. App détecte reconnexion Internet
|
||||
2. Récupération queue locale : SELECT * FROM pending_actions ORDER BY created_at
|
||||
3. Envoi batch API : POST /sync/actions
|
||||
4. Backend traite chaque action
|
||||
5. Confirmation réception : DELETE FROM pending_actions WHERE id IN (...)
|
||||
6. Toast : "3 likes et 1 abonnement synchronisés"
|
||||
```
|
||||
|
||||
**Gestion erreurs sync** :
|
||||
- Si échec après 3 tentatives → notification : "Impossible de synchroniser. Réessayez plus tard"
|
||||
- Actions conservées jusqu'à sync réussie (pas de perte)
|
||||
- **Rétention max 7 jours** : après = purge (évite queue infinie)
|
||||
|
||||
**Conflits contenus supprimés** :
|
||||
|
||||
```
|
||||
Backend retourne : {deleted_content_ids: [123, 456]}
|
||||
→ App supprime fichiers locaux
|
||||
→ Si contenu 123 en cours d'écoute :
|
||||
- Attendre fin lecture actuelle
|
||||
- Passage auto suivant après 2s
|
||||
→ Toast : "1 contenu téléchargé a été retiré (violation règles)"
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- **Pas de conflit possible** : actions unilatérales user (likes/abonnements)
|
||||
- **UX fluide** : pas de blocage offline
|
||||
- **Batch = économie** : requêtes HTTP groupées
|
||||
- **Conformité modération** : contenu illégal disparaît même offline
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 11
|
||||
|
||||
| Aspect | Décision | Valeur |
|
||||
|--------|----------|--------|
|
||||
| **Zone téléchargement** | Choix | Manuel (autour/ville/département/région/recherche) |
|
||||
| **Limite gratuit** | Contenus | 50 max |
|
||||
| **Limite Premium** | Contenus | Illimité (espace disque) |
|
||||
| **Connexion** | Par défaut | WiFi (mobile avec confirmation) |
|
||||
| **Qualité Standard** | Bitrate | 48 kbps Opus |
|
||||
| **Qualité Haute** | Bitrate | 64 kbps (Premium uniquement) |
|
||||
| **Validité** | Durée | 30 jours |
|
||||
| **Renouvellement** | Mode | Automatique si WiFi |
|
||||
| **Notification expiration** | Délai | J-3 |
|
||||
| **Sync actions** | Mode | Batch automatique reconnexion |
|
||||
| **Rétention queue** | Durée | 7 jours max |
|
||||
|
||||
---
|
||||
319
docs/regles-metier/09-abonnements-notifications.md
Normal file
319
docs/regles-metier/09-abonnements-notifications.md
Normal file
@@ -0,0 +1,319 @@
|
||||
## 8. Abonnements et notifications
|
||||
|
||||
### 8.1 Impact sur l'algorithme
|
||||
|
||||
**Décision** : Boost +30% au score + reste dans le mix
|
||||
|
||||
**Boost de score abonnements** :
|
||||
- **+30% au score final** pour contenus d'un créateur suivi
|
||||
- Application : multiplicateur sur le score calculé
|
||||
|
||||
```
|
||||
score_final_avec_boost = score_final × 1.3
|
||||
```
|
||||
|
||||
**Reste dans le mix** :
|
||||
- ❌ **Pas de priorité absolue** (pas de file dédiée abonnements)
|
||||
- ✅ Contenu suivi entre en **compétition avec autres contenus**
|
||||
- ✅ Si créateur suivi publie contenu faible engagement → peut être battu par contenu viral non-suivi
|
||||
|
||||
**Exemple concret** :
|
||||
```
|
||||
Utilisateur à Paris, 2 contenus disponibles :
|
||||
|
||||
Contenu A (créateur NON suivi) :
|
||||
- Score géo : 0.9 (très proche)
|
||||
- Score intérêts : 0.8
|
||||
- Score engagement : 0.7
|
||||
→ Score final : 0.80
|
||||
|
||||
Contenu B (créateur suivi) :
|
||||
- Score géo : 0.5 (moyennement proche)
|
||||
- Score intérêts : 0.6
|
||||
- Score engagement : 0.5
|
||||
→ Score final : 0.53
|
||||
→ Score avec boost : 0.53 × 1.3 = 0.69
|
||||
|
||||
→ Contenu A proposé en premier (0.80 > 0.69)
|
||||
```
|
||||
|
||||
**Cas où abonnement fait la différence** :
|
||||
```
|
||||
Contenu A (non suivi) : score 0.70
|
||||
Contenu B (suivi) : score 0.60 → avec boost 0.78
|
||||
→ Contenu B proposé (boost fait pencher la balance)
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- **Équilibre** : valorise abonnements sans enfermer utilisateur
|
||||
- **Découverte** : contenus viraux/locaux peuvent toujours émerger
|
||||
- **Prévisible** : boost fixe, pas de logique opaque
|
||||
- **Coût 0** : multiplicateur simple dans l'algo
|
||||
|
||||
---
|
||||
|
||||
### 8.2 Notifications contextuelles
|
||||
|
||||
**Décision** : Push adapté selon contexte (voiture vs à pied) + limite 10/jour
|
||||
|
||||
**Détection contexte utilisateur** :
|
||||
|
||||
| Contexte | Détection | Comportement |
|
||||
|----------|-----------|--------------|
|
||||
| **En voiture** | Vitesse GPS >10 km/h | Notifications silencieuses (in-app uniquement) + commandes volant |
|
||||
| **À pied** | Vitesse GPS <5 km/h | Notifications push actives + interface tactile/vocale |
|
||||
|
||||
**Notifications activées** :
|
||||
|
||||
#### En voiture (mode conduite)
|
||||
|
||||
| Événement | Notification | Comportement |
|
||||
|-----------|--------------|--------------|
|
||||
| **Nouveau contenu créateur suivi** | In-app uniquement | Badge compteur, pas de push (sécurité) |
|
||||
| **Live créateur suivi** | In-app uniquement | Badge compteur, pas de push |
|
||||
| **Point d'intérêt proche** | Audio notification | Bip + annonce vocale : "Audio-guide disponible" |
|
||||
|
||||
#### À pied (mode piéton)
|
||||
|
||||
| Événement | Notification | Comportement |
|
||||
|-----------|--------------|--------------|
|
||||
| **Nouveau contenu créateur suivi** | ✅ Push | Si utilisateur dans zone géo du contenu |
|
||||
| **Live créateur suivi** | ✅ Push | Si utilisateur dans zone géo |
|
||||
| **Audio-guide disponible** | ✅ Push | "📍 Audio-guide disponible : [Lieu]" |
|
||||
| **Séquence suivante suggérée** | Audio notification | Annonce vocale : "Pièce suivante disponible" |
|
||||
|
||||
**Format notifications** :
|
||||
|
||||
**Nouveau contenu** :
|
||||
```
|
||||
🎧 [Nom créateur] a publié : "[Titre contenu]"
|
||||
Tap pour écouter
|
||||
```
|
||||
|
||||
**Live en direct** :
|
||||
```
|
||||
🔴 [Nom créateur] est en direct : "[Titre live]"
|
||||
Tap pour rejoindre
|
||||
```
|
||||
|
||||
**Audio-guide à pied** :
|
||||
```
|
||||
📍 Audio-guide disponible : [Nom du lieu]
|
||||
Choisissez parmi 3 guides pour [Musée du Louvre]
|
||||
Tap pour explorer
|
||||
```
|
||||
|
||||
**Filtrage géographique** :
|
||||
- Si contenu/live hors zone utilisateur → **pas de notification**
|
||||
- Évite frustration : "notification pour contenu que je ne peux pas écouter"
|
||||
- Exception : contenu national → notifie tous les abonnés
|
||||
|
||||
**Fréquence maximale** :
|
||||
- **Maximum 10 notifications push/jour** par utilisateur (tous types confondus)
|
||||
- Si dépassement : notifications regroupées
|
||||
- Message groupé : "🎧 3 nouveaux contenus de créateurs suivis"
|
||||
|
||||
**Plages horaires** :
|
||||
- **Mode silencieux** : 22h-8h (pas de push, sauf live)
|
||||
- Paramétrable utilisateur (désactivation totale possible)
|
||||
- Option "Notifications importantes uniquement" (lives uniquement)
|
||||
|
||||
**Gestion préférences** :
|
||||
|
||||
| Préférence | Défaut | Description |
|
||||
|------------|--------|-------------|
|
||||
| **Nouveaux contenus** | ✅ Activé | Push à chaque nouveau contenu (à pied uniquement) |
|
||||
| **Lives** | ✅ Activé | Push au démarrage live (à pied uniquement) |
|
||||
| **Audio-guides proximité** | ✅ Activé | Push quand audio-guide détecté à <100m |
|
||||
| **Mode silencieux** | ✅ Activé (22h-8h) | Pas de push nocturne |
|
||||
| **Limite quotidienne** | 10 | Modifiable 5-20 |
|
||||
|
||||
**Justification** :
|
||||
- **Sécurité routière** : pas de push en conduite (distraction)
|
||||
- **Engagement piéton** : push actifs pour audio-guides (valeur ajoutée tourisme)
|
||||
- **Pas de spam** : limite 10/jour + mode silencieux
|
||||
- **Filtrage géo** : pertinence maximale (pas de notif inutiles)
|
||||
- **Coût** : Firebase Cloud Messaging (gratuit jusqu'à volume élevé)
|
||||
|
||||
---
|
||||
|
||||
### 8.3 Mode Audio-guide (piéton)
|
||||
|
||||
**Décision** : Navigation manuelle multiséquence + choix parmi plusieurs guides
|
||||
|
||||
**Fonctionnement** :
|
||||
|
||||
#### Détection et proposition
|
||||
|
||||
1. Utilisateur à pied (<5 km/h) passe à <**100m** d'un lieu avec audio-guides
|
||||
2. **Notification push** : "📍 Audio-guide disponible : [Musée du Louvre]"
|
||||
3. Tap notification → **Page de sélection** audio-guides
|
||||
|
||||
#### Page de sélection
|
||||
|
||||
**Affichage** :
|
||||
```
|
||||
📍 Musée du Louvre
|
||||
|
||||
Choisissez votre guide :
|
||||
|
||||
┌─────────────────────────────────┐
|
||||
│ 🎨 Visite complète (45 min) │
|
||||
│ Par [Créateur A] • 12 séquences│
|
||||
│ ⭐ 4.8 • 1.2K écoutes │
|
||||
└─────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────┐
|
||||
│ 🏛️ Œuvres majeures (20 min) │
|
||||
│ Par [Créateur B] • 5 séquences │
|
||||
│ ⭐ 4.9 • 3.5K écoutes │
|
||||
└─────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────┐
|
||||
│ 👶 Visite famille (30 min) │
|
||||
│ Par [Créateur C] • 8 séquences │
|
||||
│ ⭐ 4.7 • 850 écoutes │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Interface audio-guide
|
||||
|
||||
**Après sélection** :
|
||||
```
|
||||
🎨 Visite complète • Musée du Louvre
|
||||
|
||||
Piste actuelle : 2/12
|
||||
"La Joconde - Histoire et mystères"
|
||||
[████████────────────] 3:24 / 6:50
|
||||
|
||||
Liste des séquences :
|
||||
✅ 1. Introduction et architecture
|
||||
▶️ 2. La Joconde - Histoire et mystères
|
||||
⏸️ 3. Vénus de Milo
|
||||
⏸️ 4. Victoire de Samothrace
|
||||
⏸️ 5. Peintures Renaissance
|
||||
...
|
||||
⏸️ 12. Conclusion et boutique
|
||||
```
|
||||
|
||||
**Navigation** :
|
||||
|
||||
| Action | Geste | Effet |
|
||||
|--------|-------|-------|
|
||||
| **Séquence suivante** | Tap "Suivant" ou commande vocale "Suivant" | Passe à séquence N+1 |
|
||||
| **Séquence précédente** | Tap "Précédent" ou commande vocale "Précédent" | Revient à séquence N-1 |
|
||||
| **Saut direct** | Tap séquence dans liste | Lecture séquence choisie |
|
||||
| **Pause** | Tap bouton pause | Met en pause, reprise position exacte |
|
||||
| **Quitter** | Tap "×" | Sauvegarde progression, sortie guide |
|
||||
|
||||
**Guidage vocal automatique** :
|
||||
- Entre 2 séquences : "Vous avez terminé la séquence 2. Dirigez-vous vers la Vénus de Milo pour la séquence 3."
|
||||
- Si utilisateur s'éloigne (>50m de la prochaine pièce) : "Vous vous éloignez de la prochaine étape. Consultez le plan."
|
||||
|
||||
**Sauvegarde progression** :
|
||||
- Position dans guide sauvegardée automatiquement
|
||||
- Retour ultérieur : "Reprendre à la séquence 5 ?" ou "Recommencer depuis le début"
|
||||
- Historique : guide marqué "Terminé" si toutes séquences écoutées
|
||||
|
||||
**Création audio-guide multiséquence** :
|
||||
|
||||
**Processus créateur** :
|
||||
1. Créateur upload **plusieurs fichiers audio** (1 par séquence)
|
||||
2. Numérote les séquences : "Séquence 1", "Séquence 2", etc.
|
||||
3. Titre chaque séquence : "Introduction", "La Joconde", etc.
|
||||
4. Définit **point GPS unique** pour tout le guide (centre du lieu)
|
||||
5. Métadonnées : durée totale calculée automatiquement
|
||||
|
||||
**Format stockage** :
|
||||
```json
|
||||
{
|
||||
"guide_id": "abc123",
|
||||
"title": "Visite complète Musée du Louvre",
|
||||
"location": {"lat": 48.8606, "lon": 2.3376, "radius": 200},
|
||||
"sequences": [
|
||||
{
|
||||
"sequence_number": 1,
|
||||
"title": "Introduction et architecture",
|
||||
"audio_url": "https://cdn.../seq1.mp3",
|
||||
"duration_seconds": 180
|
||||
},
|
||||
{
|
||||
"sequence_number": 2,
|
||||
"title": "La Joconde - Histoire et mystères",
|
||||
"audio_url": "https://cdn.../seq2.mp3",
|
||||
"duration_seconds": 410
|
||||
},
|
||||
...
|
||||
],
|
||||
"total_duration_seconds": 2700,
|
||||
"creator_id": "creator_xyz"
|
||||
}
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- **UX piéton** : navigation tactile adaptée (pas de commandes volant)
|
||||
- **Autonomie** : utilisateur maître de son rythme (pas d'enchaînement forcé)
|
||||
- **Choix** : plusieurs guides = diversité styles (famille, expert, rapide)
|
||||
- **Engagement** : sauvegarde progression = incitation terminer
|
||||
- **Coût** : réutilise infra contenu standard (juste métadonnées séquences)
|
||||
|
||||
---
|
||||
|
||||
### 8.4 Limites et désabonnement
|
||||
|
||||
**Décision** : 200 abonnements max + désabonnement -5% jauges
|
||||
|
||||
**Nombre maximum d'abonnements** :
|
||||
- **200 créateurs maximum** par utilisateur
|
||||
- Raisons :
|
||||
- **Évite spam** : au-delà de 200, notifications ingérables
|
||||
- **Usage réaliste** : 200 créateurs = déjà énorme (vs 100-150 sur YouTube/Twitter)
|
||||
- **Performance** : requêtes SQL optimisées (index sur 200 max)
|
||||
|
||||
**Si limite atteinte** :
|
||||
- Message : "Vous suivez déjà 200 créateurs. Désabonnez-vous d'un créateur pour en suivre un nouveau."
|
||||
- Liste triable : par date abonnement, nb contenus écoutés, dernière activité
|
||||
- Suggestion : "Vous n'avez pas écouté [Créateur X] depuis 6 mois, le désabonner ?"
|
||||
|
||||
**Abonnement initial** :
|
||||
- Impact : **+5% toutes jauges tags du créateur** (défini en [ADR-010](../adr/010-commandes-volant.md))
|
||||
- Action : Bouton "S'abonner" dans profil créateur (interface mobile)
|
||||
- Immédiat à l'action
|
||||
|
||||
**Désabonnement** :
|
||||
- Impact : **-5% toutes jauges tags du créateur** (symétrique)
|
||||
- Action : Bouton "Se désabonner" dans profil créateur
|
||||
- Immédiat à l'action
|
||||
- Pas de confirmation (action réversible)
|
||||
|
||||
**Exemple** :
|
||||
```
|
||||
Créateur tague ses contenus : Automobile, Voyage
|
||||
|
||||
Abonnement :
|
||||
→ Jauge Automobile : 60% → 65% (+5%)
|
||||
→ Jauge Voyage : 55% → 60% (+5%)
|
||||
|
||||
3 mois plus tard, désabonnement :
|
||||
→ Jauge Automobile : 65% → 60% (-5%)
|
||||
→ Jauge Voyage : 60% → 55% (-5%)
|
||||
```
|
||||
|
||||
**Gestion multi-tags** :
|
||||
- Si créateur a 3 tags → **+5% sur chacun des 3 tags**
|
||||
- Logique : abonnement = signal fort d'affinité à TOUS les sujets du créateur
|
||||
|
||||
**Abonnements réciproques** :
|
||||
- ❌ **Pas d'abonnement mutuel visible**
|
||||
- Créateur ne voit pas qui est abonné (privacy)
|
||||
- Créateur voit uniquement : nombre total abonnés (métrique globale)
|
||||
|
||||
**Justification** :
|
||||
- **Limite 200** : équilibre entre liberté et gestion spam
|
||||
- **Symétrie +5%/-5%** : cohérence mathématique, prévisibilité
|
||||
- **Privacy** : pas de liste publique abonnés (évite stalking)
|
||||
- **Coût** : table abonnements PostgreSQL standard
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 8
|
||||
135
docs/regles-metier/10-gestion-erreurs.md
Normal file
135
docs/regles-metier/10-gestion-erreurs.md
Normal file
@@ -0,0 +1,135 @@
|
||||
## 12. Gestion des erreurs
|
||||
|
||||
### 12.1 Aucun contenu disponible
|
||||
|
||||
**Stratégie** : Élargissement automatique progressif
|
||||
|
||||
**Flow** :
|
||||
|
||||
```
|
||||
1. Recherche rayon 50 km → aucun résultat
|
||||
2. Élargissement auto 100 km
|
||||
3. Si toujours rien → département
|
||||
4. Si toujours rien → région
|
||||
5. Dernier recours → contenu national (toujours disponible)
|
||||
```
|
||||
|
||||
**Messages adaptatifs** :
|
||||
|
||||
| Cas | Message |
|
||||
|-----|---------|
|
||||
| **Trouvé à 100 km** | "Aucun contenu dans votre zone immédiate. Voici du contenu à proximité (100 km)" |
|
||||
| **Trouvé département** | "Aucun contenu local disponible. Voici du contenu dans votre département" |
|
||||
| **Contenu national** | "Aucun contenu local disponible. Voici du contenu national qui pourrait vous intéresser" |
|
||||
|
||||
**Justification** :
|
||||
- **UX fluide** : pas de message d'erreur bloquant "Aucun contenu"
|
||||
- **User ne reste jamais sans contenu**
|
||||
- **Contenu national = filet de sécurité** : actualités Le Monde, podcasts génériques
|
||||
|
||||
---
|
||||
|
||||
### 12.2 Contenu signalé/supprimé pendant l'écoute
|
||||
|
||||
**Décision** : Pas d'interruption brutale
|
||||
|
||||
**Flow** :
|
||||
|
||||
```
|
||||
1. Contenu supprimé côté backend (modération)
|
||||
2. Si contenu en écoute → laisser terminer lecture en cours
|
||||
3. Après fin lecture → désactiver bouton "Précédent" pour ce contenu
|
||||
4. Passage automatique suivant après 2s
|
||||
5. Toast notification discrète : "Contenu précédent retiré (violation règles)"
|
||||
```
|
||||
|
||||
**Si tentative "Précédent" manuellement** :
|
||||
- Message : "Ce contenu n'est plus disponible"
|
||||
- Retour au contenu actuel
|
||||
|
||||
**Justification** :
|
||||
- **Sécurité routière** : pas d'interruption brutale pendant conduite
|
||||
- **User informé mais pas alarmé** : message discret
|
||||
- **Empêche réécoute** : contenu modéré inaccessible
|
||||
|
||||
---
|
||||
|
||||
### 12.3 Perte de réseau
|
||||
|
||||
**Buffer adaptatif** (cf. TECHNICAL.md) :
|
||||
|
||||
| Réseau | Buffer min | Buffer cible | Buffer max |
|
||||
|--------|------------|--------------|------------|
|
||||
| **WiFi** | 5s | 30s | 120s |
|
||||
| **4G/5G** | 10s | 45s | 120s |
|
||||
| **3G** | 30s | 90s | 300s |
|
||||
|
||||
**Comportement détaillé** :
|
||||
|
||||
**Phase 1 : Connexion instable** (latence élevée, paquets perdus)
|
||||
- Aucun message immédiat
|
||||
- Lecture continue sur buffer
|
||||
- Si > 10s latence : toast discret "Connexion instable"
|
||||
|
||||
**Phase 2 : Perte totale réseau**
|
||||
- Lecture continue jusqu'à épuisement buffer
|
||||
- Toast : "Hors ligne, lecture sur buffer (30s restantes)"
|
||||
- Compte à rebours visible
|
||||
|
||||
**Phase 3 : Buffer épuisé sans reconnexion**
|
||||
- Pause automatique
|
||||
- Overlay : "Connexion perdue. Reconnexion en cours..."
|
||||
- Retry automatique toutes les 5s (max 6 tentatives = 30s)
|
||||
|
||||
**Phase 4 : Basculement mode offline** (après 30s échec)
|
||||
- Popup : "Voulez-vous continuer avec vos contenus téléchargés ?"
|
||||
- Boutons : "Réessayer" / "Mode offline"
|
||||
- Si "Mode offline" → lecture contenus téléchargés
|
||||
|
||||
**Reconnexion réussie** :
|
||||
- Reprise automatique lecture au point d'arrêt exact
|
||||
- Toast : "Connexion rétablie"
|
||||
|
||||
**Justification** :
|
||||
- **Expérience fluide zones blanches** (tunnels, campagne)
|
||||
- **Buffer généreux** : absorbe fluctuations réseau mobile
|
||||
- **Mode offline secours** : si coupure prolongée
|
||||
|
||||
---
|
||||
|
||||
### 12.4 Géolocalisation désactivée
|
||||
|
||||
**Mode dégradé automatique**
|
||||
|
||||
**Contenu disponible** :
|
||||
|
||||
| Type contenu | Disponible |
|
||||
|--------------|-----------|
|
||||
| **Contenu national** (podcasts, actualités) | ✅ |
|
||||
| **Contenu téléchargé** (offline) | ✅ |
|
||||
| **Contenus "Neutre"** géographiquement | ✅ |
|
||||
| **Contenu géolocalisé** (Ancré/Contextuel) | ❌ |
|
||||
| **Audio-guides** | ❌ |
|
||||
| **Notifications push géo-déclenchées** | ❌ |
|
||||
|
||||
**Popup au lancement** :
|
||||
- **Apparition** : Premier lancement après refus géolocalisation
|
||||
- **Message** : "RoadWave fonctionne mieux avec la géolocalisation activée. Sans elle, seul le contenu national sera disponible."
|
||||
- **Boutons** :
|
||||
- "Activer" → Redirection paramètres OS
|
||||
- "Continuer sans" → Mode dégradé
|
||||
- **Checkbox** : "Ne plus me demander"
|
||||
|
||||
**Banner permanent si refus** :
|
||||
- Bandeau haut écran : "Mode limité : géolocalisation désactivée. [Activer]"
|
||||
- Pas intrusif mais rappel constant
|
||||
- Disparaît si géolocalisation réactivée
|
||||
|
||||
**Justification** :
|
||||
- **App reste fonctionnelle** sans GPS (pas de blocage)
|
||||
- **Incitation forte** à activer (meilleure UX)
|
||||
- **Respecte choix user** (RGPD : consentement libre)
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 12
|
||||
285
docs/regles-metier/11-creation-publication-contenu.md
Normal file
285
docs/regles-metier/11-creation-publication-contenu.md
Normal file
@@ -0,0 +1,285 @@
|
||||
## 4. Création et publication de contenu
|
||||
|
||||
### 4.1 Upload et encodage
|
||||
|
||||
**Décision** : Formats universels avec encodage asynchrone
|
||||
|
||||
**Formats acceptés** :
|
||||
- ✅ MP3 (`.mp3`)
|
||||
- ✅ AAC (`.aac`, `.m4a`)
|
||||
- ❌ WAV, FLAC (trop lourds, inutiles en voiture)
|
||||
|
||||
**Limites** :
|
||||
|
||||
| Paramètre | Valeur | Justification |
|
||||
|-----------|--------|---------------|
|
||||
| **Taille maximale** | 200 MB | ~4h de podcast à 128 kbps |
|
||||
| **Durée maximale** | 4 heures | Suffisant pour podcasts longs |
|
||||
| **Validation format** | Client + backend | Double sécurité |
|
||||
|
||||
**Pipeline d'encodage** :
|
||||
|
||||
```
|
||||
1. Upload fichier (MP3/AAC) → OVH Object Storage temporaire
|
||||
2. Job asynchrone (worker Go + FFmpeg) :
|
||||
- Validation format et intégrité
|
||||
- Réencodage Opus 3 profils (24/48/64 kbps)
|
||||
- Génération segments HLS (.m3u8 + .ts)
|
||||
- Génération image couverture par défaut
|
||||
3. Suppression fichier original (économie stockage)
|
||||
4. Notification créateur : "Contenu prêt à publier"
|
||||
```
|
||||
|
||||
**Temps d'encodage estimé** :
|
||||
- Contenu 5 min → ~30 secondes
|
||||
- Podcast 1h → ~5 minutes
|
||||
- Podcast 4h → ~20 minutes
|
||||
|
||||
**Profils Opus générés** :
|
||||
|
||||
| Qualité | Bitrate | Usage |
|
||||
|---------|---------|-------|
|
||||
| Basse | 24 kbps | 2G/Edge |
|
||||
| Standard | 48 kbps | 3G (défaut) |
|
||||
| Haute | 64 kbps | 4G/5G |
|
||||
|
||||
**Écoute accélérée** :
|
||||
|
||||
| Vitesse | Usage |
|
||||
|---------|-------|
|
||||
| 0.75x | Compréhension difficile (accent, technique) |
|
||||
| 1.0x | Normal (défaut) |
|
||||
| 1.25x | Gain léger |
|
||||
| 1.5x | Podcasts longs |
|
||||
| 2.0x | Survol rapide (modérateurs) |
|
||||
|
||||
**Disponible pour** :
|
||||
- ✅ Modérateurs (validation rapide : 30s → 15s à 2x)
|
||||
- ✅ Auditeurs (tous les contenus)
|
||||
- ✅ Standard industrie (YouTube, Spotify, Apple Podcasts)
|
||||
|
||||
**Justification** :
|
||||
- **Simplicité** : 2 formats couvrent 95% des cas d'usage
|
||||
- **Coût optimisé** : pas de conversion WAV/FLAC lourds
|
||||
- **Stockage réduit** : suppression original après encodage
|
||||
- **Scalabilité** : workers horizontalement (Kubernetes jobs)
|
||||
- **Productivité** : écoute accélérée = double productivité modération
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Métadonnées obligatoires
|
||||
|
||||
**Décision** : Minimaliste pour réduire friction
|
||||
|
||||
**Champs obligatoires** :
|
||||
|
||||
| Champ | Format | Validation |
|
||||
|-------|--------|------------|
|
||||
| **Titre** | 5-100 caractères | Alphanumérique + ponctuation basique |
|
||||
| **Type géo** | Enum | Ancré / Contextuel / Neutre |
|
||||
| **Zone diffusion** | Composite | Voir détails ci-dessous |
|
||||
| **Tags** | Enum | 1 à 3 parmi liste prédéfinie |
|
||||
| **Classification âge** | Enum | Tout public / 13+ / 16+ / 18+ |
|
||||
|
||||
**Zone de diffusion (obligatoire)** :
|
||||
|
||||
Options mutuellement exclusives :
|
||||
- **Point GPS** : latitude + longitude + rayon (100m à 10km)
|
||||
- **Ville** : sélection dans référentiel INSEE
|
||||
- **Département** : sélection liste
|
||||
- **Région** : sélection liste
|
||||
- **National** : France entière
|
||||
|
||||
**Tags disponibles** (1 à 3 obligatoires) :
|
||||
- Automobile
|
||||
- Voyage
|
||||
- Famille
|
||||
- Amour
|
||||
- Musique
|
||||
- Économie
|
||||
- Cryptomonnaie
|
||||
- Politique
|
||||
- Culture générale
|
||||
- Sport
|
||||
- Technologie
|
||||
- Santé
|
||||
|
||||
**Champs optionnels** :
|
||||
- ❌ Description (ajout ultérieur)
|
||||
- ❌ Image couverture (génération auto)
|
||||
|
||||
**Image de couverture par défaut** :
|
||||
|
||||
Génération automatique selon règles :
|
||||
- Icône selon type géo : 📍 Ancré / 🌍 Contextuel / 🎧 Neutre
|
||||
- Couleur selon tag principal : bleu (Auto), vert (Voyage), rouge (Musique), etc.
|
||||
- Format 800×800px, PNG
|
||||
- Personnalisable ultérieurement (post-MVP)
|
||||
|
||||
**Exemple de publication** :
|
||||
```
|
||||
Titre : "Histoire de la Tour Eiffel"
|
||||
Type géo : Ancré
|
||||
Zone : Point GPS (48.8584, 2.2945, rayon 500m)
|
||||
Tags : Voyage, Culture générale
|
||||
Classification : Tout public
|
||||
→ Image auto : 📍 fond bleu-vert (Voyage)
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- **Friction minimale** : 5 champs max = 2 min de publication
|
||||
- **Publication rapide** : pas de blocage sur description/image
|
||||
- **Coût 0** : pas de génération IA au MVP
|
||||
- **Évolutif** : champs optionnels ajoutables ultérieurement
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Validation des 3 premiers contenus
|
||||
|
||||
**Décision** : Validation manuelle par équipe modération RoadWave
|
||||
|
||||
**Processus nouveau créateur** :
|
||||
|
||||
1. Créateur upload ses 3 premiers contenus
|
||||
2. Contenus passent en **file d'attente modération**
|
||||
3. Modérateur junior RoadWave :
|
||||
- Écoute 30 secondes (ou 15s à 2x)
|
||||
- Vérifie métadonnées
|
||||
- Valide ou rejette avec raison
|
||||
4. Si accepté : contenu publié + notification créateur
|
||||
5. Si refusé : notification avec raison détaillée + lien vers règles
|
||||
6. Après 3 contenus validés : créateur passe en **statut vérifié**
|
||||
|
||||
**Critères de validation** :
|
||||
|
||||
| Critère | Détails |
|
||||
|---------|---------|
|
||||
| **Qualité audio** | Compréhensible (pas de grésillement excessif) |
|
||||
| **Respect règles** | Pas de contenu prohibé évident (haine, spam, illégal) |
|
||||
| **Classification âge** | Cohérente avec contenu écouté |
|
||||
| **Tags pertinents** | Correspondance minimale avec contenu |
|
||||
| **Zone diffusion** | Cohérente (pas "Tour Eiffel" avec zone "National") |
|
||||
|
||||
**Délai de validation** :
|
||||
- Objectif : **24-48h** (jours ouvrés)
|
||||
- Priorité : FIFO (First In First Out)
|
||||
- Weekend : délai peut atteindre 72h
|
||||
- Message au créateur : "Validation en cours, délai estimé 24-48h"
|
||||
|
||||
**Notification créateur** :
|
||||
|
||||
**Si accepté** :
|
||||
- Email + push : "✅ Votre contenu '[Titre]' est en ligne !"
|
||||
- Lien direct vers le contenu
|
||||
- Compteur : "2/3 contenus validés pour devenir créateur vérifié"
|
||||
|
||||
**Si refusé** :
|
||||
- Email + push : "❌ Contenu '[Titre]' refusé"
|
||||
- Raison détaillée : "Qualité audio insuffisante" / "Tags non pertinents" / "Classification incorrecte" / etc.
|
||||
- Lien vers règles de publication
|
||||
- Possibilité de correction + resoumission
|
||||
|
||||
**Après 3 validations** :
|
||||
|
||||
Créateur obtient **statut "Vérifié"** :
|
||||
- Badge ✓ visible sur profil
|
||||
- Contenus futurs publiés **immédiatement** (modération a posteriori uniquement)
|
||||
- Modération seulement si signalé par utilisateurs
|
||||
|
||||
**Outils modérateur** :
|
||||
- Écoute accélérée (1.5x ou 2x) = double productivité
|
||||
- Interface dédiée : queue de contenus à valider
|
||||
- Raccourcis clavier : A (Accepter), R (Rejeter), Espace (Pause)
|
||||
- Historique créateur visible (si déjà 1-2 contenus validés)
|
||||
|
||||
**Modération communautaire (post-MVP)** :
|
||||
|
||||
⚠️ **Non implémenté au MVP** (complexité juridique)
|
||||
|
||||
Vision future (envisageable) :
|
||||
- Créateurs établis peuvent opt-in "Modérateur communautaire"
|
||||
- Formation obligatoire (30 min) + quiz (80%)
|
||||
- Pré-validation uniquement (validation finale toujours par équipe RoadWave)
|
||||
- Compensation : badges, premium offert
|
||||
- Attribution aléatoire (pas de collusion)
|
||||
|
||||
**Justification décision MVP** :
|
||||
- **Responsabilité juridique** : plateforme reste responsable (DSA EU)
|
||||
- **Qualité garantie** : modérateurs formés et mandatés
|
||||
- **Anti-spam efficace** : bloque 95% des abus dès le début
|
||||
- **Coût raisonnable** : 30s × 3 contenus = 1.5 min/créateur
|
||||
- **UX acceptable** : délai 24-48h expliqué clairement
|
||||
- **Pas de validation par pairs** au MVP = évite risques juridiques (collusion, compétence, conflits)
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Modification et suppression
|
||||
|
||||
**Décision** : Modification métadonnées uniquement, suppression immédiate
|
||||
|
||||
**Modification autorisée** :
|
||||
|
||||
| Élément | Modifiable | Justification |
|
||||
|---------|------------|---------------|
|
||||
| **Titre** | ✅ | Correction coquilles |
|
||||
| **Description** | ✅ | Si ajoutée ultérieurement |
|
||||
| **Tags** | ✅ | Ajustement pertinence |
|
||||
| **Image couverture** | ✅ | Personnalisation |
|
||||
| **Audio** | ❌ | Intégrité contenu |
|
||||
| **Zone diffusion** | ❌ | Évite manipulation algo |
|
||||
| **Type géo** | ❌ | Évite manipulation algo |
|
||||
| **Classification âge** | ❌ | Sécurité mineurs |
|
||||
|
||||
**Raisons restrictions** :
|
||||
|
||||
**Audio non modifiable** :
|
||||
- Évite fraude : uploader contenu validé → remplacer par spam
|
||||
- Intégrité : auditeurs doivent écouter ce qui a été validé
|
||||
|
||||
**Zone/Type non modifiables** :
|
||||
- Évite manipulation : créer "Local Paris" → changer en "National" pour boost visibilité
|
||||
- Évite abus : créer "Neutre" (faible pondération géo) → changer en "Ancré" (forte pondération)
|
||||
|
||||
**Classification non modifiable** :
|
||||
- Évite contournement : uploader "Tout public" → passer en "18+" sans revalidation
|
||||
- Sécurité : garantit que classification a été vérifiée
|
||||
|
||||
**Si besoin de changer audio/zone/classification** :
|
||||
- Action : **Supprimer contenu + republier**
|
||||
- Si créateur <3 contenus validés : retourne en file validation
|
||||
- Si créateur ≥3 contenus validés : publication immédiate
|
||||
|
||||
**Suppression de contenu** :
|
||||
|
||||
| Aspect | Comportement |
|
||||
|--------|--------------|
|
||||
| **Délai** | Immédiat | Suppression BDD + cache sous 5 min |
|
||||
| **Réversibilité** | Non | Suppression définitive |
|
||||
| **Historique auditeurs** | Marqué "Contenu supprimé par créateur" | Conserve écoute dans historique |
|
||||
| **Analytics plateforme** | Anonymisé et conservé | Métriques globales (RGPD compliant) |
|
||||
| **Fichiers cache** | Supprimés sous 24h | Purge NGINX Cache (OVH VPS) et OVH Object Storage |
|
||||
|
||||
**Exemple scénario suppression** :
|
||||
```
|
||||
Créateur supprime podcast écouté par 1000 personnes
|
||||
→ Cache/Storage : fichiers purgés sous 24h (NGINX Cache + OVH Object Storage)
|
||||
→ BDD : entrée marquée "deleted", auteur anonymisé
|
||||
→ Historique auditeurs : "Contenu supprimé" (conserve durée écoute pour stats)
|
||||
→ Analytics : métriques globales conservées (anonymes, RGPD OK)
|
||||
```
|
||||
|
||||
**Notifications suppression** :
|
||||
- Pas de notification aux auditeurs (pour éviter effet Streisand)
|
||||
- Historique reste consultable : "Vous avez écouté ce contenu le [date]"
|
||||
- Si auditeur tente de réécouter : "Ce contenu n'est plus disponible"
|
||||
|
||||
**Justification** :
|
||||
- **Simplicité** : règles claires et non-ambiguës
|
||||
- **Sécurité** : évite manipulations algorithme et contournements modération
|
||||
- **Contrôle créateur** : liberté totale de supprimer (RGPD)
|
||||
- **Traçabilité** : historique conservé pour analytics (anonymisé)
|
||||
- **Coût 0** : pas de revalidation métadonnées
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 4
|
||||
254
docs/regles-metier/12-radio-live.md
Normal file
254
docs/regles-metier/12-radio-live.md
Normal file
@@ -0,0 +1,254 @@
|
||||
## 7. Radio live
|
||||
|
||||
### 7.1 Démarrage d'un live
|
||||
|
||||
**Décision** : Buffer 15s + notification abonnés + limite 8h
|
||||
|
||||
**Processus de démarrage** :
|
||||
|
||||
1. Créateur appuie "Démarrer live" dans l'app
|
||||
2. **Vérification pré-live** :
|
||||
- Connexion ≥1 Mbps upload (warning si insuffisant)
|
||||
- Micro autorisé
|
||||
- Zone diffusion déjà définie (ville, département, région, national)
|
||||
3. **Buffer initial 15 secondes** avant diffusion publique
|
||||
- Créateur parle pendant 15s → accumulation buffer serveur
|
||||
- Message créateur : "Live démarre dans 15s... Testez votre micro"
|
||||
- Permet vérifier qualité audio avant diffusion
|
||||
4. Après 15s → **Live public**, auditeurs peuvent rejoindre
|
||||
|
||||
**Notification abonnés** :
|
||||
- ✅ **Push notification immédiate** à tous les abonnés dans la zone géographique
|
||||
- Message : "🔴 [Nom créateur] est en direct : [Titre live]"
|
||||
- Tap notification → ouverture app + lecture live immédiate
|
||||
- **Filtrage géographique** : si abonné hors zone, pas de notif (évite frustration)
|
||||
|
||||
**Limite de durée** :
|
||||
- **Maximum 8 heures** par session live
|
||||
- Warning créateur à 7h30 : "Votre live se terminera dans 30 min"
|
||||
- Si besoin continuer → arrêt + redémarrage nouveau live (évite abus ressources serveur)
|
||||
|
||||
**Métadonnées obligatoires** :
|
||||
|
||||
| Champ | Format | Validation |
|
||||
|-------|--------|------------|
|
||||
| **Titre** | 5-100 caractères | Ex: "Discussion politique en direct" |
|
||||
| **Tags** | 1-3 centres d'intérêt | Sélection liste prédéfinie |
|
||||
| **Classification âge** | Enum | Tout public / 13+ / 16+ / 18+ |
|
||||
| **Zone diffusion** | Geo | Ville / Département / Région / National |
|
||||
|
||||
**Contenus interdits en live** :
|
||||
|
||||
| Type | Description | Sanction |
|
||||
|------|-------------|----------|
|
||||
| **Concert/spectacle** | Diffusion concert en direct depuis la salle | Strike 2 immédiat + suspension 7 jours |
|
||||
| **Événement sportif payant** | Match, compétition avec droits TV | Strike 2 immédiat + suspension 7 jours |
|
||||
| **Œuvre protégée** | Film, série, musique en fond sans droits | Strike 1 + suspension 3 jours + suppression live |
|
||||
| **Contenu violent** | Agression, violence physique | Strike 3 immédiat + suspension 30 jours |
|
||||
| **Contenu illégal** | Apologie terrorisme, pédopornographie | Strike 4 (ban définitif) + signalement autorités |
|
||||
|
||||
**Exemple usecase interdit** :
|
||||
```
|
||||
❌ Utilisateur dans salle de concert diffuse live performance
|
||||
→ Violation droits d'auteur + droits de diffusion
|
||||
→ Détection : modération réactive (signalements) + IA audio fingerprint
|
||||
→ Sanction : Strike 2 (suspension 7 jours) + suppression live + suppression replay
|
||||
```
|
||||
|
||||
**Détection violations** :
|
||||
- **Signalement utilisateurs** : bouton "Signaler" accessible pendant live
|
||||
- **IA audio fingerprint** : détection musique protégée en arrière-plan (post-MVP, voir [Section 18](18-detection-contenu-protege.md))
|
||||
- **Modération réactive** : modérateurs peuvent écouter lives signalés en temps réel
|
||||
- **Coupure immédiate** : modérateur peut arrêter live si contenu illégal évident
|
||||
|
||||
**Justification** :
|
||||
- **Buffer 15s** : équilibre entre test qualité et friction minimale
|
||||
- **Notification abonnés** : engagement maximal, valeur ajoutée live
|
||||
- **8h max** : couvre 99% cas usage (podcasts longs, émissions radio) sans abus
|
||||
- **Interdictions strictes** : protection juridique plateforme (DSA EU, droits d'auteur)
|
||||
- **Coût** : WebRTC ingestion + HLS distribution (réutilise infra existante)
|
||||
|
||||
---
|
||||
|
||||
### 7.2 Arrêt du live
|
||||
|
||||
**Décision** : Compte à rebours 5s + tolérance déconnexion 60s + enregistrement auto
|
||||
|
||||
**Fin manuelle créateur** :
|
||||
|
||||
1. Créateur appuie "Arrêter live"
|
||||
2. **Compte à rebours 5 secondes** affiché
|
||||
- Message audio : "Ce live se termine dans 5... 4... 3... 2... 1"
|
||||
- Permet au créateur de faire un outro propre
|
||||
- Annulable pendant décompte (bouton "Annuler")
|
||||
3. Timer atteint 0 → arrêt diffusion
|
||||
4. **Traitement post-live automatique** démarre (voir ci-dessous)
|
||||
|
||||
**Fin automatique si déconnexion** :
|
||||
|
||||
| Durée coupure | Comportement |
|
||||
|---------------|--------------|
|
||||
| **<60 secondes** | Message auditeurs : "Connexion créateur perdue, reconnexion en cours..." |
|
||||
| **≥60 secondes** | Arrêt automatique live + message : "Le live est terminé suite à une coupure de connexion" |
|
||||
|
||||
**Enregistrement automatique** :
|
||||
|
||||
✅ **Obligatoire et automatique** (valeur ajoutée énorme)
|
||||
|
||||
**Processus** :
|
||||
1. Pendant live : enregistrement continu serveur (format Opus raw)
|
||||
2. Fin live → **job asynchrone** (worker Go + FFmpeg) :
|
||||
- Conversion MP3 256 kbps (qualité optimale)
|
||||
- Génération segments HLS (comme contenu classique)
|
||||
- Normalisation volume -14 LUFS
|
||||
- Détection silences prolongés (nettoyage)
|
||||
3. **Publication automatique** du replay :
|
||||
- Titre : "[REPLAY] [Titre live original]"
|
||||
- Même zone diffusion, tags, classification
|
||||
- Disponible sous **5-10 minutes** après fin live
|
||||
- Type géo : automatiquement "Géo-neutre" (replay = contenu pérenne)
|
||||
|
||||
**Options créateur** :
|
||||
|
||||
| Option | Défaut | Description |
|
||||
|--------|--------|-------------|
|
||||
| **Publier replay automatiquement** | ✅ OUI | Désactivable avant démarrage live |
|
||||
| **Supprimer replay après coup** | ✅ Possible | Suppression standard contenu |
|
||||
| **Modifier replay** | ❌ Non | Intégrité enregistrement |
|
||||
|
||||
**Conservation fichier source** :
|
||||
- Opus raw conservé **7 jours** après fin live (backup)
|
||||
- Suppression automatique après 7j (économie stockage)
|
||||
- Si replay supprimé par créateur → fichier raw supprimé immédiatement
|
||||
|
||||
**Justification** :
|
||||
- **Compte à rebours 5s** : outro propre, pas de coupure brutale
|
||||
- **Tolérance 60s** : évite arrêts intempestifs (tunnel, changement cellule)
|
||||
- **Enregistrement auto** : valorisation contenu éphémère, génération contenu pérenne
|
||||
- **MP3 256 kbps** : qualité optimale pour replay (vs 48 kbps live)
|
||||
- **Coût** : stockage minimal (Opus → MP3 1× par live, puis suppression raw après 7j)
|
||||
|
||||
---
|
||||
|
||||
### 7.3 Comportement auditeur
|
||||
|
||||
**Décision** : Buffer 15s + continuation hors zone + reconnexion au live actuel + écoute passive uniquement
|
||||
|
||||
**Buffer de synchronisation** :
|
||||
|
||||
- **15 secondes** entre créateur et auditeurs
|
||||
- Raisons :
|
||||
- Stabilité réseau mobile (3G/4G fluctuant)
|
||||
- Synchronisation approximative acceptable (pas besoin temps réel strict)
|
||||
- Permet buffering anticiper coupures courtes (tunnels)
|
||||
|
||||
**Comparaison buffers** :
|
||||
|
||||
| Buffer | Avantages | Inconvénients | Décision |
|
||||
|--------|-----------|---------------|----------|
|
||||
| 5s | Quasi temps réel | Instable 3G, coupures fréquentes | ❌ |
|
||||
| 10s | Bon compromis | Légèrement juste pour 3G | ❌ |
|
||||
| **15s** | **Stabilité optimale 3G/4G** | Léger décalage acceptable | ✅ |
|
||||
| 20s+ | Très stable | Décalage trop perceptible | ❌ |
|
||||
|
||||
**Zone géographique pendant live** :
|
||||
|
||||
- ✅ **Continuation si sortie de zone**
|
||||
- Scénario : auditeur écoute live régional → sort du département → **live continue**
|
||||
- Raisons :
|
||||
- Pas de coupure brutale (mauvaise UX)
|
||||
- Écoute engagée = terminer naturellement
|
||||
- Après fin live → algo normal (pas de contenus hors zone)
|
||||
|
||||
**Reconnexion après coupure réseau** :
|
||||
|
||||
| Durée coupure | Comportement |
|
||||
|---------------|--------------|
|
||||
| **<90 secondes** | Reprend au live actuel (pas au buffer ancien) + saut temporel transparent |
|
||||
| **≥90 secondes** | Message : "Live en cours perdu, passage au contenu suivant" + algo propose contenu normal |
|
||||
|
||||
**Interactions disponibles** :
|
||||
|
||||
**Décision ferme** : ❌ **Aucun chat en direct, ni maintenant ni dans le futur**
|
||||
|
||||
**Raisons** :
|
||||
- **Sécurité routière** : pas de distraction en voiture (focus UX)
|
||||
- **Harcèlement** : évite contenu haineux, insultes, trolling
|
||||
- **Modération** : pas de coût modération temps réel (impossible à scale)
|
||||
- **Simplicité** : écoute passive = expérience uniforme
|
||||
|
||||
**Actions autorisées pendant live** :
|
||||
|
||||
| Action | Disponible | Effet |
|
||||
|--------|------------|-------|
|
||||
| **Like** | ✅ | Bouton cœur interface mobile (véhicule arrêté) |
|
||||
| **Abonnement créateur** | ✅ | Bouton profil créateur (interface mobile) |
|
||||
| **Skip** | ✅ | Passe au contenu suivant, sort du live |
|
||||
| **Précédent** | ❌ | Pas de sens sur live (flux temps réel) |
|
||||
| **Chat** | ❌ | Jamais implémenté (décision définitive) |
|
||||
| **Réactions emoji** | ❌ | Jamais implémenté (décision définitive) |
|
||||
|
||||
**Messages utilisateur** :
|
||||
- "💬 Les discussions ne sont pas disponibles sur RoadWave pour garantir votre sécurité en voiture et éviter le harcèlement."
|
||||
|
||||
**Justification décision définitive** :
|
||||
- **UX cohérente** : RoadWave = écoute en conduisant, pas réseau social interactif
|
||||
- **Bien-être** : évite toxicité, harcèlement, haine (fléau réseaux sociaux)
|
||||
- **Juridique** : pas de risque contentieux modération chat (DSA EU)
|
||||
- **Coût** : 0€ infra chat, 0€ modération temps réel
|
||||
- **Différenciation** : positionnement "audio safe" vs plateformes toxiques
|
||||
|
||||
---
|
||||
|
||||
### 7.4 Architecture technique
|
||||
|
||||
**Stack** :
|
||||
|
||||
```
|
||||
Créateur (App mobile)
|
||||
↓ WebRTC (OPUS 48 kbps)
|
||||
Serveur Ingestion (Go + Pion WebRTC)
|
||||
↓ Conversion temps réel
|
||||
Serveur HLS (segments .ts)
|
||||
↓ NGINX Cache (OVH VPS)
|
||||
Auditeurs (App mobile, HLS natif)
|
||||
```
|
||||
|
||||
**Flux détaillé** :
|
||||
1. **Créateur** → WebRTC OPUS 48 kbps vers serveur Go
|
||||
2. **Serveur Go** → Conversion temps réel OPUS → segments HLS (.m3u8 + .ts)
|
||||
3. **NGINX Cache (OVH VPS)** → Distribution HLS avec cache
|
||||
4. **Auditeurs** → Lecture HLS native iOS/Android (buffer 15s)
|
||||
5. **Enregistrement parallèle** → Opus raw stocké temporairement
|
||||
6. **Post-live** → Job async : Opus → MP3 256 kbps → Publication replay
|
||||
|
||||
**Dépendances** :
|
||||
- ✅ **Pion WebRTC** (Go library, open source, MIT license)
|
||||
- ✅ **FFmpeg** (conversion audio, LGPL/GPL)
|
||||
- ✅ **NGINX** (cache et distribution HLS, open source)
|
||||
- ✅ **OVH Object Storage** (stockage origin, compatible S3)
|
||||
- ✅ **PostgreSQL + Redis** (métadonnées live + cache)
|
||||
|
||||
**Avantages** :
|
||||
- ✅ Pas de dépendance Google/Facebook/Cloudflare (souveraineté)
|
||||
- ✅ WebRTC standard ouvert (Pion = lib Go pure)
|
||||
- ✅ Réutilise infra HLS existante (pas de doublon)
|
||||
- ✅ NGINX Cache (OVH VPS) optimise la distribution (coût réduit)
|
||||
- ✅ Scalable horizontalement (workers Go)
|
||||
|
||||
**Coût estimé infrastructure** :
|
||||
|
||||
| Phase | Utilisateurs | Infra live | Coût/mois |
|
||||
|-------|--------------|------------|-----------|
|
||||
| **MVP** | 0-100K | 1 instance Go (ingestion 100 lives simultanés) | +50€ (serveur) + bande passante |
|
||||
| **Growth** | 100K-1M | 3-5 instances Go (500 lives simultanés) | +200€ + bande passante |
|
||||
| **Scale** | 1M-10M | Kubernetes auto-scale (2000+ lives) | +1K€ + bande passante |
|
||||
|
||||
**Bande passante** :
|
||||
- Live : 48 kbps × nb_auditeurs (via NGINX Cache, segments)
|
||||
- Exemple : 100 auditeurs = 4.8 Mbps = ~2 Go/heure via cache
|
||||
- Coût estimé : ~0.02€/heure pour 100 auditeurs
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 7
|
||||
411
docs/regles-metier/13-detection-contenu-protege.md
Normal file
411
docs/regles-metier/13-detection-contenu-protege.md
Normal file
@@ -0,0 +1,411 @@
|
||||
## 18. Détection de contenu protégé par droits d'auteur
|
||||
|
||||
### 18.1 Périmètre et objectifs
|
||||
|
||||
**Décision** : Focus musique uniquement, approche minimaliste MVP
|
||||
|
||||
**Contenu protégé couvert** :
|
||||
|
||||
| Type | Couvert MVP | Justification |
|
||||
|------|-------------|---------------|
|
||||
| **Musique** | ✅ OUI | Risque principal (80% violations), détectable auditivement |
|
||||
| **Films/séries** | ❌ NON | Rare dans podcasts audio, complexité technique élevée |
|
||||
| **Livres audio** | ❌ NON | Difficile à distinguer de lecture légitime |
|
||||
| **Jingles/pubs** | ❌ NON | Usage souvent transformatif, faible risque juridique |
|
||||
|
||||
**Objectifs** :
|
||||
|
||||
1. **Juridique** : Conformité droits d'auteur UE (directive 2019/790)
|
||||
2. **Protection plateforme** : Éviter contentieux avec ayants droit (SACEM, labels)
|
||||
3. **Qualité** : Encourager contenu original créateurs
|
||||
4. **Pragmatisme** : Coût 0€ au MVP, scalable post-MVP
|
||||
|
||||
**Non-objectifs MVP** :
|
||||
|
||||
- ❌ Détection automatisée (fingerprinting audio)
|
||||
- ❌ Intégration bases de données commerciales (ACRCloud, etc.)
|
||||
- ❌ Détection temps réel sur lives (voir section 7)
|
||||
- ❌ Système de licences musicales
|
||||
|
||||
---
|
||||
|
||||
### 18.2 Règles d'utilisation de musique
|
||||
|
||||
**Décision** : Tolérance 30 secondes pour extraits (fair use)
|
||||
|
||||
**Cas autorisés** :
|
||||
|
||||
| Cas d'usage | Durée max | Condition | Exemple |
|
||||
|-------------|-----------|-----------|---------|
|
||||
| **Citation/critique** | 30 secondes | Commentaire ajouté, contexte éditorial | Review album, analyse musicale |
|
||||
| **Musique libre de droits** | Illimitée | Preuve licence si demandée | Epidemic Sound, Artlist, CC0 |
|
||||
| **Musique originale** | Illimitée | Créateur = compositeur/interprète | Podcast musical créateur |
|
||||
| **Domaine public** | Illimitée | Œuvre >70 ans après mort auteur | Classique pré-1950 |
|
||||
|
||||
**Cas interdits** :
|
||||
|
||||
| Violation | Description | Détection |
|
||||
|-----------|-------------|-----------|
|
||||
| **Musique intégrale** | Titre complet en fond ou standalone | Écoute modérateur (évident) |
|
||||
| **Compilation DJ** | Mix de titres protégés sans droits | Écoute modérateur |
|
||||
| **Extrait >30s** | Citation longue sans transformation | Chronométrage manuel si signalé |
|
||||
| **Karaoké** | Instrumental protégé + voix créateur | Écoute modérateur (reconnaissable) |
|
||||
|
||||
**Exception fair use (30 secondes)** :
|
||||
|
||||
**Conditions cumulatives** :
|
||||
1. Extrait ≤30 secondes **ET**
|
||||
2. Usage transformatif : commentaire, critique, analyse **ET**
|
||||
3. Pas de substitution à l'œuvre originale **ET**
|
||||
4. Mention titre + artiste dans métadonnées (recommandé)
|
||||
|
||||
**Justification juridique** :
|
||||
- Directive UE 2019/790 : exception citation à des fins de critique
|
||||
- Jurisprudence FR : citation courte autorisée si justifiée
|
||||
- 30s = standard industrie (YouTube, TikTok)
|
||||
|
||||
---
|
||||
|
||||
### 18.3 Processus de détection MVP
|
||||
|
||||
**Décision** : Validation manuelle lors des 3 premiers contenus uniquement
|
||||
|
||||
**Workflow intégré à la validation existante** (voir section 4.3) :
|
||||
|
||||
```
|
||||
Upload contenu (créateur <3 validés)
|
||||
↓
|
||||
File d'attente modération
|
||||
↓
|
||||
Modérateur écoute 30s (déjà existant)
|
||||
↓
|
||||
Vérification AJOUTÉE :
|
||||
├─ Musique en fond détectée ?
|
||||
│ ├─ OUI → Musique reconnaissable (titre connu) ?
|
||||
│ │ ├─ OUI → Durée >30s ?
|
||||
│ │ │ ├─ OUI → REFUS (violation droits)
|
||||
│ │ │ └─ NON → ACCEPTÉ (fair use)
|
||||
│ │ └─ NON → ACCEPTÉ (musique libre probable)
|
||||
│ └─ NON → ACCEPTÉ (pas de musique)
|
||||
└─
|
||||
Validation normale continue
|
||||
```
|
||||
|
||||
**Critères de détection manuelle** :
|
||||
|
||||
| Indicateur | Action modérateur |
|
||||
|------------|-------------------|
|
||||
| **Musique reconnaissable** (hit radio, classique célèbre) | Chronométrer l'extrait |
|
||||
| **Extrait >30s** | Refus automatique |
|
||||
| **Extrait ≤30s** | Vérifier usage transformatif (commentaire ?) |
|
||||
| **Musique d'ambiance inconnue** | Accepter (probable musique libre) |
|
||||
| **Doute** | Demander preuve licence au créateur |
|
||||
|
||||
**Après 3 contenus validés** :
|
||||
|
||||
- ✅ Créateur = statut "Vérifié"
|
||||
- ✅ Publication immédiate sans validation préalable
|
||||
- ✅ Modération **a posteriori uniquement** (si signalé)
|
||||
|
||||
**Justification** :
|
||||
- **Coût 0€** : réutilise écoute 30s déjà effectuée
|
||||
- **Scalable** : pas de validation pour créateurs établis
|
||||
- **Pragmatique** : détecte violations évidentes (90% des cas)
|
||||
- **Humain nécessaire** : fair use impossible à automatiser fiablement
|
||||
|
||||
---
|
||||
|
||||
### 18.4 Signalement et modération a posteriori
|
||||
|
||||
**Décision** : Réutilise système existant section 14
|
||||
|
||||
**Signalement utilisateur** :
|
||||
|
||||
- Catégorie existante : **🎵 Droits d'auteur**
|
||||
- Formulaire : identique autres signalements
|
||||
- Commentaire optionnel : "Quelle musique ? À quel timestamp ?"
|
||||
|
||||
**Traitement modérateur** :
|
||||
|
||||
1. Signalement reçu → file d'attente priorité MOYENNE (délai 24-48h)
|
||||
2. Modérateur écoute le timestamp indiqué
|
||||
3. Vérification :
|
||||
- Musique identifiable ? → Recherche Shazam/SoundHound (outil externe)
|
||||
- Durée extrait ?
|
||||
- Usage transformatif (commentaire/critique) ?
|
||||
4. Décision :
|
||||
- **Violation confirmée** → Application sanctions (voir 18.5)
|
||||
- **Fair use** → Rejet signalement
|
||||
- **Doute** → Escalade modérateur senior
|
||||
|
||||
**Outils modérateur** :
|
||||
|
||||
| Outil | Usage | Coût |
|
||||
|-------|-------|------|
|
||||
| **Écoute manuelle** | Détection présence musique | 0€ |
|
||||
| **Shazam/SoundHound** | Identification titre (externe, outil perso modérateur) | 0€ |
|
||||
| **Chronomètre** | Mesure durée extrait | 0€ |
|
||||
| **Notes** | Documentation décision (audit DSA) | 0€ |
|
||||
|
||||
**Priorité traitement** :
|
||||
|
||||
- Score IA : NON APPLICABLE (pas d'IA au MVP)
|
||||
- Priorité : **MOYENNE** (délai 24-48h jours ouvrés)
|
||||
- Escalade senior si :
|
||||
- Créateur conteste avec preuve licence
|
||||
- Doute fair use complexe
|
||||
- Récidive (>2 violations)
|
||||
|
||||
---
|
||||
|
||||
### 18.5 Sanctions
|
||||
|
||||
**Décision** : Échelle progressive avec tolérance première violation
|
||||
|
||||
**Grille de sanctions** :
|
||||
|
||||
| Occurrence | Sanction | Durée | Justification |
|
||||
|------------|----------|-------|---------------|
|
||||
| **1ère violation** | ⚠️ Avertissement + Suppression contenu | - | Tolérance erreur bonne foi |
|
||||
| **2e violation** | 🟡 Strike 1 + Suppression + Suspension upload | 3 jours | Négligence confirmée |
|
||||
| **3e violation** | 🟠 Strike 2 + Suppression + Suspension upload | 7 jours | Récidive caractérisée |
|
||||
| **4e violation** | 🟠 Strike 3 + Suppression + Suspension upload | 30 jours | Abus répété |
|
||||
| **5e violation** | 🔴 Strike 4 + Ban définitif compte créateur | Permanent | Abus délibéré |
|
||||
|
||||
**Détail sanctions** :
|
||||
|
||||
**Avertissement (1ère fois)** :
|
||||
- Suppression contenu immédiate
|
||||
- Email + push + in-app : "⚠️ Contenu retiré pour violation droits d'auteur"
|
||||
- Explication pédagogique : règles musique, lien vers CGU
|
||||
- **Pas de strike** (tolérance)
|
||||
- Créateur peut republier version corrigée
|
||||
|
||||
**Strike 1 (2e fois)** :
|
||||
- Suppression contenu
|
||||
- Strike ajouté au compteur (visible profil créateur)
|
||||
- Suspension upload **3 jours**
|
||||
- Email détaillé : titre détecté, timestamp, règle violée
|
||||
|
||||
**Strike 2 (3e fois)** :
|
||||
- Idem Strike 1
|
||||
- Suspension upload **7 jours**
|
||||
- Warning : "Strike 2/4 - Vous approchez du seuil critique"
|
||||
|
||||
**Strike 3 (4e fois)** :
|
||||
- Idem Strike 2
|
||||
- Suspension upload **30 jours**
|
||||
- Warning : "Strike 3/4 - Prochaine violation = ban définitif"
|
||||
|
||||
**Strike 4 - Ban définitif (5e fois)** :
|
||||
- Désactivation compte créateur
|
||||
- Tous contenus dépubliés
|
||||
- Pas de création nouveau compte (email/téléphone blacklisté)
|
||||
|
||||
**Exceptions - Pas de sanction si** :
|
||||
|
||||
- ✅ Créateur prouve licence acquise (facture, abonnement Epidemic Sound, etc.)
|
||||
- ✅ Musique = domaine public vérifié
|
||||
- ✅ Musique = œuvre originale créateur (preuve registre SACEM si demandée)
|
||||
- ✅ Extrait ≤30s + usage transformatif évident (critique musicale)
|
||||
|
||||
**Réhabilitation** :
|
||||
|
||||
- **-1 strike automatique** tous les **6 mois** sans nouvelle violation
|
||||
- Conditions : aucun signalement validé pendant la période
|
||||
- Minimum : 0 strikes (pas de valeur négative)
|
||||
- Avertissement (1ère fois) ne compte pas pour la réhabilitation
|
||||
|
||||
**Exemple** :
|
||||
```
|
||||
Créateur a Strike 2 (7 jours de suspension)
|
||||
→ 6 mois sans incident
|
||||
→ Strike 2 devient Strike 1
|
||||
→ 6 mois additionnels sans incident
|
||||
→ Strike 1 effacé, compte propre
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- **Tolérance 1ère fois** : évite punir erreurs honnêtes
|
||||
- **Escalade progressive** : dissuasion sans brutalité
|
||||
- **4 strikes avant ban** : cohérent avec système global (sections 9, 14)
|
||||
- **Réhabilitation 6 mois** : encourage bon comportement long terme
|
||||
- **Conforme DSA** : sanctions proportionnées + droit d'appel
|
||||
|
||||
---
|
||||
|
||||
### 18.6 Processus d'appel
|
||||
|
||||
**Décision** : Réutilise système existant section 14.3.3
|
||||
|
||||
**Accès** :
|
||||
- Bouton "Contester cette décision" dans notification sanction
|
||||
- Délai : **7 jours** après notification
|
||||
|
||||
**Formulaire d'appel** :
|
||||
|
||||
Champs standards (voir section 14) **+** champs spécifiques :
|
||||
|
||||
| Champ additionnel | Type | Obligatoire |
|
||||
|-------------------|------|-------------|
|
||||
| **Preuve licence** | Upload PDF/image (facture, contrat) | ❌ Optionnel |
|
||||
| **Lien source musique libre** | URL (YouTube Audio Library, etc.) | ❌ Optionnel |
|
||||
| **Déclaration œuvre originale** | Checkbox | ❌ Optionnel |
|
||||
|
||||
**Traitement appel** :
|
||||
|
||||
1. Modérateur senior examine :
|
||||
- Preuves fournies (licence, facture)
|
||||
- Réécoute contenu (usage transformatif ?)
|
||||
- Recherche musique (domaine public ?)
|
||||
2. Délai : **72h** (standard section 14)
|
||||
3. Décision finale :
|
||||
- **Appel accepté** → Strike retiré + Contenu rétabli + Excuse formelle
|
||||
- **Appel rejeté** → Sanction maintenue + Explication détaillée
|
||||
|
||||
**Cas particulier : Musique libre mal détectée**
|
||||
|
||||
Si créateur prouve musique = licence Epidemic Sound / Artlist :
|
||||
- ✅ Appel automatiquement accepté
|
||||
- ✅ Ajout titre à **whitelist interne** (évite futures erreurs)
|
||||
- ✅ Excuse + compensation (ex: 1 mois Premium offert)
|
||||
|
||||
**Justification** :
|
||||
- Réutilise processus éprouvé (section 14)
|
||||
- Preuve licence = résout 90% des cas
|
||||
- Délai 72h acceptable (pas de suspension immédiate sur appel)
|
||||
|
||||
---
|
||||
|
||||
### 18.7 Éducation créateurs
|
||||
|
||||
**Décision** : Prévention via documentation + tooltips
|
||||
|
||||
**Ressources disponibles** :
|
||||
|
||||
| Ressource | Contenu | Accès |
|
||||
|-----------|---------|-------|
|
||||
| **Page CGU dédiée** | Règles musique détaillées + exemples | Lien dans CGU + FAQ |
|
||||
| **Tooltip upload** | "⚠️ Pas de musique protégée >30s" | Interface upload contenu |
|
||||
| **Liste musique libre** | Liens Epidemic, Artlist, YouTube Audio Library | Page aide créateurs |
|
||||
| **Exemples fair use** | Cas OK : review 20s + commentaire / Cas KO : hit complet en fond | FAQ illustrée |
|
||||
|
||||
**Messages préventifs** :
|
||||
|
||||
**Lors du premier upload** (popup) :
|
||||
```
|
||||
🎵 Attention aux droits d'auteur
|
||||
|
||||
Vous pouvez utiliser :
|
||||
✅ Votre propre musique originale
|
||||
✅ Musique libre de droits (Epidemic Sound, etc.)
|
||||
✅ Extraits courts ≤30s pour critique/analyse
|
||||
|
||||
Interdit :
|
||||
❌ Musique populaire en intégrale ou fond prolongé
|
||||
❌ Compilation de hits sans droits
|
||||
|
||||
[J'ai compris] [En savoir plus]
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- Prévention > sanction (économie modération)
|
||||
- Créateurs informés = moins de violations
|
||||
- Coût : 0€ (documentation statique)
|
||||
|
||||
---
|
||||
|
||||
### 18.8 Évolution post-MVP
|
||||
|
||||
**Décision** : Audio fingerprinting open-source si besoin scalabilité
|
||||
|
||||
**Déclencheurs réintégration** :
|
||||
|
||||
1. Volume signalements "Droits d'auteur" >50/mois
|
||||
2. Temps modération musique >20h/mois
|
||||
3. Contentieux avec ayants droit (SACEM, labels)
|
||||
|
||||
**Solution technique prévue** :
|
||||
|
||||
| Composant | Technologie | Fonction | Coût |
|
||||
|-----------|-------------|----------|------|
|
||||
| **Fingerprinting** | Chromaprint (open-source) | Génération empreinte audio | 0€ |
|
||||
| **Base de référence** | MusicBrainz + AcoustID | Comparaison empreintes | 0€ |
|
||||
| **Matching** | Python + PostgreSQL | Détection similarité >85% | 0€ |
|
||||
| **Infrastructure** | VPS 8GB RAM + 4 vCPU | Processing async | 50-100€/mois |
|
||||
|
||||
**Workflow automatisé** :
|
||||
|
||||
```
|
||||
Upload contenu
|
||||
↓
|
||||
Job async : extraction empreinte audio (Chromaprint)
|
||||
↓
|
||||
Comparaison avec base MusicBrainz
|
||||
↓
|
||||
Match >95% → FLAG automatique "Musique détectée : [Titre]"
|
||||
↓
|
||||
Modérateur humain vérifie :
|
||||
├─ Durée extrait ?
|
||||
├─ Usage transformatif ?
|
||||
└─ Décision finale (accept/reject)
|
||||
```
|
||||
|
||||
**Limites connues** :
|
||||
|
||||
- ❌ Précision ~70% (vs 95%+ API commerciales)
|
||||
- ❌ Base MusicBrainz incomplète (surtout hits récents)
|
||||
- ❌ Faux positifs possibles (musique libre similaire)
|
||||
- ✅ Mais : **gratuit** + **self-hosted** + **scalable**
|
||||
|
||||
**Alternative commerciale** (si budget disponible) :
|
||||
|
||||
- ACRCloud : 300-800€/mois, précision 95%+, base exhaustive
|
||||
- Audible Magic : 500-1000€/mois, musique + films/TV
|
||||
- **Décision différée** post-MVP selon besoins réels
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 18
|
||||
|
||||
| Point | Décision | Coût |
|
||||
|-------|----------|------|
|
||||
| **Périmètre MVP** | Musique uniquement (80% des violations) | 0€ |
|
||||
| **Tolérance** | 30 secondes pour extraits (fair use) | 0€ |
|
||||
| **Détection MVP** | Manuelle lors des 3 premiers contenus | 0€ |
|
||||
| **Outils modérateur** | Écoute + Shazam externe + Chronomètre | 0€ |
|
||||
| **Sanctions** | Progressive : Avertissement → Strike 1 (3j) → Strike 2 (7j) → Strike 3 (30j) → Strike 4 (ban) | 0€ |
|
||||
| **Appel** | Réutilise processus section 14 (délai 72h) | 0€ |
|
||||
| **Éducation** | CGU + Tooltips + FAQ musique libre | 0€ |
|
||||
| **Post-MVP** | Chromaprint + MusicBrainz (si >50 signalements/mois) | 50-100€/mois |
|
||||
|
||||
**Coût total MVP** : **0€** (validation manuelle intégrée)
|
||||
|
||||
**Conformité juridique** :
|
||||
- ✅ Directive UE 2019/790 (droit d'auteur + exception citation)
|
||||
- ✅ DSA (Digital Services Act) : modération réactive + droit d'appel
|
||||
- ✅ SACEM/SDRM : protection ayants droit + processus contentieux
|
||||
- ✅ Fair use : tolérance 30s conforme jurisprudence FR/UE
|
||||
|
||||
**Scalabilité** :
|
||||
- 0-1000 contenus/mois : validation manuelle suffisante (3h/mois modération)
|
||||
- 1000-10K contenus/mois : fingerprinting open-source requis
|
||||
- 10K+ contenus/mois : API commerciale à considérer (ACRCloud)
|
||||
|
||||
**Risques identifiés** :
|
||||
|
||||
| Risque | Probabilité | Impact | Mitigation |
|
||||
|--------|-------------|--------|------------|
|
||||
| **Contentieux ayant droit** | Faible | Élevé | Réactivité suppression (<24h) + CGU claires |
|
||||
| **Faux négatifs** (violation non détectée) | Moyenne | Moyen | Signalements utilisateurs + modération a posteriori |
|
||||
| **Faux positifs** (musique libre bloquée) | Faible | Faible | Processus d'appel 72h + whitelist |
|
||||
| **Volume signalements** | Faible | Moyen | Évolution fingerprinting si >50/mois |
|
||||
|
||||
---
|
||||
|
||||
**Lien avec autres sections** :
|
||||
- Section 4.3 : Validation des 3 premiers contenus (workflow intégré)
|
||||
- Section 7.2 : Interdictions lives + fingerprinting post-MVP
|
||||
- Section 14 : Système modération (signalements, sanctions, appels)
|
||||
|
||||
**Prochaine section à clarifier** : Section 11 (Mode offline) ou Section 12 (Gestion des erreurs)
|
||||
393
docs/regles-metier/14-moderation-flows.md
Normal file
393
docs/regles-metier/14-moderation-flows.md
Normal file
@@ -0,0 +1,393 @@
|
||||
## 14. Modération - Flows opérationnels
|
||||
|
||||
### 14.1 Signalement
|
||||
|
||||
**Décision** : Formulaire simple avec 7 catégories prédéfinies
|
||||
|
||||
#### 14.1.1 Catégories de signalement
|
||||
|
||||
Liste déroulante avec 7 options :
|
||||
|
||||
| Catégorie | Description |
|
||||
|-----------|-------------|
|
||||
| 🚫 **Haine & violence** | Incitation à la haine, discrimination, menaces |
|
||||
| 🔞 **Contenu sexuel** | Pornographie, contenu explicite |
|
||||
| ⚖️ **Illégalité** | Terrorisme, apologie de crimes |
|
||||
| 🎵 **Droits d'auteur** | Musique/contenu protégé non autorisé (voir [Section 18](18-detection-contenu-protege.md) pour règles détaillées) |
|
||||
| 📧 **Spam** | Publicité non sollicitée, répétition |
|
||||
| ❌ **Fausse information** | Désinformation sur santé, sécurité routière |
|
||||
| 🔧 **Autre** | Champ texte obligatoire si sélectionné |
|
||||
|
||||
**Justification** :
|
||||
- Équilibre entre simplicité (pas trop de choix) et précision (aide les modérateurs)
|
||||
- Coût : 0€ (liste déroulante standard)
|
||||
|
||||
---
|
||||
|
||||
#### 14.1.2 Commentaire du signaleur
|
||||
|
||||
**Décision** : Optionnel avec incitation
|
||||
|
||||
- Champ texte libre (0-500 caractères)
|
||||
- Placeholder : "Décrivez le problème (optionnel mais recommandé)"
|
||||
- Non bloquant : le signalement peut être envoyé sans commentaire
|
||||
|
||||
**Justification** :
|
||||
- Encourage la qualité des signalements sans créer de friction
|
||||
- Aide les modérateurs à comprendre le contexte
|
||||
- Pas de risque d'abandon du processus
|
||||
|
||||
---
|
||||
|
||||
#### 14.1.3 Confirmation après signalement
|
||||
|
||||
**Décision** : Toast in-app avec lien historique
|
||||
|
||||
**Affichage** :
|
||||
- Toast notification : "✓ Signalement envoyé. Nous l'examinerons sous 24-48h."
|
||||
- Durée affichage : 5 secondes
|
||||
- Bouton optionnel "Voir mes signalements" (accès historique)
|
||||
|
||||
**Historique personnel** :
|
||||
- Liste des signalements envoyés par l'utilisateur
|
||||
- Statut : En cours / Traité / Rejeté
|
||||
- Notification in-app si action prise (contenu retiré, signalement rejeté)
|
||||
|
||||
**Justification** :
|
||||
- Transparence maximale
|
||||
- Coût : 0€ (aucun email automatique)
|
||||
- Bonne UX
|
||||
|
||||
---
|
||||
|
||||
### 14.2 Traitement des signalements
|
||||
|
||||
#### 14.2.1 IA pré-filtre (transcription + analyse)
|
||||
|
||||
**Décision** : OpenAI Whisper open source + NLP
|
||||
|
||||
**Stack technique** :
|
||||
|
||||
| Composant | Technologie | Hébergement |
|
||||
|-----------|-------------|-------------|
|
||||
| **Transcription** | Whisper large-v3 | Self-hosted (CPU MVP, GPU scale) |
|
||||
| **Analyse sentiment** | distilbert-base-uncased | Self-hosted |
|
||||
| **Détection haine** | facebook/roberta-hate-speech | Self-hosted |
|
||||
| **Mots-clés** | Liste noire FR/EN + regex | PostgreSQL |
|
||||
|
||||
**Processus** :
|
||||
1. Signalement reçu → ajout file d'attente asynchrone
|
||||
2. Transcription audio (1-10 minutes selon durée)
|
||||
3. Analyse automatique :
|
||||
- Score de confiance : 0-100%
|
||||
- Catégorie détectée
|
||||
- Timestamps des passages problématiques
|
||||
4. Priorisation automatique selon score
|
||||
|
||||
**Délais** :
|
||||
- Audio <5 min : 1-3 minutes
|
||||
- Audio 5-30 min : 3-10 minutes
|
||||
- Audio >30 min : 10-20 minutes
|
||||
|
||||
**Coût** :
|
||||
- **MVP** : 0€ (CPU standard, processing asynchrone)
|
||||
- **Scale** : 50-200€/mois (GPU VPS si >1000 signalements/jour)
|
||||
|
||||
**Justification** :
|
||||
- 100% open source, pas de dépendance GAFAM
|
||||
- Coût maîtrisé (scaling progressif)
|
||||
- Gain productivité modérateurs ×3-5
|
||||
|
||||
---
|
||||
|
||||
#### 14.2.2 Délais de traitement (SLA)
|
||||
|
||||
**Décision** : SLA progressif selon priorité
|
||||
|
||||
| Priorité | Délai cible | Traitement |
|
||||
|----------|-------------|------------|
|
||||
| **CRITIQUE** | <2h (24/7) | Violence, suicide, mise en danger → Astreinte modérateur senior |
|
||||
| **HAUTE** | <24h (jours ouvrés) | Haine, harcèlement, désinformation → Modérateur junior/senior |
|
||||
| **MOYENNE** | <24h (jours ouvrés) | Spam, contenu inapproprié → Modérateur junior |
|
||||
| **BASSE** | <72h (jours ouvrés) | Qualité audio, tags incorrects → Modérateur junior |
|
||||
|
||||
**Traitement automatique** :
|
||||
- Score IA >95% + catégorie évidente (ex: spam répété) → Action automatique immédiate
|
||||
- Notification créateur + possibilité d'appel
|
||||
|
||||
**Justification** :
|
||||
- Réaliste et conforme DSA (Digital Services Act)
|
||||
- Scalable : priorisation automatique
|
||||
- Ressources humaines optimisées
|
||||
|
||||
---
|
||||
|
||||
#### 14.2.3 Priorisation automatique
|
||||
|
||||
**Décision** : File d'attente intelligente basée sur score IA
|
||||
|
||||
**Calcul de priorité** :
|
||||
|
||||
```
|
||||
Priorité = (Score_IA × 0.7) + (Signalements_cumulés × 0.2) + (Fiabilité_signaleur × 0.1)
|
||||
```
|
||||
|
||||
**Détails** :
|
||||
- **Score_IA** : 0-100% (confiance analyse automatique)
|
||||
- **Signalements_cumulés** : nombre de signalements du même contenu (boost priorité)
|
||||
- **Fiabilité_signaleur** : score utilisateur (historique signalements pertinents)
|
||||
|
||||
**Classification résultante** :
|
||||
- Priorité ≥90 → **CRITIQUE** (traitement immédiat)
|
||||
- Priorité 70-89 → **HAUTE** (file prioritaire)
|
||||
- Priorité 40-69 → **MOYENNE** (file normale)
|
||||
- Priorité <40 → **BASSE** (file différée)
|
||||
|
||||
**Justification** :
|
||||
- Optimise le temps des modérateurs
|
||||
- Traite les cas graves en priorité
|
||||
- Coût : 0€ (algorithme simple)
|
||||
|
||||
---
|
||||
|
||||
### 14.3 Sanctions
|
||||
|
||||
#### 14.3.1 Notification au créateur
|
||||
|
||||
**Décision** : Multi-canal (email + push + in-app)
|
||||
|
||||
**Canaux utilisés** :
|
||||
|
||||
| Canal | Timing | Contenu |
|
||||
|-------|--------|---------|
|
||||
| **Push notification** | Immédiat | Alerte courte : "Votre contenu a été modéré" |
|
||||
| **In-app** | Au prochain lancement | Popup détaillée avec bouton "Voir détails" |
|
||||
| **Email** | Dans l'heure | Notification complète avec lien vers formulaire d'appel |
|
||||
|
||||
**Contenu email** :
|
||||
```
|
||||
Objet : Modération de votre contenu "[Titre du contenu]"
|
||||
|
||||
Bonjour [Pseudo],
|
||||
|
||||
Votre contenu "[Titre]" publié le [Date] a été modéré.
|
||||
|
||||
Catégorie violée : [Catégorie]
|
||||
Raison : [Explication détaillée]
|
||||
Sanction : [Strike X / Suspension X jours / Suppression contenu]
|
||||
|
||||
Extrait audio concerné : [Timestamp]
|
||||
Transcription : "[Passage problématique surligné]"
|
||||
|
||||
Vous pouvez contester cette décision sous 7 jours :
|
||||
[Lien formulaire d'appel]
|
||||
|
||||
L'équipe RoadWave
|
||||
```
|
||||
|
||||
**Coût** :
|
||||
- Email : ~0.001€/notification (Brevo, Resend)
|
||||
- Push : 0€ (Firebase Cloud Messaging / APNs)
|
||||
- In-app : 0€
|
||||
|
||||
**Justification** :
|
||||
- Conformité DSA (transparence obligatoire)
|
||||
- Multi-canal garantit réception
|
||||
- Coût négligeable
|
||||
|
||||
---
|
||||
|
||||
#### 14.3.2 Détail de la sanction
|
||||
|
||||
**Décision** : Notification complète avec preuves
|
||||
|
||||
**Éléments inclus obligatoirement** :
|
||||
|
||||
1. **Catégorie violée** : référence précise CGU (ex: "Article 3.2 - Haine & violence")
|
||||
2. **Raison détaillée** : explication en langage clair (non juridique)
|
||||
3. **Extrait audio** : timestamp exact du passage problématique (ex: "3:42-4:15")
|
||||
4. **Transcription** : texte problématique surligné en rouge
|
||||
5. **Gravité** : Strike actuel + conséquences (ex: "Strike 2/4 - Suspension 7 jours")
|
||||
6. **Recours** : lien direct vers formulaire d'appel + délai (7 jours)
|
||||
|
||||
**Exemple visuel in-app** :
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ ⚠️ Contenu modéré │
|
||||
├─────────────────────────────────────┤
|
||||
│ Titre : "Mon podcast #42" │
|
||||
│ Publié le : 15/01/2026 │
|
||||
│ │
|
||||
│ Catégorie violée : │
|
||||
│ 🚫 Haine & violence (Article 3.2) │
|
||||
│ │
|
||||
│ Passage problématique : 3:42-4:15 │
|
||||
│ "[Transcription surlignée]" │
|
||||
│ │
|
||||
│ Sanction : Strike 2/4 │
|
||||
│ Suspension : 7 jours │
|
||||
│ │
|
||||
│ [Contester cette décision] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- Transparence maximale (obligation DSA)
|
||||
- Créateur comprend l'erreur → amélioration future
|
||||
- Réduit les appels non fondés
|
||||
|
||||
---
|
||||
|
||||
#### 14.3.3 Processus d'appel
|
||||
|
||||
**Décision** : Formulaire in-app structuré
|
||||
|
||||
**Accès** :
|
||||
- Bouton "Contester cette décision" dans notification
|
||||
- Section "Mes sanctions" dans profil créateur
|
||||
|
||||
**Formulaire d'appel** :
|
||||
|
||||
| Champ | Type | Obligatoire |
|
||||
|-------|------|-------------|
|
||||
| **Sanction contestée** | Pré-rempli (non modifiable) | ✅ |
|
||||
| **Raison de l'appel** | Texte libre (50-1000 caractères) | ✅ |
|
||||
| **Arguments** | Zone texte enrichie | ✅ |
|
||||
| **Preuves** | Upload fichiers (max 5, 10 MB total) | ❌ |
|
||||
|
||||
**Après soumission** :
|
||||
- Génération numéro de ticket unique (ex: `#MOD-2026-00142`)
|
||||
- Email confirmation : "Votre appel sera traité sous 72h"
|
||||
- Statut visible dans l'app : "En cours d'examen"
|
||||
|
||||
**Délai de soumission** :
|
||||
- Maximum **7 jours** après notification de sanction
|
||||
- Après 7 jours : appel automatiquement refusé
|
||||
|
||||
**Justification** :
|
||||
- Professionnel et traçable
|
||||
- Intégration complète avec système modération
|
||||
- Coût : 0€ (formulaire custom backend)
|
||||
|
||||
---
|
||||
|
||||
#### 14.3.4 Délai de réponse pour appel
|
||||
|
||||
**Décision** : SLA 72h garanti
|
||||
|
||||
**Délais** :
|
||||
|
||||
| Type d'appel | Délai | Responsable |
|
||||
|--------------|-------|-------------|
|
||||
| **Standard** | 72h max (3 jours ouvrés) | Modérateur senior |
|
||||
| **Complexe** | 5 jours ouvrés + notification intermédiaire J+3 | Modérateur senior + Admin modération |
|
||||
| **Critique** | 24h (cas suspension longue/ban) | Admin modération |
|
||||
|
||||
**Notification intermédiaire** (si délai >72h) :
|
||||
- Email J+3 : "Votre appel #MOD-XXX est en cours d'examen approfondi. Réponse sous 2 jours."
|
||||
|
||||
**Réponse finale** :
|
||||
|
||||
Email détaillé avec :
|
||||
1. **Décision** : Maintien / Annulation / Réduction de sanction
|
||||
2. **Justification** : explication de la décision d'appel
|
||||
3. **Actions** : Strike retiré / Suspension annulée / Contenu rétabli (si applicable)
|
||||
4. **Définitif** : mention "Cette décision est définitive" (pas de second appel)
|
||||
|
||||
**Suivi in-app** :
|
||||
- Mise à jour statut : "Appel accepté ✓" ou "Appel rejeté ✗"
|
||||
- Badge notification
|
||||
|
||||
**Justification** :
|
||||
- Équilibre entre rapidité et qualité de traitement
|
||||
- Conforme pratiques industrie (YouTube, TikTok : 5-7 jours)
|
||||
- Ressources humaines réalistes
|
||||
|
||||
---
|
||||
|
||||
### 14.4 Outils modérateurs
|
||||
|
||||
**Stack technique complète** :
|
||||
|
||||
| Outil | Technologie | Fonction |
|
||||
|-------|-------------|----------|
|
||||
| **Dashboard** | React + TanStack Table | Interface modération |
|
||||
| **File signalements** | PostgreSQL + Redis | Priorisation temps réel |
|
||||
| **Player audio** | Wavesurfer.js | Lecture avec waveform + annotations |
|
||||
| **Transcription** | Whisper large-v3 | Conversion audio → texte |
|
||||
| **Historique créateur** | Vue 360° | Contenus, strikes, appels, métriques |
|
||||
| **Actions rapides** | Shortcuts clavier | Approuver (A), Rejeter (R), Escalade (E) |
|
||||
| **Logs audit** | PostgreSQL + export | Traçabilité complète (DSA) |
|
||||
| **Collaboration** | Système de commentaires | Modérateurs peuvent s'entraider sur cas complexes |
|
||||
|
||||
**Fonctionnalités clés** :
|
||||
|
||||
1. **Lecture accélérée** : 0.75x à 2x (gain productivité)
|
||||
2. **Marqueurs temporels** : annotation directe sur waveform
|
||||
3. **Historique créateur** : vue rapide contenus précédents + strikes
|
||||
4. **Statistiques** : signalements traités/jour, temps moyen, précision
|
||||
5. **Fil d'activité** : actions récentes équipe (temps réel)
|
||||
|
||||
**Coût infrastructure** :
|
||||
- MVP : 0-50€/mois (serveur CPU)
|
||||
- Scale : 50-200€/mois (GPU + Redis Cluster)
|
||||
|
||||
---
|
||||
|
||||
### 14.5 Modération préventive (rappel)
|
||||
|
||||
**Nouveaux créateurs** :
|
||||
- Validation manuelle des **3 premiers contenus**
|
||||
- Délai : 24-48h (jours ouvrés)
|
||||
- Transcription automatique pour aide modérateur
|
||||
|
||||
**Score de confiance** :
|
||||
- Évolution dynamique selon historique
|
||||
- Créateur fiable (0 strike depuis 6 mois) → validation automatique
|
||||
- Créateur suspect (strikes récents) → validation manuelle systématique
|
||||
|
||||
**Publicités** :
|
||||
- Validation manuelle obligatoire 24-48h (responsabilité juridique)
|
||||
- Transcription + analyse métadonnées (ciblage, durée, volume)
|
||||
|
||||
**Justification** :
|
||||
- Prévention > réaction (économie modération)
|
||||
- Qualité plateforme préservée dès le début
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 14
|
||||
|
||||
| Point | Décision | Coût |
|
||||
|-------|----------|------|
|
||||
| **Catégories signalement** | 7 catégories prédéfinies + champ libre | 0€ |
|
||||
| **Commentaire signaleur** | Optionnel avec incitation | 0€ |
|
||||
| **Confirmation** | Toast in-app + historique personnel | 0€ |
|
||||
| **IA pré-filtre** | Whisper (CPU MVP, GPU scale) + NLP open source | 0-200€/mois |
|
||||
| **Délais traitement** | SLA progressif : 2h/24h/72h selon priorité | Dépend équipe |
|
||||
| **Priorisation** | File intelligente basée score IA | 0€ |
|
||||
| **Notification sanction** | Email + push + in-app (multi-canal) | ~0.001€/notif |
|
||||
| **Détail sanction** | Complet : raison + extrait + transcription | 0€ |
|
||||
| **Processus appel** | Formulaire in-app structuré | 0€ |
|
||||
| **Délai appel** | 72h garanti (standard) | Dépend équipe |
|
||||
| **Outils modérateurs** | Dashboard React + Whisper + Wavesurfer.js | 0-200€/mois |
|
||||
|
||||
**Coût total MVP** : **0-200€/mois** (infrastructure IA optionnelle)
|
||||
|
||||
**Conformité** :
|
||||
- ✅ DSA (Digital Services Act) : transparence, traçabilité, délais
|
||||
- ✅ RGPD : données modération anonymisées après 3 ans
|
||||
- ✅ Logs audit : toutes actions tracées (obligation légale plateforme)
|
||||
|
||||
**Scalabilité** :
|
||||
- 0-1000 signalements/mois : équipe 1-2 modérateurs junior + 1 senior
|
||||
- 1000-10K signalements/mois : équipe 5-10 modérateurs + IA GPU
|
||||
- 10K+ signalements/mois : équipe dédiée + IA optimisée + modération communautaire
|
||||
|
||||
---
|
||||
|
||||
**🎯 Modération communautaire** : Voir [Section 19](19-moderation-communautaire.md) pour le système complet de badges, récompenses et priorisation des signalements pertinents.
|
||||
|
||||
---
|
||||
|
||||
**Section suivante** : [Section 19 - Modération Communautaire](19-moderation-communautaire.md)
|
||||
430
docs/regles-metier/15-moderation-communautaire.md
Normal file
430
docs/regles-metier/15-moderation-communautaire.md
Normal file
@@ -0,0 +1,430 @@
|
||||
## 19. Modération Communautaire - Badges et Récompenses
|
||||
|
||||
**Contexte** : Système de gamification pour encourager les utilisateurs à signaler du contenu inapproprié de manière pertinente et qualitative.
|
||||
|
||||
**Objectifs** :
|
||||
- Améliorer la qualité des signalements (réduire les signalements abusifs)
|
||||
- Réduire la charge de travail des modérateurs (priorisation automatique)
|
||||
- Récompenser les contributeurs actifs et fiables
|
||||
|
||||
---
|
||||
|
||||
### 19.1 Système de badges
|
||||
|
||||
**Décision** : 3 niveaux de badges selon l'historique de signalements validés
|
||||
|
||||
#### 19.1.1 Badges et critères
|
||||
|
||||
| Badge | Nom | Critères | Avantages |
|
||||
|-------|-----|----------|-----------|
|
||||
| 🥉 | **Contributeur Bronze** | 5 signalements validés + 70% taux pertinence | Signalements prioritaires (+10 points algorithme) |
|
||||
| 🥈 | **Contributeur Argent** | 20 signalements validés + 80% taux pertinence | Signalements prioritaires (+20 points) + Badge visible profil |
|
||||
| 🥇 | **Contributeur Or** | 50 signalements validés + 90% taux pertinence | Signalements prioritaires (+30 points) + Badge visible + Réduction Premium |
|
||||
|
||||
**Règles d'éligibilité** :
|
||||
- Minimum **10 signalements envoyés** pour être éligible aux badges
|
||||
- Les signalements "En cours" ne comptent pas dans le calcul
|
||||
- Les signalements rejetés font baisser le taux de pertinence
|
||||
|
||||
**Calcul du taux de pertinence** :
|
||||
```
|
||||
Taux de pertinence = (Signalements validés / Total signalements envoyés) × 100
|
||||
```
|
||||
|
||||
**Période de calcul** :
|
||||
- Seuls les **6 derniers mois** comptent (période glissante)
|
||||
- Évite que les utilisateurs se reposent sur leurs lauriers
|
||||
|
||||
**Justification** :
|
||||
- **Simple** : 3 niveaux seulement (pas d'over-engineering)
|
||||
- **Gratuit** : logique backend + affichage frontend
|
||||
- **Efficace** : incite la qualité plutôt que la quantité
|
||||
|
||||
---
|
||||
|
||||
#### 19.1.2 Délai entre obtention badges
|
||||
|
||||
**Décision** : Délai minimum entre niveaux
|
||||
|
||||
| Transition | Délai minimum |
|
||||
|------------|---------------|
|
||||
| **Bronze → Argent** | 30 jours |
|
||||
| **Argent → Or** | 60 jours |
|
||||
|
||||
**Justification** :
|
||||
- Évite la montée en badge trop rapide (anti-farming)
|
||||
- Force une contribution régulière sur la durée
|
||||
- Détecte les patterns suspects (audit modérateur si trop rapide)
|
||||
|
||||
---
|
||||
|
||||
#### 19.1.3 Découverte du système
|
||||
|
||||
**IMPORTANT** : L'utilisateur doit être informé du système de récompenses **dès son premier signalement**.
|
||||
|
||||
**Moment d'affichage** :
|
||||
- Après avoir envoyé le **premier signalement**
|
||||
- Juste après le toast de confirmation standard
|
||||
- **2 secondes de délai** avant affichage de la modal
|
||||
|
||||
**Modal d'information** (affichage unique, ne se réaffiche jamais) :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 🎯 Bravo ! Vous contribuez à une │
|
||||
│ communauté plus saine │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ En signalant ce contenu, vous participez │
|
||||
│ activement à améliorer l'expérience de │
|
||||
│ tous les utilisateurs RoadWave. │
|
||||
│ │
|
||||
│ Vos contributions de qualité sont │
|
||||
│ valorisées et récompensées : │
|
||||
│ │
|
||||
│ 🥉 Bronze : 5 signalements validés │
|
||||
│ → Signalements prioritaires │
|
||||
│ │
|
||||
│ 🥈 Argent : 20 signalements validés │
|
||||
│ → Badge visible + priorité accrue │
|
||||
│ │
|
||||
│ 🥇 Or : 50 signalements validés │
|
||||
│ → Réduction Premium -50% pendant 3 mois │
|
||||
│ │
|
||||
│ 💡 Votre taux de pertinence compte ! │
|
||||
│ Signalements validés ÷ Total × 100 │
|
||||
│ │
|
||||
│ Continuez à nous aider, chaque │
|
||||
│ signalement pertinent compte ! 🙏 │
|
||||
│ │
|
||||
│ [En savoir plus] [J'ai compris] │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Lien "En savoir plus"** :
|
||||
- Redirection vers page dédiée expliquant :
|
||||
- Calcul détaillé du taux de pertinence
|
||||
- Critères d'obtention pour chaque badge
|
||||
- Avantages détaillés de chaque niveau
|
||||
- Règles anti-abus (limite 10 signalements/24h)
|
||||
- Durée de validité des badges (audit trimestriel)
|
||||
|
||||
**Coût** : **0€** (modal one-time, logique backend simple)
|
||||
|
||||
---
|
||||
|
||||
#### 19.1.4 Affichage badges et statistiques
|
||||
|
||||
**Badge visible** :
|
||||
- **Profil utilisateur** : visible par tous les autres utilisateurs
|
||||
- **Historique signalements** : visible uniquement par l'utilisateur lui-même
|
||||
- **Toast après obtention** :
|
||||
- 🥉 Bronze : "🎉 Félicitations ! Vous êtes désormais Contributeur Bronze. Merci de rendre RoadWave meilleur !"
|
||||
- 🥈 Argent : "🎉 Impressionnant ! Badge Contributeur Argent obtenu. Votre engagement fait la différence !"
|
||||
- 🥇 Or : "🎉 Exceptionnel ! Vous êtes Contributeur Or. La communauté vous remercie pour votre aide précieuse !"
|
||||
|
||||
**Statistiques personnelles** (page Profil > Mes signalements) :
|
||||
```
|
||||
📊 Vos statistiques de modération
|
||||
Signalements envoyés : 27
|
||||
Validés : 23 ✅
|
||||
Rejetés : 4 ❌
|
||||
Taux de pertinence : 85%
|
||||
|
||||
Badge actuel : 🥈 Contributeur Argent
|
||||
Prochain palier : 🥇 Contributeur Or (30 signalements validés restants)
|
||||
```
|
||||
|
||||
**Toast après traitement signalement** :
|
||||
- Si validé : "✅ Bravo ! Votre signalement a aidé la communauté. Progression : 3/5 pour badge Bronze 🥉"
|
||||
- Si rejeté : "❌ Signalement non retenu. Taux de pertinence : 60%. Continuez vos efforts !"
|
||||
|
||||
**Justification** :
|
||||
- **Transparence totale** : l'utilisateur voit sa progression en temps réel
|
||||
- **Motivation** : gamification saine (pas de pression, juste encouragement)
|
||||
- **Gratifiant** : messages positifs valorisant la contribution
|
||||
|
||||
---
|
||||
|
||||
### 19.2 Score de fiabilité
|
||||
|
||||
**Décision** : Score interne utilisé pour prioriser les signalements dans l'algorithme
|
||||
|
||||
#### 19.2.1 Formule
|
||||
|
||||
```
|
||||
Score fiabilité = min(100, (Validés × 10 - Rejetés × 5 + Bonus_Or × 20))
|
||||
```
|
||||
|
||||
**Détails** :
|
||||
- **Validés** : nombre de signalements validés par modérateurs
|
||||
- **Rejetés** : nombre de signalements rejetés
|
||||
- **Bonus_Or** : +20 points si badge Or actif
|
||||
- **Plafond** : maximum 100 points
|
||||
|
||||
**Exemples** :
|
||||
|
||||
| Cas | Validés | Rejetés | Badge | Score |
|
||||
|-----|---------|---------|-------|-------|
|
||||
| Nouvel utilisateur fiable | 10 | 1 | Aucun | 95 |
|
||||
| Utilisateur moyen | 15 | 8 | Bronze | 70 |
|
||||
| Contributeur Or | 50 | 3 | Or | 100 (plafonné) |
|
||||
| Signaleur abusif | 2 | 20 | Aucun | 0 (min 0) |
|
||||
|
||||
---
|
||||
|
||||
#### 19.2.2 Utilisation dans l'algorithme de priorisation
|
||||
|
||||
**Rappel formule Section 14.2.3** :
|
||||
```
|
||||
Priorité = (Score_IA × 0.7) + (Signalements_cumulés × 0.2) + (Fiabilité_signaleur × 0.1)
|
||||
```
|
||||
|
||||
**Intégration** :
|
||||
- **Fiabilité_signaleur** = Score fiabilité / 100 (normalisé 0-1)
|
||||
- Les signalements des utilisateurs avec badge Argent/Or passent automatiquement devant les autres (même score IA)
|
||||
|
||||
**Affichage à l'utilisateur** :
|
||||
- **NON affiché** publiquement (risque de gamification abusive)
|
||||
- **Visible** uniquement dans les stats personnelles :
|
||||
```
|
||||
Votre score de fiabilité : 85/100
|
||||
→ Vos signalements sont traités en priorité
|
||||
```
|
||||
|
||||
**Coût** : **0€** (calcul automatique lors du traitement)
|
||||
|
||||
---
|
||||
|
||||
### 19.3 Statut "Utilisateur de confiance"
|
||||
|
||||
**Décision** : Statut automatique pour utilisateurs Badge Argent ou Or
|
||||
|
||||
**Critère** :
|
||||
- Badge **Argent OU Or** actif = automatiquement "Utilisateur de confiance"
|
||||
|
||||
**Avantages** :
|
||||
1. **Priorisation signalements** : traités avant signalements standards (même score IA)
|
||||
2. **Badge visible** : affichage "Utilisateur de confiance" sur profil public
|
||||
3. **Notification différée** : résultats sous 12h (au lieu de 24-48h standards)
|
||||
|
||||
**Révocation** :
|
||||
- Perte badge Argent/Or → perte statut confiance automatique
|
||||
- Retour au statut normal, aucune sanction supplémentaire
|
||||
|
||||
**Coût** : **0€** (simple flag booléen calculé automatiquement)
|
||||
|
||||
---
|
||||
|
||||
### 19.4 Réduction Premium pour badge Or
|
||||
|
||||
**Décision** : Seuls les utilisateurs **Contributeur Or** (top contributeurs) obtiennent une réduction Premium
|
||||
|
||||
#### 19.4.1 Conditions
|
||||
|
||||
| Critère | Valeur |
|
||||
|---------|--------|
|
||||
| **Montant** | **-50% pendant 3 mois** (2.49€/mois au lieu de 4.99€) |
|
||||
| **Éligibilité** | Badge Or actif + pas déjà Premium |
|
||||
| **Durée** | 3 mois à partir de l'activation |
|
||||
| **Renouvellement** | Prix normal (4.99€/mois) après 3 mois |
|
||||
| **Cumul** | Non cumulable avec offre annuelle |
|
||||
| **Délai activation** | 30 jours après obtention badge Or |
|
||||
|
||||
---
|
||||
|
||||
#### 19.4.2 Notification
|
||||
|
||||
**Email + Push + In-app** dès obtention badge Or :
|
||||
|
||||
```
|
||||
🎉 Exceptionnel ! Vous avez obtenu le badge Contributeur Or
|
||||
|
||||
Vous faites partie des meilleurs contributeurs de RoadWave.
|
||||
Grâce à votre engagement et votre vigilance, vous aidez
|
||||
des milliers d'utilisateurs à profiter d'une expérience
|
||||
audio de qualité.
|
||||
|
||||
La communauté vous remercie ! 🙏
|
||||
|
||||
En reconnaissance de votre aide précieuse, vous bénéficiez de :
|
||||
|
||||
✨ 3 mois d'abonnement Premium à -50%
|
||||
→ 2.49€/mois au lieu de 4.99€
|
||||
|
||||
[Profiter de l'offre]
|
||||
|
||||
Cette offre est valable pendant 30 jours.
|
||||
Merci de contribuer à rendre RoadWave meilleur chaque jour !
|
||||
```
|
||||
|
||||
**Rappels** :
|
||||
- Email + Push J-7 : "Il vous reste 7 jours pour profiter de votre réduction Premium -50%"
|
||||
- Email + Push J-1 : "Dernière chance ! Votre réduction Premium -50% expire demain"
|
||||
|
||||
**Si non activée après 30 jours** :
|
||||
- Offre expirée (notification : "Votre offre Premium -50% a expiré")
|
||||
- Badge Or conservé (seule l'offre expire, pas le badge)
|
||||
|
||||
---
|
||||
|
||||
#### 19.4.3 Perte du badge Or
|
||||
|
||||
**Conditions** :
|
||||
- Taux de pertinence descend sous **90%** après audit trimestriel
|
||||
- Signalements abusifs détectés (voir Section 19.5)
|
||||
|
||||
**Conséquences** :
|
||||
- **Badge Or révoqué** immédiatement
|
||||
- **Abonnement Premium en cours** reste actif jusqu'à sa fin normale
|
||||
- **Nouvelle souscription** à prix normal (4.99€/mois)
|
||||
- **Pas de nouvelle offre -50%** même si badge Or réobtenu ultérieurement
|
||||
|
||||
**Justification** :
|
||||
- Maintien qualité badges sur le long terme
|
||||
- Évite abus système
|
||||
|
||||
---
|
||||
|
||||
#### 19.4.4 ROI et justification
|
||||
|
||||
**Coût maximum** : **200€/mois** (si 50 utilisateurs Or simultanés avec réduction active)
|
||||
|
||||
**ROI attendu** :
|
||||
- **1 utilisateur Or** = économie ~5-10h modération/mois = 75-150€ économisés (taux horaire modérateur ~15€/h)
|
||||
- **10 utilisateurs Or actifs** = 750-1500€ économisés > 200€ coût réductions
|
||||
- **ROI positif dès 2-3 utilisateurs Or actifs**
|
||||
|
||||
**Justification** :
|
||||
- **Incitation forte** pour meilleurs contributeurs uniquement
|
||||
- **Conversion** : utilisateurs gratuits très engagés → Premium payant après 3 mois
|
||||
- **Risque limité** : coût max plafonné, largement compensé par économie modération
|
||||
|
||||
---
|
||||
|
||||
### 19.5 Anti-abus
|
||||
|
||||
**CRITIQUE** : Système de protection contre les comportements abusifs
|
||||
|
||||
#### 19.5.1 Limite temporelle
|
||||
|
||||
**Décision** : Maximum 10 signalements / 24h par utilisateur
|
||||
|
||||
**Règles** :
|
||||
- Au-delà de 10 signalements/24h → signalements automatiquement rejetés
|
||||
- Alerte modérateur automatique (enquête manuelle)
|
||||
- Message utilisateur : "Limite quotidienne atteinte (10 signalements/24h). Réessayez demain."
|
||||
|
||||
**Justification** :
|
||||
- Évite signalement massif (farming)
|
||||
- 10/jour = largement suffisant pour usage légitime
|
||||
- Coût : 0€
|
||||
|
||||
---
|
||||
|
||||
#### 19.5.2 Détection patterns suspects
|
||||
|
||||
**Audit automatique hebdomadaire** :
|
||||
|
||||
```sql
|
||||
-- Détection signaleurs suspects
|
||||
SELECT user_id, COUNT(*) as total
|
||||
FROM reports
|
||||
WHERE created_at > NOW() - INTERVAL '7 days'
|
||||
GROUP BY user_id
|
||||
HAVING COUNT(*) > 30
|
||||
```
|
||||
|
||||
**Action** :
|
||||
- Enquête manuelle modérateur
|
||||
- Révocation badge si abus confirmé
|
||||
|
||||
**Patterns détectés** :
|
||||
- Signalement massif (>30/semaine)
|
||||
- Taux de pertinence <50% malgré volume élevé
|
||||
- Signalements tous rejetés sur période 7 jours
|
||||
- Signalements ciblant toujours même créateur (harcèlement)
|
||||
|
||||
---
|
||||
|
||||
#### 19.5.3 Audit trimestriel automatique
|
||||
|
||||
**Décision** : Tous les 3 mois, recalcul badges et révocation si critères non respectés
|
||||
|
||||
**Processus** :
|
||||
1. Recalcul taux pertinence sur période glissante 6 mois
|
||||
2. Vérification critères :
|
||||
- Bronze : minimum 70% pertinence
|
||||
- Argent : minimum 80% pertinence
|
||||
- Or : minimum 90% pertinence
|
||||
3. Révocation badge si taux insuffisant
|
||||
4. Email notification **7 jours avant audit** :
|
||||
```
|
||||
Votre badge Contributeur Argent sera audité dans 7 jours.
|
||||
Taux de pertinence actuel : 78%
|
||||
Minimum requis : 80%
|
||||
|
||||
Continuez à signaler du contenu pertinent pour conserver votre badge !
|
||||
```
|
||||
|
||||
**Après audit** :
|
||||
- Email résultat : "Badge conservé ✓" ou "Badge révoqué ✗"
|
||||
- Possibilité de réobtenir badge ultérieurement (pas de ban)
|
||||
|
||||
**Justification** :
|
||||
- Maintien qualité badges sur le long terme
|
||||
- Évite repos sur lauriers
|
||||
- Coût : 0€ (script automatique)
|
||||
|
||||
---
|
||||
|
||||
#### 19.5.4 Sanctions abus détecté
|
||||
|
||||
| Gravité | Abus détecté | Sanction |
|
||||
|---------|--------------|----------|
|
||||
| **Mineur** | >10 signalements/jour pendant 1 jour | Avertissement + limite 5/jour pendant 7 jours |
|
||||
| **Modéré** | >30 signalements/semaine avec <50% pertinence | Révocation badge + interdiction signalement 30 jours |
|
||||
| **Grave** | Signalements massifs coordonnés (farming) | Ban permanent fonctionnalité signalement + révocation tous badges |
|
||||
|
||||
**Notification sanction** :
|
||||
- Email + Push + In-app
|
||||
- Explication détaillée de l'abus détecté
|
||||
- Durée sanction
|
||||
- **Pas de recours** pour sanctions graves (farming confirmé)
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 19
|
||||
|
||||
| Point | Décision | Coût |
|
||||
|-------|----------|------|
|
||||
| **Badges (3 niveaux)** | Bronze (5), Argent (20), Or (50) signalements validés | 0€ |
|
||||
| **Modal découverte** | Affichage unique au 1er signalement | 0€ |
|
||||
| **Score fiabilité** | Calcul automatique pour priorisation | 0€ |
|
||||
| **Utilisateurs de confiance** | Statut auto Argent/Or | 0€ |
|
||||
| **Réduction Premium Or** | -50% pendant 3 mois (Post-MVP) | 0-200€/mois |
|
||||
| **Limite temporelle** | Max 10 signalements/24h | 0€ |
|
||||
| **Audit trimestriel** | Révocation si critères non respectés | 0€ |
|
||||
| **Sanctions abus** | Mineur/Modéré/Grave selon pattern | 0€ |
|
||||
|
||||
**Coût total MVP** : **0€**
|
||||
|
||||
**Coût total Post-MVP** : **0-200€/mois** (réductions Premium Or)
|
||||
|
||||
**ROI** : **Positif dès 2-3 utilisateurs Or actifs** (économie modération > coût réductions)
|
||||
|
||||
---
|
||||
|
||||
**Conformité** :
|
||||
- ✅ RGPD : données modération anonymisées après 3 ans
|
||||
- ✅ Transparence : utilisateur informé dès le 1er signalement
|
||||
- ✅ Anti-discrimination : système accessible à tous, basé uniquement sur pertinence
|
||||
|
||||
**Scalabilité** :
|
||||
- 0-100 utilisateurs actifs : système automatique, 0€
|
||||
- 100-1000 utilisateurs actifs : 0-50€/mois (quelques badges Or)
|
||||
- 1000+ utilisateurs actifs : 50-200€/mois (max 50 badges Or simultanés)
|
||||
|
||||
---
|
||||
|
||||
**Prochaine section à clarifier** : Section 20 (si nécessaire) ou validation complète règles métier
|
||||
167
docs/regles-metier/16-publicites.md
Normal file
167
docs/regles-metier/16-publicites.md
Normal file
@@ -0,0 +1,167 @@
|
||||
## 6. Publicités
|
||||
|
||||
### 6.1 Système de campagnes publicitaires
|
||||
|
||||
**Décision** : Interface self-service avec maîtrise budget et métriques détaillées
|
||||
|
||||
**Fonctionnalités publicitaire** :
|
||||
|
||||
#### Création de campagne
|
||||
|
||||
**Paramètres configurables** :
|
||||
|
||||
| Paramètre | Options | Justification |
|
||||
|-----------|---------|---------------|
|
||||
| **Budget total** | Montant libre (min 50€) | Maîtrise coût total |
|
||||
| **Durée campagne** | Date début/fin + étalement | Ex: 300€ sur 2 semaines |
|
||||
| **Ciblage géographique** | Point GPS / Ville / Département / Région / National | Précision selon besoin |
|
||||
| **Ciblage horaire** | Plages horaires (ex: 7h-9h, 17h-19h) | Optimisation trajet domicile-travail |
|
||||
| **Centres d'intérêt** | Tags (ex: Automobile, Voyage) | Ciblage thématique |
|
||||
| **Tranche d'âge** | Tout public / 13+ / 16+ / 18+ | Respect classifications |
|
||||
|
||||
**Étalement budget** :
|
||||
```
|
||||
Exemple campagne :
|
||||
- Budget : 300€
|
||||
- Durée : 14 jours
|
||||
- Zone : Département du Var
|
||||
- Horaires : 7h-9h + 17h-19h (rush)
|
||||
|
||||
Calcul automatique :
|
||||
→ Budget/jour = 300€ / 14 = 21.43€/jour
|
||||
→ Diffusions/jour estimées : ~430 (0.05€/écoute)
|
||||
→ Alerte si budget épuisé avant fin (réajustement possible)
|
||||
```
|
||||
|
||||
**Mode de paiement** :
|
||||
- ✅ Prépaiement obligatoire (évite impayés)
|
||||
- ✅ Carte bancaire uniquement (Mangopay)
|
||||
- ✅ Recharge automatique optionnelle (si budget <10%)
|
||||
|
||||
#### Validation et modération
|
||||
|
||||
**Processus** :
|
||||
1. Publicitaire upload audio pub (formats : MP3, AAC)
|
||||
2. **Validation manuelle obligatoire** (modérateur RoadWave)
|
||||
- Délai : 24-48h ouvrées
|
||||
- Critères : respect réglementation, qualité audio, classification correcte
|
||||
3. Si accepté → campagne démarre à la date choisie
|
||||
4. Si refusé → email avec raison + remboursement automatique
|
||||
|
||||
**Contenus interdits en pub** :
|
||||
- ❌ Alcool, tabac (réglementation française)
|
||||
- ❌ Jeux d'argent
|
||||
- ❌ Contenu politique (pendant campagnes électorales)
|
||||
- ❌ Contenu sexuel ou violence
|
||||
- ✅ Tous commerces/services légaux
|
||||
|
||||
#### Dashboard métriques engagement
|
||||
|
||||
**Indicateurs temps réel** :
|
||||
|
||||
| Métrique | Description | Utilité |
|
||||
|----------|-------------|---------|
|
||||
| **Impressions** | Nombre de diffusions | Volume exposition |
|
||||
| **Écoutes complètes** | Pub écoutée >80% | Engagement réel |
|
||||
| **Taux de skip** | % skip après délai min | Qualité contenu |
|
||||
| **Durée moyenne écoute** | Secondes écoutées | Rétention attention |
|
||||
| **Likes** | Nombre de likes | Appréciation contenu |
|
||||
| **Abonnements** | Abonnements au créateur pub | Conversion forte |
|
||||
| **Coût par écoute** | Budget / écoutes complètes | ROI campagne |
|
||||
| **Répartition géographique** | Heatmap diffusions | Validation ciblage |
|
||||
| **Répartition horaire** | Graphique par heure | Optimisation horaires |
|
||||
|
||||
**Métriques engagement avancées** :
|
||||
- **Taux complétion par tranche d'âge** : identifier audience réceptive
|
||||
- **Carte de chaleur GPS** : visualiser zones forte écoute
|
||||
- **Comparatif campagnes** : A/B testing créatifs publicitaires
|
||||
|
||||
**Export données** :
|
||||
- ✅ CSV/Excel pour analyse externe
|
||||
- ✅ Graphiques interactifs (Chart.js)
|
||||
- ✅ Rapport PDF automatique fin de campagne
|
||||
|
||||
#### Gestion budget et alertes
|
||||
|
||||
**Suivi temps réel** :
|
||||
- Dashboard : Budget restant, % consommé, jours restants
|
||||
- Projection : "À ce rythme, budget épuisé dans X jours"
|
||||
- Alerte email/push si :
|
||||
- Budget consommé à 80%
|
||||
- Budget consommé à 90%
|
||||
- Budget épuisé
|
||||
- Campagne terminée (rapport final)
|
||||
|
||||
**Ajustements en cours** :
|
||||
- ✅ Pause campagne (budget conservé)
|
||||
- ✅ Prolonger campagne (recharge budget)
|
||||
- ✅ Modifier ciblage horaire/géo (si <50% budget consommé)
|
||||
- ❌ Modifier audio (nécessite nouvelle validation)
|
||||
|
||||
#### Système d'enchères (post-MVP)
|
||||
|
||||
**Optionnel future** :
|
||||
- Enchère au CPM (coût pour 1000 impressions)
|
||||
- Priorité selon prix : pub prix élevé → diffusion privilégiée
|
||||
- Floor price : 2€ CPM minimum
|
||||
- Évite surcharge pub : max 1 pub / 5 contenus stricte
|
||||
|
||||
**Justification décision MVP** :
|
||||
- Tarif fixe simple : 0.05€/écoute complète
|
||||
- Pas de complexité enchères immédiatement
|
||||
- Scalable : passage enchères ultérieur si demande forte
|
||||
|
||||
---
|
||||
|
||||
### 6.2 Insertion et fréquence
|
||||
|
||||
**Décision** : Paramétrable admin + respect expérience utilisateur
|
||||
|
||||
**Fréquence d'insertion** :
|
||||
- **Défaut : 1 pub / 5 contenus** (utilisateurs gratuits)
|
||||
- **Paramétrable admin** : curseur 1/3 à 1/10
|
||||
- **Utilisateurs Premium** : 0 pub (modèle sans publicité)
|
||||
|
||||
**Règles strictes** :
|
||||
- ⚠️ **Jamais d'interruption** contenu en cours
|
||||
- Pub s'insère uniquement **entre deux contenus** (pendant délai 2s)
|
||||
- Rotation : même pub max **3 fois/jour** par utilisateur (évite saturation)
|
||||
- Limite : max **6 pubs/heure** par utilisateur (évite spam)
|
||||
|
||||
**Ciblage intelligent** :
|
||||
- Géolocalisation prioritaire (point GPS > ville > département > région > national)
|
||||
- Centres d'intérêt secondaires (tags utilisateur)
|
||||
- Horaire (campagne 7h-9h → diffusion uniquement pendant plage)
|
||||
|
||||
**Volume audio normalisé** :
|
||||
- Pub normalisée à **-14 LUFS** (standard broadcast)
|
||||
- Évite effet "pub trop forte" (frustration utilisateur)
|
||||
- Validation automatique via FFmpeg lors encodage
|
||||
|
||||
---
|
||||
|
||||
### 6.3 Caractéristiques publicités
|
||||
|
||||
**Durée** :
|
||||
- Minimum : **10 secondes**
|
||||
- Maximum : **60 secondes**
|
||||
- Recommandé : **15-30 secondes** (sweet spot engagement)
|
||||
|
||||
**Skippable** :
|
||||
- Délai minimum obligatoire : **5 secondes** (paramétrable admin : 3-10s)
|
||||
- Bouton "Passer la publicité" apparaît après délai
|
||||
- Durée minimale comptabilisée pour facturation
|
||||
|
||||
**Facturation** :
|
||||
- **Écoute complète** (>80%) : 0.05€ facturé publicitaire
|
||||
- **Skip après délai min** : 0.02€ (exposition partielle)
|
||||
- **Skip immédiat** (<5s) : 0€ (pas d'engagement)
|
||||
|
||||
**Justification modèle tarif** :
|
||||
- Incitatif qualité : pub engageante = coût réduit
|
||||
- Équitable : publicitaire paie pour attention réelle
|
||||
- Transparent : dashboard montre écoutes complètes vs skips
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 6
|
||||
176
docs/regles-metier/17-premium.md
Normal file
176
docs/regles-metier/17-premium.md
Normal file
@@ -0,0 +1,176 @@
|
||||
## 10. Premium
|
||||
|
||||
### 10.1 Offre et tarification
|
||||
|
||||
**Décision** : Deux formules sans essai gratuit
|
||||
|
||||
| Formule | Prix | Économie | Prix effectif |
|
||||
|---------|------|----------|---------------|
|
||||
| **Mensuel** | 4.99€/mois | - | 4.99€/mois |
|
||||
| **Annuel** | 49.99€/an | 2 mois offerts | 4.16€/mois |
|
||||
|
||||
**❌ Pas d'essai gratuit**
|
||||
|
||||
**Raisons** :
|
||||
- **Anti-abus vacances** : évite inscriptions opportunistes (essai 14j avant road trip vacances, puis annulation)
|
||||
- **Protection revenus créateurs** : les écoutes Premium rémunèrent créateurs dès jour 1
|
||||
- **Simplicité** : pas de gestion période trial + conversion
|
||||
- **Engagement** : utilisateur qui paie dès début = plus engagé
|
||||
|
||||
**❌ Pas de partage familial (MVP)**
|
||||
|
||||
**Raisons** :
|
||||
- Complexité technique (gestion invitations, validation liens, limite devices)
|
||||
- Risque abus ("familles" de 6 inconnus)
|
||||
- Coût dev/support élevé pour ROI incertain
|
||||
- La plupart des users RoadWave sont individuels (conducteurs)
|
||||
- **Post-MVP** : Si forte demande, offre "Famille" à 9.99€/mois pour 5 comptes
|
||||
|
||||
**Justification tarif** :
|
||||
- **Aligné marché bas** : Spotify = 10.99€, YouTube Premium = 11.99€, Apple Music = 10.99€
|
||||
- **Prix accessible** : cible conducteurs quotidiens (budget raisonnable)
|
||||
- **Incitation annuel** : 2 mois offerts = engagement long terme + réduction churn
|
||||
|
||||
---
|
||||
|
||||
### 10.2 Multi-devices et détection simultanée
|
||||
|
||||
**Décision** : 1 seul stream actif par compte à tout moment
|
||||
|
||||
**Détection connexion simultanée** :
|
||||
|
||||
```
|
||||
User A écoute sur iPhone
|
||||
→ User A lance sur iPad
|
||||
→ Détection : session active iPhone existe
|
||||
→ Action : Arrêt lecture iPhone (WebSocket close)
|
||||
→ Message iPhone : "Lecture interrompue : votre compte est utilisé sur un autre appareil"
|
||||
→ Lecture démarre iPad
|
||||
```
|
||||
|
||||
**Implémentation technique** :
|
||||
|
||||
```
|
||||
Redis : active_streams:{user_id} → {device_id, started_at}
|
||||
TTL : 5 minutes (refresh à chaque heartbeat)
|
||||
|
||||
Heartbeat toutes les 30s depuis app :
|
||||
→ Si autre device détecté : kill session actuelle
|
||||
→ Si pas de heartbeat pendant 5 min : considérer session morte
|
||||
```
|
||||
|
||||
**Exceptions** :
|
||||
- Contenus téléchargés (offline) ne comptent pas comme stream actif
|
||||
- Transition rapide device (<10s) tolérée (changement voiture → maison)
|
||||
|
||||
**Justification** :
|
||||
- **Anti-partage compte** : empêche 2 personnes d'utiliser même compte Premium
|
||||
- **Protection revenus créateurs** : 1 abonnement = 1 personne = 1 écoute
|
||||
- **UX claire** : message explicite, pas de coupure brutale
|
||||
|
||||
---
|
||||
|
||||
### 10.3 Contenus exclusifs Premium
|
||||
|
||||
**Décision** : Créateur décide (déjà couvert section 9.6)
|
||||
|
||||
**Rappel règles** :
|
||||
- Toggle "Réservé Premium" par contenu
|
||||
- Aucune limite de ratio gratuit/premium
|
||||
- Badge 👑 visible
|
||||
- Users gratuits : lecture bloquée avec CTA "Passez Premium"
|
||||
|
||||
**Impact algorithme** :
|
||||
- Contenus premium inclus dans recommandations
|
||||
- Si user gratuit → skip automatique (ne consomme pas slot)
|
||||
- Si user premium → diffusé normalement selon score
|
||||
|
||||
---
|
||||
|
||||
### 10.4 Avantages Premium
|
||||
|
||||
**Inclus dans l'abonnement** :
|
||||
|
||||
| Avantage | Gratuit | Premium |
|
||||
|----------|---------|---------|
|
||||
| **Publicités** | 1/5 contenus | 0 (aucune) |
|
||||
| **Contenus exclusifs** | ❌ Bloqués | ✅ Accès complet |
|
||||
| **Qualité audio** | 48 kbps Opus | 64 kbps Opus |
|
||||
| **Mode offline** | 50 contenus max | Illimité |
|
||||
| **Historique écoute** | 100 derniers | Illimité |
|
||||
|
||||
**Qualité audio** :
|
||||
- Gratuit : 48 kbps Opus (~20 MB/h) = très correct pour voix
|
||||
- Premium : 64 kbps Opus (~30 MB/h) = excellente qualité
|
||||
|
||||
**Justification différences** :
|
||||
- **0 pub** = argument principal (confort écoute)
|
||||
- **Qualité audio** = avantage tangible audiophiles
|
||||
- **Offline illimité** = use case road trips longs
|
||||
- **Pas d'over-engineering** : pas de badges cosmétiques, fonctionnalités sociales, etc. (focus essentiel)
|
||||
|
||||
---
|
||||
|
||||
### 10.5 Gestion abonnement
|
||||
|
||||
**Souscription** :
|
||||
|
||||
| Canal | Prestataire | Prix | Commission |
|
||||
|-------|-------------|------|------------|
|
||||
| **Web (desktop/mobile)** | Mangopay | 4.99€ | 1.8% + 0.18€ = 0.27€ |
|
||||
| **iOS App** | Apple In-App Purchase | 5.99€ | 30% (Apple) |
|
||||
| **Android App** | Google Play Billing | 5.99€ | 30% (Google) |
|
||||
|
||||
**Majoration mobile (5.99€)** :
|
||||
- Apple/Google prennent 30% de commission
|
||||
- RoadWave majore prix de 20% pour compenser
|
||||
- **Incitation web** : Email aux users "Abonnez-vous sur roadwave.com pour 4.99€/mois" (38% moins cher en frais !)
|
||||
|
||||
**Renouvellement automatique** :
|
||||
- Email rappel **7 jours avant** renouvellement
|
||||
- Email confirmation **après** renouvellement réussi
|
||||
- Retry automatique si échec paiement (3 tentatives sur 7 jours)
|
||||
- Annulation automatique après 3 échecs
|
||||
|
||||
**Annulation** :
|
||||
- Self-service dans Settings app : "Abonnement > Annuler"
|
||||
- Accès Premium maintenu jusqu'à **fin période payée**
|
||||
- Pas de remboursement prorata (standard industrie)
|
||||
- Email confirmation annulation avec date fin d'accès
|
||||
|
||||
**Réabonnement** :
|
||||
- Possibilité immédiate
|
||||
- ❌ Pas de nouvelle période d'essai (pas d'essai du tout)
|
||||
|
||||
**Architecture données** :
|
||||
|
||||
```sql
|
||||
CREATE TABLE subscriptions (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID NOT NULL REFERENCES users(id) UNIQUE,
|
||||
mangopay_recurring_payin_id VARCHAR(255), -- Null si IAP
|
||||
mangopay_user_id VARCHAR(255), -- Null si IAP
|
||||
apple_transaction_id VARCHAR(255), -- Null si Mangopay
|
||||
google_purchase_token VARCHAR(255), -- Null si Mangopay
|
||||
status VARCHAR(50) NOT NULL, -- 'active', 'cancelled', 'expired', 'past_due'
|
||||
plan VARCHAR(50) NOT NULL, -- 'monthly', 'yearly'
|
||||
current_period_start TIMESTAMP NOT NULL,
|
||||
current_period_end TIMESTAMP NOT NULL,
|
||||
cancelled_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
**Vérification Premium en temps réel** :
|
||||
|
||||
```
|
||||
Cache Redis : premium:{user_id} → boolean (TTL 1h)
|
||||
Refresh via webhooks :
|
||||
- Mangopay : PAYIN_NORMAL_SUCCEEDED, PAYIN_NORMAL_FAILED
|
||||
- Apple : App Store Server Notifications
|
||||
- Google : Real-time Developer Notifications
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 10
|
||||
308
docs/regles-metier/18-monetisation-createurs.md
Normal file
308
docs/regles-metier/18-monetisation-createurs.md
Normal file
@@ -0,0 +1,308 @@
|
||||
## 9. Monétisation créateurs
|
||||
|
||||
### 9.1 Pourboires
|
||||
|
||||
**Décision** : ❌ Fonctionnalité abandonnée pour le MVP
|
||||
|
||||
**Raisons** :
|
||||
- Complexité juridique (collecte pour compte de tiers, TVA variable)
|
||||
- Frais de transaction élevés sur petits montants (Mangopay ~1.8% + 0.18€)
|
||||
- UX additionnelle à développer (wallet, transactions, confirmations)
|
||||
- Charge comptable importante pour la plateforme
|
||||
|
||||
**Post-MVP** : Possible réintégration avec crypto (Bitcoin/Lightning Network) si législation UE l'autorise clairement (régulation MiCA en cours).
|
||||
|
||||
---
|
||||
|
||||
### 9.2 Conditions d'activation de la monétisation
|
||||
|
||||
**Décision** : 5 critères cumulatifs obligatoires
|
||||
|
||||
| Critère | Seuil | Justification |
|
||||
|---------|-------|---------------|
|
||||
| **Ancienneté** | Compte créé depuis ≥ 3 mois | Anti-fraude : temps de détecter comportements suspects |
|
||||
| **Popularité** | ≥ 500 abonnés | Garantit audience réelle et engagée |
|
||||
| **Engagement** | ≥ 10 000 écoutes complètes cumulées | Créateurs produisant du contenu de qualité |
|
||||
| **Fiabilité** | Aucun strike actif, 0 contenu modéré dans les 6 derniers mois | Historique propre requis |
|
||||
| **Régularité** | ≥ 5 contenus publiés dans les 90 derniers jours | Activité constante |
|
||||
|
||||
**Vérification** : Automatique via requêtes SQL lors de la demande d'activation
|
||||
|
||||
**Affichage** :
|
||||
- Bouton "Demander la monétisation" dans profil créateur
|
||||
- Si critères non remplis → affichage progression vers objectifs
|
||||
- Si critères remplis → redirection vers KYC Mangopay
|
||||
|
||||
**Justification** :
|
||||
- **Anti-fraude** : Le délai de 3 mois permet de détecter les comptes suspects
|
||||
- **Qualité** : Seuls les créateurs sérieux avec audience réelle sont monétisés
|
||||
- **Coût administratif** : Réduit le nombre de comptes à gérer (KYC, comptabilité, virements)
|
||||
- **Légitimité** : Audience organique prouvée
|
||||
|
||||
---
|
||||
|
||||
### 9.3 KYC (Know Your Customer) et inscription
|
||||
|
||||
**Décision** : Statut juridique professionnel obligatoire
|
||||
|
||||
**Statuts acceptés** :
|
||||
- Auto-entrepreneur (micro-BNC pour artistes/créateurs de contenu)
|
||||
- SARL/SAS/SASU (sociétés)
|
||||
|
||||
**Documents requis** :
|
||||
|
||||
| Document | Obligatoire | Format | Validité |
|
||||
|----------|-------------|--------|----------|
|
||||
| **SIRET** | ✅ | 14 chiffres | Permanent |
|
||||
| **RIB professionnel** | ✅ | IBAN FR | Permanent |
|
||||
| **Pièce d'identité** | ✅ | CNI/Passeport | En cours de validité |
|
||||
| **Numéro TVA intracommunautaire** | ⚠️ Si applicable | FR + 11 chiffres | Permanent |
|
||||
| **Kbis <3 mois** | ⚠️ Si société | PDF | <3 mois |
|
||||
|
||||
**Vérification** : Via Mangopay (KYC intégré + vérification bancaire)
|
||||
|
||||
**Délai** : 24-72h si documents conformes
|
||||
|
||||
**Rejet possible si** :
|
||||
- Documents invalides/illisibles
|
||||
- Identité ne correspond pas au compte RoadWave
|
||||
- Liste noire anti-blanchiment (vérification automatique Mangopay)
|
||||
- RIB non professionnel (particulier)
|
||||
|
||||
**Base légale** :
|
||||
- **Conformité fiscale** : L'État français impose déclaration revenus >1200€/an (DAS2)
|
||||
- **Anti-blanchiment** : Directive EU 2018/843 (5ème directive LCB-FT)
|
||||
- **RGPD** : Données hébergées EU via Mangopay (conforme)
|
||||
|
||||
**Justification** :
|
||||
- **Responsabilité légale** : RoadWave doit pouvoir prouver identité réelle créateurs monétisés
|
||||
- **Automatisation** : Mangopay gère tout (KYC, vérifications, conformité, e-wallets)
|
||||
- **KYC gratuit** : inclus dans l'offre Mangopay (vs 1.20€ chez Stripe)
|
||||
- **Souveraineté EU** : Mangopay est européen (France/Luxembourg), régulé ACPR
|
||||
|
||||
---
|
||||
|
||||
### 9.4 Sources de revenus créateurs
|
||||
|
||||
#### A) Publicités (utilisateurs gratuits)
|
||||
|
||||
**Formule** : **3€ / 1000 écoutes complètes** (CPM créateur)
|
||||
|
||||
**Répartition économique** :
|
||||
|
||||
```
|
||||
Publicité facturée par RoadWave : 0.05€/écoute complète = 50€ CPM
|
||||
├─ Créateur touche : 3€ (6% du CA pub)
|
||||
└─ Plateforme garde : 47€ (94%)
|
||||
├─ CDN + infrastructure : ~10-15€
|
||||
├─ Modération + support : ~5-10€
|
||||
├─ Développement + R&D : ~10-15€
|
||||
└─ Marge opérationnelle : ~10-15€
|
||||
```
|
||||
|
||||
**Exemple concret** :
|
||||
- 10 000 écoutes/mois → créateur touche **30€**
|
||||
- 50 000 écoutes/mois → créateur touche **150€**
|
||||
- 100 000 écoutes/mois → créateur touche **300€**
|
||||
|
||||
**Comparaison industrie** :
|
||||
- YouTube : 3-5€/1000 vues
|
||||
- Spotify : 3-4€/1000 écoutes
|
||||
- RoadWave : 3€/1000 écoutes (aligné)
|
||||
|
||||
**Règles comptabilisation** :
|
||||
- ✅ Écoute complète = ≥80% du contenu écouté
|
||||
- ✅ Utilisateur gratuit uniquement
|
||||
- ❌ Écoutes Premium ne comptent pas ici (autre système)
|
||||
- ❌ Bots détectés exclus (rate limiting + analyse patterns)
|
||||
|
||||
---
|
||||
|
||||
#### B) Abonnés Premium
|
||||
|
||||
**Formule** : **70% au créateur, 30% à la plateforme**
|
||||
|
||||
**Répartition proportionnelle au temps d'écoute effectif** :
|
||||
|
||||
```
|
||||
Utilisateur Premium = 4.99€/mois
|
||||
├─ 3.49€ reversés aux créateurs (70%)
|
||||
└─ 1.50€ gardés par plateforme (30%)
|
||||
|
||||
Si l'utilisateur écoute 3 créateurs ce mois :
|
||||
- Créateur A : 10h d'écoute (50%) → 1.75€
|
||||
- Créateur B : 6h d'écoute (30%) → 1.05€
|
||||
- Créateur C : 4h d'écoute (20%) → 0.70€
|
||||
```
|
||||
|
||||
**Calcul technique** :
|
||||
|
||||
```sql
|
||||
-- Pour chaque utilisateur Premium
|
||||
SELECT
|
||||
creator_id,
|
||||
SUM(listen_duration_seconds) AS total_seconds,
|
||||
(SUM(listen_duration_seconds) / total_user_seconds) AS ratio,
|
||||
(4.99 * 0.70 * ratio) AS revenue_euros
|
||||
FROM premium_listens
|
||||
WHERE user_id = :user_id
|
||||
AND month = :current_month
|
||||
GROUP BY creator_id;
|
||||
```
|
||||
|
||||
**Comparaison industrie** :
|
||||
- YouTube Premium : 70/30
|
||||
- Spotify : 70/30
|
||||
- Apple Music : 52/48 (moins avantageux)
|
||||
- RoadWave : 70/30 (standard)
|
||||
|
||||
**Justification** :
|
||||
- **Standard industrie** : ratio équitable éprouvé
|
||||
- **Incitation qualité** : créateurs les plus écoutés gagnent plus
|
||||
- **Équité** : pas de "winner takes all", chaque créateur écouté reçoit sa part
|
||||
- **Marge plateforme** : 30% couvre absence revenus pub sur Premium
|
||||
|
||||
---
|
||||
|
||||
### 9.5 Paiement des créateurs
|
||||
|
||||
**Seuil minimum** : 50€
|
||||
|
||||
- En dessous → solde reporté mois suivant
|
||||
- Évite frais bancaires sur micro-sommes
|
||||
- Standard industrie (YouTube/Twitch/Spotify = 50-100€)
|
||||
|
||||
**Fréquence** : Mensuelle
|
||||
|
||||
| Date | Action |
|
||||
|------|--------|
|
||||
| **Dernier jour du mois** (ex: 31 janvier) | Calcul revenus du mois via SQL |
|
||||
| **1-14 du mois suivant** | Traitement contestations/fraudes éventuelles |
|
||||
| **15 du mois suivant** (ex: 15 février) | Virement SEPA via Mangopay (Payout) |
|
||||
| **16-18 du mois suivant** | Réception virement (1-3 jours ouvrés SEPA) |
|
||||
|
||||
**Virement via Mangopay** :
|
||||
- SEPA pour comptes EU (gratuit, 1-3 jours)
|
||||
- Virement international hors EU (frais variables selon pays, rare en pratique)
|
||||
- **E-wallets automatiques** : chaque créateur possède un wallet Mangopay où ses revenus sont transférés automatiquement
|
||||
|
||||
**Tableau de bord créateur** (temps réel) :
|
||||
|
||||
| Métrique | Description | Mise à jour |
|
||||
|----------|-------------|-------------|
|
||||
| **Revenus pub** | Écoutes × CPM | Temps réel |
|
||||
| **Revenus premium** | Abonnés actifs × ratio écoute | Temps réel |
|
||||
| **Solde disponible** | Total revenus mois en cours | Temps réel |
|
||||
| **Solde en attente** | Revenus mois précédent (paiement le 15) | Figé fin de mois |
|
||||
| **Historique virements** | Liste des paiements reçus | Permanent |
|
||||
| **Export comptable CSV** | Données pour expert-comptable | Téléchargement |
|
||||
|
||||
**Gestion échecs virement** :
|
||||
1. Tentative 1 (15 du mois) → échec
|
||||
2. Retry automatique J+3
|
||||
3. Retry automatique J+7
|
||||
4. Si 3 échecs → suspension monétisation + email créateur (RIB invalide)
|
||||
|
||||
---
|
||||
|
||||
### 9.6 Contenus Premium exclusifs
|
||||
|
||||
**Décision** : Créateur décide individuellement pour chaque contenu
|
||||
|
||||
**Fonctionnement** :
|
||||
- Toggle "Réservé Premium" lors création/édition contenu
|
||||
- **Aucune limite imposée** : créateur peut mettre 0%, 50% ou 100% en premium
|
||||
- Badge 👑 visible sur interface utilisateur
|
||||
|
||||
**Comportement utilisateurs gratuits** :
|
||||
- Contenu premium visible dans liste/algo
|
||||
- Tentative lecture → overlay bloquant
|
||||
- Message : "Ce contenu est réservé aux abonnés Premium"
|
||||
- CTA : "Passez Premium pour 4.99€/mois"
|
||||
|
||||
**Comportement algorithme** :
|
||||
- Contenus premium inclus dans recommandations
|
||||
- Si user gratuit → contenu skippé automatiquement (ne consomme pas de slot)
|
||||
- Si user premium → diffusé normalement
|
||||
|
||||
**Métadonnées** :
|
||||
- Champ `is_premium` (boolean) en base
|
||||
- Index sur ce champ pour requêtes rapides
|
||||
- Cache Redis : `content:{id}:premium` (TTL 1h)
|
||||
|
||||
**Justification** :
|
||||
- **Liberté créateur** : chaque créateur choisit sa stratégie (freemium, tout gratuit, tout premium)
|
||||
- **Incitation Premium** : contenu exclusif = argument fort pour s'abonner
|
||||
- **Équité** : un petit créateur peut tout mettre en premium, un gros peut tout offrir gratuitement
|
||||
|
||||
---
|
||||
|
||||
### 9.7 Obligations fiscales
|
||||
|
||||
**RoadWave génère automatiquement** :
|
||||
|
||||
| Document | Fréquence | Destinataire | Base légale |
|
||||
|----------|-----------|--------------|-------------|
|
||||
| **Relevé mensuel PDF** | Chaque mois | Créateur | Transparence |
|
||||
| **Export CSV comptable** | À la demande | Créateur + expert-comptable | Facilitation déclarations |
|
||||
| **DAS2 annuel** | Si >1200€/an | Impôts (DGFIP) | Obligation légale France |
|
||||
|
||||
**Créateur responsable de** :
|
||||
- Déclarer ses revenus à l'URSSAF (cotisations sociales auto-entrepreneur ou IS/IR)
|
||||
- Déclarer ses revenus aux impôts (IR ou IS selon statut)
|
||||
- Gérer sa TVA si applicable (franchise en base jusqu'à ~37K€/an en micro-BNC)
|
||||
- Conserver justificatifs **10 ans** (obligation légale comptable)
|
||||
|
||||
**Mangopay transmet automatiquement** :
|
||||
- Données aux autorités fiscales EU via **DAC7** (directive 2021/514)
|
||||
- Justificatif de chaque virement (preuve bancaire pour comptabilité créateur)
|
||||
|
||||
**Exemple DAS2** :
|
||||
```
|
||||
Si créateur a touché 2500€ en 2026 :
|
||||
→ RoadWave envoie DAS2 aux impôts en janvier 2027
|
||||
→ Créateur reçoit copie par email
|
||||
→ Créateur doit déclarer ces 2500€ dans sa déclaration annuelle
|
||||
```
|
||||
|
||||
**Justification** :
|
||||
- **Conformité légale** : RoadWave doit déclarer revenus versés (DAS2, DAC7)
|
||||
- **Responsabilité fiscale** : Le créateur reste responsable de sa déclaration (impossible de gérer pour lui)
|
||||
- **Automatisation** : Minimise charge administrative côtés créateur et plateforme
|
||||
|
||||
---
|
||||
|
||||
### 9.8 Désactivation et suspension monétisation
|
||||
|
||||
**Créateur peut** :
|
||||
- Désactiver temporairement (vacances, pause création)
|
||||
- Réactiver sans refaire KYC si données à jour (<2 ans)
|
||||
- Solde conservé pendant désactivation
|
||||
|
||||
**Plateforme suspend automatiquement si** :
|
||||
|
||||
| Motif | Action | Réversible |
|
||||
|-------|--------|------------|
|
||||
| **Strike 3+ actif** | Suspension immédiate | Oui, après résolution strikes |
|
||||
| **Compte bancaire invalide** | Suspension après 3 échecs virement | Oui, après mise à jour RIB |
|
||||
| **Documents KYC expirés** | Suspension avec préavis 30j | Oui, après renouvellement docs |
|
||||
| **Fraude détectée** | Suspension immédiate + enquête | Cas par cas |
|
||||
|
||||
**Suppression définitive si** :
|
||||
- Demande du créateur (solde versé sous 30 jours)
|
||||
- Inactivité 24 mois + solde <50€ (purge RGPD)
|
||||
- Ban définitif compte (Strike 4)
|
||||
|
||||
**Notification** :
|
||||
- Email + in-app pour toute suspension
|
||||
- Raison explicite fournie
|
||||
- Procédure de réactivation indiquée
|
||||
|
||||
**Justification** :
|
||||
- **Flexibilité** : créateur peut faire pause sans perdre statut
|
||||
- **Sécurité** : plateforme doit pouvoir suspendre en cas problème légal/technique
|
||||
- **RGPD** : suppression auto données inactives après délai raisonnable
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 9
|
||||
633
docs/regles-metier/19-autres-comportements.md
Normal file
633
docs/regles-metier/19-autres-comportements.md
Normal file
@@ -0,0 +1,633 @@
|
||||
## 15. Autres comportements
|
||||
|
||||
### 15.1 Partage de contenu
|
||||
|
||||
**Décision** : Système de partage complet avec web player
|
||||
|
||||
#### 15.1.1 Bouton "Partager"
|
||||
|
||||
**Disponibilité** : Partout dans l'application
|
||||
|
||||
**Emplacements** :
|
||||
- Player en lecture (bouton dans contrôles)
|
||||
- Page profil créateur (sur chaque contenu)
|
||||
- Liste de recherche (menu contextuel)
|
||||
- Historique personnel
|
||||
|
||||
**Icône** : ⬆️ (universelle iOS/Android)
|
||||
|
||||
**Menu options** :
|
||||
- Copier le lien
|
||||
- WhatsApp
|
||||
- Email
|
||||
- SMS
|
||||
- Plus... (sheet natif OS)
|
||||
|
||||
**Justification** :
|
||||
- Viralité = croissance organique gratuite
|
||||
- Aucune friction, partage universel
|
||||
|
||||
---
|
||||
|
||||
#### 15.1.2 Comportement du lien partagé
|
||||
|
||||
**Format URL** : `https://roadwave.fr/share/c/[content_id]`
|
||||
|
||||
**Comportement multi-plateforme** :
|
||||
|
||||
```
|
||||
User clique lien partagé
|
||||
↓
|
||||
Page web responsive
|
||||
↓
|
||||
┌─────────────────────────────────┐
|
||||
│ Si app installée │
|
||||
│ → Deep link (ouverture directe) │
|
||||
└─────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────┐
|
||||
│ Si app non installée │
|
||||
│ → Web player + CTA téléchargement│
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Contenu de la page web** :
|
||||
|
||||
```html
|
||||
┌───────────────────────────────────────┐
|
||||
│ RoadWave │
|
||||
├───────────────────────────────────────┤
|
||||
│ [Image cover 16:9] │
|
||||
│ │
|
||||
│ 📻 Titre du contenu │
|
||||
│ Par @créateur · 12 min · 🎧 2.3K │
|
||||
│ │
|
||||
│ 📍 Paris 5e · Ancré │
|
||||
│ 🏷️ #Voyage #Histoire │
|
||||
│ │
|
||||
│ Description : Lorem ipsum... │
|
||||
│ │
|
||||
│ [▶️ Écouter maintenant] │
|
||||
│ (Player HTML5 si contenu public) │
|
||||
│ │
|
||||
│ ────────────────────────────────── │
|
||||
│ │
|
||||
│ 📱 Télécharger l'app RoadWave │
|
||||
│ [App Store] [Google Play] │
|
||||
│ │
|
||||
│ [Voir le profil de @créateur] │
|
||||
└───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Métadonnées Open Graph (SEO)** :
|
||||
|
||||
```html
|
||||
<meta property="og:title" content="[Titre contenu] - RoadWave">
|
||||
<meta property="og:description" content="[Description ou extrait]">
|
||||
<meta property="og:image" content="[URL cover image]">
|
||||
<meta property="og:audio" content="[URL audio si public]">
|
||||
<meta property="og:type" content="music.song">
|
||||
<meta property="og:site_name" content="RoadWave">
|
||||
<meta name="twitter:card" content="player">
|
||||
<meta name="twitter:player" content="https://roadwave.fr/player/[content_id]">
|
||||
```
|
||||
|
||||
**Deep linking** :
|
||||
- iOS : Universal Links (configuration `apple-app-site-association`)
|
||||
- Android : App Links (configuration `assetlinks.json`)
|
||||
- URL scheme : `roadwave://content/[content_id]`
|
||||
|
||||
**Justification** :
|
||||
- Meilleure viralité (partage social optimisé)
|
||||
- SEO (contenus indexés Google)
|
||||
- UX optimale (web + app)
|
||||
- Coût : 0€ (backend simple + CDN existant)
|
||||
|
||||
---
|
||||
|
||||
#### 15.1.3 Contenus Premium partagés
|
||||
|
||||
**Décision** : Preview 30 secondes + paywall
|
||||
|
||||
**Comportement** :
|
||||
|
||||
1. User clique lien contenu Premium partagé
|
||||
2. Page web affiche badge "👑 Contenu Premium"
|
||||
3. Player démarre automatiquement
|
||||
4. Après **30 secondes exactement** :
|
||||
- Fade out audio (2 secondes)
|
||||
- Overlay apparaît :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 👑 Contenu réservé Premium │
|
||||
│ │
|
||||
│ Profitez de ce contenu complet │
|
||||
│ et de milliers d'autres │
|
||||
│ sans publicité │
|
||||
│ │
|
||||
│ [Passer Premium - 4.99€/mois] │
|
||||
│ [Télécharger l'app] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
5. Utilisateur peut :
|
||||
- S'abonner Premium (redirection web Mangopay)
|
||||
- Télécharger l'app (redirection stores)
|
||||
- Rejouer les 30 premières secondes (illimité)
|
||||
|
||||
**Tracking** :
|
||||
- Métriques créateur : "Partages Premium" + "Conversions Premium"
|
||||
- Créateur touche sa part si conversion (70%)
|
||||
|
||||
**Justification** :
|
||||
- Équilibre viralité / monétisation
|
||||
- 30s = assez pour donner envie, pas assez pour satisfaire
|
||||
- Protège revenus créateurs
|
||||
|
||||
---
|
||||
|
||||
### 15.2 Profil créateur
|
||||
|
||||
**Décision** : Profil public complet et transparent
|
||||
|
||||
#### 15.2.1 Structure de la page profil
|
||||
|
||||
**URL** : `https://roadwave.fr/@[pseudo]`
|
||||
|
||||
**Layout** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ [Photo profil 120×120] │
|
||||
│ @pseudo ✓ │
|
||||
│ [Badge vérifié si applicable] │
|
||||
│ │
|
||||
│ Bio : Lorem ipsum dolor sit amet... │
|
||||
│ (300 caractères max) │
|
||||
│ │
|
||||
│ 🎧 1.2K abonnés │
|
||||
│ 📻 42 contenus │
|
||||
│ ⏱️ 18h de contenu créé │
|
||||
│ 🔊 54K écoutes totales │
|
||||
│ │
|
||||
│ [S'abonner] [Partager profil] [•••] │
|
||||
│ │
|
||||
│ ──────────────────────────────────── │
|
||||
│ │
|
||||
│ Contenus ▼ [Plus récents ▼] │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────┐ │
|
||||
│ │ [Cover] Titre contenu 1 │ │
|
||||
│ │ 12 min · 🎧 2.3K · 📍 Paris │ │
|
||||
│ │ [▶️] │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────┐ │
|
||||
│ │ [Cover] Titre contenu 2 │ │
|
||||
│ │ 8 min · 🎧 5.1K · 📍 Lyon │ │
|
||||
│ │ [▶️] │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Charger plus] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Informations affichées** :
|
||||
|
||||
| Élément | Visibilité | Détails |
|
||||
|---------|------------|---------|
|
||||
| **Photo + pseudo** | ✅ Public | Identité visuelle |
|
||||
| **Badge vérifié ✓** | ✅ Public (si applicable) | Compte authentique |
|
||||
| **Bio** | ✅ Public | 0-300 caractères, markdown basique (gras, italique, liens) |
|
||||
| **Nombre abonnés** | ✅ Public | Arrondi si >1000 (ex: 1.2K, 54K) |
|
||||
| **Nombre contenus** | ✅ Public | Exact |
|
||||
| **Durée totale créée** | ✅ Public | Arrondi en heures (ex: 18h, 142h) |
|
||||
| **Écoutes totales** | ✅ Public | Arrondi (ex: 54K, 1.2M) |
|
||||
| **Liste abonnés** | ❌ Privé | Protection vie privée (RGPD) |
|
||||
| **Revenus** | ❌ Privé | Confidentialité financière |
|
||||
| **Localisation précise** | ❌ Privé | Sécurité |
|
||||
| **Email** | ❌ Privé | Anti-spam |
|
||||
|
||||
**Tri des contenus** :
|
||||
|
||||
| Option | Comportement |
|
||||
|--------|--------------|
|
||||
| **Plus récents** | Date publication DESC (défaut) |
|
||||
| **Plus populaires** | Écoutes complètes × (1 + (date_publication - now) / 90 jours) |
|
||||
| **Plus anciens** | Date publication ASC |
|
||||
| **Par tag** | Filtre multi-sélection tags |
|
||||
|
||||
**Recherche locale** :
|
||||
- Barre recherche dans profil : "Rechercher dans les contenus de @pseudo"
|
||||
- Recherche full-text sur titres + descriptions
|
||||
|
||||
**Actions menu [•••]** :
|
||||
- Partager profil
|
||||
- Signaler profil (spam, usurpation)
|
||||
- Bloquer créateur (masque tous ses contenus)
|
||||
|
||||
---
|
||||
|
||||
#### 15.2.2 Statistiques publiques
|
||||
|
||||
**Décision** : Stats arrondies et motivantes
|
||||
|
||||
**Affichage public** :
|
||||
|
||||
| Métrique | Format affichage | Exemple |
|
||||
|----------|------------------|---------|
|
||||
| **Abonnés** | Exact si <1000, arrondi sinon | 342 / 1.2K / 54K / 1.2M |
|
||||
| **Écoutes totales** | Arrondi dès 1000 | 842 / 5.4K / 142K / 2.1M |
|
||||
| **Contenus publiés** | Exact | 42 contenus |
|
||||
| **Durée totale** | Arrondi en heures | 18h / 142h de contenu |
|
||||
|
||||
**Métriques PRIVÉES (créateur uniquement)** :
|
||||
|
||||
| Métrique | Disponible dans dashboard créateur |
|
||||
|----------|-------------------------------------|
|
||||
| **Taux complétion moyen** | 78% (écoutes >80% / écoutes totales) |
|
||||
| **Évolution abonnés** | Graphique 30j / 90j / 1 an |
|
||||
| **Écoutes par contenu** | Tableau détaillé |
|
||||
| **Revenus** | Dashboard monétisation dédié |
|
||||
| **Taux conversion Premium** | Partages → conversions |
|
||||
| **Démographie** | Âge / zone géo (agrégée, anonymisée) |
|
||||
|
||||
**Justification** :
|
||||
- Arrondi = évite comparaisons anxiogènes
|
||||
- Preuve sociale pour nouveaux auditeurs (trust)
|
||||
- Gamification douce (motivation créateurs)
|
||||
- Privacy by design
|
||||
|
||||
---
|
||||
|
||||
#### 15.2.3 Badge vérifié
|
||||
|
||||
**Décision** : Badge unique ✓ (vérifié officiel)
|
||||
|
||||
**Critères d'attribution** (au moins UN des critères) :
|
||||
|
||||
1. **KYC monétisation validé** : identité vérifiée via Mangopay KYC
|
||||
2. **Célébrité / Média officiel** : validation manuelle équipe RoadWave
|
||||
3. **Communauté significative** : ≥10K abonnés + compte actif >6 mois
|
||||
|
||||
**Affichage** :
|
||||
- Badge bleu **✓** accolé au pseudo (partout : profil, player, recherche)
|
||||
- Tooltip au survol/appui long : "Compte vérifié"
|
||||
|
||||
**Processus d'obtention** :
|
||||
|
||||
| Type | Processus |
|
||||
|------|-----------|
|
||||
| **Automatique (KYC)** | Badge attribué dès validation documents Mangopay |
|
||||
| **Manuel (célébrité)** | Formulaire demande → équipe vérifie identité → validation 48-72h |
|
||||
| **Automatique (10K)** | Badge attribué automatiquement à 10K abonnés si compte >6 mois |
|
||||
|
||||
**Retrait du badge** :
|
||||
- Suspension monétisation → badge retiré temporairement
|
||||
- Strikes multiples → badge retiré définitivement
|
||||
- Usurpation identité détectée → ban + retrait
|
||||
|
||||
**Justification** :
|
||||
- Combat usurpations d'identité
|
||||
- Trust auditeurs (surtout pour médias/personnalités)
|
||||
- Simplicité (1 seul badge, pas de gamification excessive)
|
||||
- Coût : 0€ (champ boolean `verified` en DB)
|
||||
|
||||
---
|
||||
|
||||
### 15.3 Recherche
|
||||
|
||||
**Décision** : Recherche full-text + géo + filtres avancés
|
||||
|
||||
#### 15.3.1 Recherche par mot-clé
|
||||
|
||||
**Implémentation** : PostgreSQL full-text search (français)
|
||||
|
||||
**Configuration technique** :
|
||||
|
||||
```sql
|
||||
-- Index full-text optimisé français
|
||||
CREATE INDEX idx_content_search ON contents
|
||||
USING GIN(
|
||||
to_tsvector('french',
|
||||
coalesce(title, '') || ' ' ||
|
||||
coalesce(description, '') || ' ' ||
|
||||
coalesce(creator_pseudo, '')
|
||||
)
|
||||
);
|
||||
|
||||
-- Recherche avec ranking
|
||||
SELECT
|
||||
c.*,
|
||||
ts_rank(
|
||||
to_tsvector('french', c.title || ' ' || c.description),
|
||||
plainto_tsquery('french', $search_query)
|
||||
) AS rank
|
||||
FROM contents c
|
||||
WHERE to_tsvector('french', c.title || ' ' || c.description)
|
||||
@@ plainto_tsquery('french', $search_query)
|
||||
ORDER BY rank DESC, listen_count DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
**Champs indexés** :
|
||||
- Titre du contenu (poids × 3)
|
||||
- Description (poids × 1)
|
||||
- Pseudo créateur (poids × 2)
|
||||
- Tags (poids × 1.5)
|
||||
|
||||
**Fonctionnalités** :
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Stemming français** | "voyages" trouve "voyage", "voyager", etc. |
|
||||
| **Correction auto** | Suggestion si 0 résultat |
|
||||
| **Recherches populaires** | "Essayez plutôt : balade paris, audio-guide louvre" |
|
||||
| **Historique personnel** | 10 dernières recherches sauvegardées |
|
||||
| **Autocomplete** | Suggestions pendant frappe (top 5) |
|
||||
|
||||
**Coût** : 0€ (PostgreSQL natif)
|
||||
|
||||
**Migration future** :
|
||||
- Si >100K contenus : Meilisearch (typo-tolerance avancée, ~20-50€/mois)
|
||||
- Si >1M contenus : Elasticsearch cluster
|
||||
|
||||
**Justification** :
|
||||
- PostgreSQL full-text = performant jusqu'à 500K contenus
|
||||
- Stemming français natif
|
||||
- 0€, aucune dépendance externe
|
||||
|
||||
---
|
||||
|
||||
#### 15.3.2 Recherche géographique
|
||||
|
||||
**Décision** : Recherche lieu + rayon paramétrable
|
||||
|
||||
**Interface utilisateur** :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 🔍 Recherche contenu... │
|
||||
├─────────────────────────────────────┤
|
||||
│ <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] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Géocodage** :
|
||||
|
||||
| Service | Usage | Coût |
|
||||
|---------|-------|------|
|
||||
| **Nominatim (OSM)** | MVP (API publique) | 0€ (rate limit 1 req/s) |
|
||||
| **Nominatim self-hosted** | Scale (Docker) | 20-50€/mois VPS |
|
||||
| **Mapbox Geocoding** | Fallback premium | 0.50€ / 1000 requêtes |
|
||||
|
||||
**Processus de recherche géo** :
|
||||
|
||||
1. User tape "Louvre" ou "Paris"
|
||||
2. Autocomplete via Nominatim → liste suggestions
|
||||
3. User sélectionne → récupération coordonnées (lat, lon)
|
||||
4. Requête PostGIS :
|
||||
|
||||
```sql
|
||||
SELECT c.*,
|
||||
ST_Distance(c.location::geography, ST_Point($lon, $lat)::geography) AS distance
|
||||
FROM contents c
|
||||
WHERE ST_DWithin(
|
||||
c.location::geography,
|
||||
ST_Point($lon, $lat)::geography,
|
||||
$radius_meters
|
||||
)
|
||||
ORDER BY distance ASC;
|
||||
```
|
||||
|
||||
**Affichage résultats** :
|
||||
- Tri par défaut : distance croissante
|
||||
- Indication distance : "À 2.3 km" / "À 15 km" / "À 142 km"
|
||||
- Option carte : markers cliquables (clustering si >50 résultats)
|
||||
|
||||
**Coût** :
|
||||
- MVP : 0€ (Nominatim public)
|
||||
- Scale : 20-50€/mois (Nominatim self-hosted Docker)
|
||||
|
||||
**Justification** :
|
||||
- Essentiel pour tourisme / planification trajet
|
||||
- OpenStreetMap = pas de dépendance Google
|
||||
- PostGIS = performant (index GIST natif)
|
||||
|
||||
---
|
||||
|
||||
#### 15.3.3 Filtres avancés
|
||||
|
||||
**Décision** : 7 catégories de filtres combinables
|
||||
|
||||
**Interface filtres** :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Filtres [×] │
|
||||
├─────────────────────────────────────┤
|
||||
│ Type de contenu │
|
||||
│ ☐ Contenu court (<5 min) │
|
||||
│ ☐ Podcast (>5 min) │
|
||||
│ ☐ Radio live │
|
||||
│ ☐ Audio-guide │
|
||||
│ │
|
||||
│ Durée │
|
||||
│ ○ Toutes durées │
|
||||
│ ○ <5 min │
|
||||
│ ○ 5-15 min │
|
||||
│ ○ 15-30 min │
|
||||
│ ○ >30 min │
|
||||
│ │
|
||||
│ Classification âge │
|
||||
│ ☐ Tout public │
|
||||
│ ☐ 13+ │
|
||||
│ ☐ 16+ │
|
||||
│ ☐ 18+ │
|
||||
│ │
|
||||
│ Géo-pertinence │
|
||||
│ ☐ Ancré (lieu précis) │
|
||||
│ ☐ Contextuel (zone large) │
|
||||
│ ☐ Neutre (national) │
|
||||
│ │
|
||||
│ Tags (multi-sélection) │
|
||||
│ ☐ Automobile ☐ Voyage │
|
||||
│ ☐ Famille ☐ Histoire │
|
||||
│ ☐ Économie ☐ Sciences │
|
||||
│ ... (liste complète tags) │
|
||||
│ │
|
||||
│ Date de publication │
|
||||
│ ○ Toutes dates │
|
||||
│ ○ Dernières 24h │
|
||||
│ ○ Cette semaine │
|
||||
│ ○ Ce mois │
|
||||
│ ○ Cette année │
|
||||
│ │
|
||||
│ Abonnement │
|
||||
│ ○ Tous les contenus │
|
||||
│ ○ Gratuits uniquement │
|
||||
│ ○ Premium uniquement 👑 │
|
||||
│ │
|
||||
│ ────────────────────────────── │
|
||||
│ [Réinitialiser] [Appliquer] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Options de tri** :
|
||||
|
||||
| Tri | Algorithme |
|
||||
|-----|-----------|
|
||||
| **Pertinence** | Score recherche × (1 + log(listen_count + 1)) |
|
||||
| **Popularité** | Écoutes complètes derniers 30j DESC |
|
||||
| **Récent** | Date publication DESC |
|
||||
| **Proximité** | Distance GPS ASC (si recherche géo active) |
|
||||
| **Durée** | Durée audio ASC ou DESC |
|
||||
|
||||
**Sauvegarde de recherches** :
|
||||
|
||||
- Bouton "💾 Sauvegarder cette recherche"
|
||||
- Nom personnalisable : "Podcasts voyage Paris"
|
||||
- Maximum **5 recherches sauvegardées**
|
||||
- Accès rapide : onglet "Recherches sauvegardées" dans page recherche
|
||||
- Notifications optionnelles : "3 nouveaux contenus dans 'Podcasts voyage Paris'"
|
||||
|
||||
**Performances** :
|
||||
|
||||
```sql
|
||||
-- Index composites pour filtres
|
||||
CREATE INDEX idx_content_filters ON contents (
|
||||
content_type,
|
||||
duration,
|
||||
age_rating,
|
||||
geo_type,
|
||||
published_at
|
||||
);
|
||||
|
||||
-- Index GIN pour tags
|
||||
CREATE INDEX idx_content_tags ON contents USING GIN(tags);
|
||||
```
|
||||
|
||||
**Coût** : 0€ (PostgreSQL + index standards)
|
||||
|
||||
**Justification** :
|
||||
- Filtres essentiels pour découvrabilité
|
||||
- Combinables = puissance maximale
|
||||
- Sauvegarde = gain temps utilisateurs réguliers
|
||||
|
||||
---
|
||||
|
||||
#### 15.3.4 Page de résultats
|
||||
|
||||
**Décision** : Liste avec previews enrichies
|
||||
|
||||
**Layout résultats** :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 🔍 "voyage paris" │
|
||||
│ 42 résultats · Tri : Pertinence ▼ │
|
||||
│ [Filtres] [Carte] │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ [Cover ] Balade à Paris │ │
|
||||
│ │ [16:9 ] @paris_stories ✓ │ │
|
||||
│ │ [Image ] 12 min · 🎧 2.3K │ │
|
||||
│ │ 📍 Paris 5e · Ancré │ │
|
||||
│ │ 🏷️ #Voyage #Histoire │ │
|
||||
│ │ [▶️ Écouter] [⋮] │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ [Cover ] Secrets Montmartre │ │
|
||||
│ │ [16:9 ] @explore_paris │ │
|
||||
│ │ [Image ] 8 min · 🎧 5.1K │ │
|
||||
│ │ 📍 Paris 18e · Guide │ │
|
||||
│ │ 🏷️ #Voyage #Art │ │
|
||||
│ │ [▶️ Écouter] [⋮] │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Charger plus] (20 suivants) │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Informations par résultat** :
|
||||
|
||||
| Élément | Affichage |
|
||||
|---------|-----------|
|
||||
| **Cover image** | 16:9, 120×68 px, lazy loading |
|
||||
| **Titre** | Tronqué 2 lignes max |
|
||||
| **Créateur** | @pseudo + badge ✓ si vérifié, cliquable → profil |
|
||||
| **Durée** | Format : "3 min" / "12 min" / "1h 24 min" |
|
||||
| **Écoutes** | Arrondi : "2.3K" / "54K" / "1.2M" |
|
||||
| **Localisation** | Ville + type géo (Ancré/Contextuel/Neutre) |
|
||||
| **Tags** | Maximum 3 premiers tags |
|
||||
| **Badge Premium** | 👑 si contenu premium |
|
||||
| **Distance** | Si recherche géo : "À 2.3 km" |
|
||||
|
||||
**Actions contextuelles [⋮]** :
|
||||
- Partager
|
||||
- Ajouter à une playlist (future feature)
|
||||
- Télécharger (offline)
|
||||
- Signaler
|
||||
|
||||
**Pagination** :
|
||||
- **20 résultats** par page
|
||||
- Infinite scroll (charger automatiquement si scroll >80%)
|
||||
- Bouton "Charger 20 suivants" en bas (fallback si scroll auto désactivé)
|
||||
|
||||
**Vue carte (alternative)** :
|
||||
- Bouton toggle "Liste / Carte"
|
||||
- Map Leaflet (OpenStreetMap)
|
||||
- Markers cliquables → popup avec preview
|
||||
- Clustering si >50 résultats proches
|
||||
|
||||
**Coût** : 0€ (Leaflet open source + OSM tiles gratuit)
|
||||
|
||||
**Justification** :
|
||||
- Équilibre information / compacité
|
||||
- Lazy loading = performances
|
||||
- Infinite scroll = UX moderne
|
||||
|
||||
---
|
||||
|
||||
## Récapitulatif Section 15
|
||||
|
||||
| Point | Décision | Coût | Complexité |
|
||||
|-------|----------|------|------------|
|
||||
| **15.1.1** Bouton partager | Disponible partout (⬆️), menu natif OS | 0€ | Faible |
|
||||
| **15.1.2** Lien partagé | Web player + deep link + Open Graph SEO | 0€ | Moyenne |
|
||||
| **15.1.3** Premium partagé | Preview 30s + paywall overlay | 0€ | Faible |
|
||||
| **15.2.1** Page profil | Profil public complet (stats + bio + contenus + tri) | 0€ | Faible |
|
||||
| **15.2.2** Stats publiques | Arrondies (abonnés, écoutes, durée totale) | 0€ | Faible |
|
||||
| **15.2.3** Badge vérifié | ✓ si KYC/célébrité/>10K abonnés | 0€ | Faible |
|
||||
| **15.3.1** Recherche texte | PostgreSQL full-text french + stemming | 0€ | Moyenne |
|
||||
| **15.3.2** Recherche géo | Lieu + rayon (Nominatim OSM) | 0-50€/mois | Moyenne |
|
||||
| **15.3.3** Filtres | 7 catégories combinables + sauvegarde recherches | 0€ | Moyenne |
|
||||
| **15.3.4** Page résultats | Liste enrichie + vue carte Leaflet + infinite scroll | 0€ | Moyenne |
|
||||
|
||||
**Coût total MVP : 0-50€/mois** (Nominatim self-hosted optionnel)
|
||||
|
||||
---
|
||||
|
||||
## Points d'attention pour Gherkin
|
||||
|
||||
- Tester partage contenu public vs Premium (preview 30s)
|
||||
- Tester deep linking iOS/Android (ouverture app si installée)
|
||||
- Tester Open Graph (aperçu correct sur WhatsApp, Twitter, Facebook)
|
||||
- Tester profil public (stats arrondies, badge vérifié)
|
||||
- Tester recherche full-text français (stemming, accents)
|
||||
- Tester recherche géo + rayon (PostGIS distance)
|
||||
- Tester combinaison filtres multiples (AND logic)
|
||||
- Tester sauvegarde recherches (max 5)
|
||||
- Tester pagination infinite scroll + fallback bouton
|
||||
- Tester vue carte Leaflet (clustering, markers cliquables)
|
||||
687
docs/regles-metier/ANNEXE-POST-MVP.md
Normal file
687
docs/regles-metier/ANNEXE-POST-MVP.md
Normal file
@@ -0,0 +1,687 @@
|
||||
# Annexe : Fonctionnalités reportées Post-MVP
|
||||
|
||||
**Date** : 2026-01-19
|
||||
**Statut** : Fonctionnalités validées mais reportées après le MVP
|
||||
|
||||
---
|
||||
|
||||
## Sommaire
|
||||
|
||||
1. [Classification politique et équilibre éditorial](#1-classification-politique-et-équilibre-éditorial)
|
||||
2. [Système de pourboires créateurs](#2-système-de-pourboires-créateurs)
|
||||
|
||||
---
|
||||
|
||||
## 1. Classification politique et équilibre éditorial
|
||||
|
||||
> ⚠️ **Reporté post-MVP** pour raisons de coût, complexité et risques juridiques.
|
||||
|
||||
### Contexte du report
|
||||
|
||||
**Raisons** :
|
||||
- **Coût modération** : Classification manuelle humaine très coûteuse (~2000€/mois pour 1-2 modérateurs senior full-time)
|
||||
- **Risque juridique** : Accusations de biais éditorial, contentieux DSA
|
||||
- **Complexité technique** : Dashboard audit, logs 3 ans, alertes déséquilibre
|
||||
- **Controverse** : Peut créer polémique dès le lancement
|
||||
- **Pas essentiel MVP** : L'application fonctionne sans ce système
|
||||
|
||||
**Version MVP** (actuelle) :
|
||||
- Tag "Politique" simple (comme "Économie", "Sport")
|
||||
- Pas de classification gauche/droite
|
||||
- Pas d'équilibrage imposé
|
||||
- Option utilisateur "Masquer politique" → 0% contenus politiques
|
||||
|
||||
---
|
||||
|
||||
### Spécifications complètes (future implémentation)
|
||||
|
||||
**Échelle de classification** (5 niveaux) :
|
||||
- 🔴 **Extrême gauche** (anticapitalisme radical, révolution)
|
||||
- 🟠 **Gauche** (écologie, social, critique capitalisme modérée)
|
||||
- ⚪ **Centre/Neutre** (pas de positionnement politique clair)
|
||||
- 🔵 **Droite** (sécurité, tradition, économie libérale)
|
||||
- 🟣 **Extrême droite** (nationalisme radical, conservatisme extrême)
|
||||
- 🟢 **Non politique** (enfants, musique, fiction, culture générale)
|
||||
|
||||
**Qui classifie** :
|
||||
- ❌ Pas de classification automatique IA (outil informatif uniquement, jamais décisionnaire)
|
||||
- ✅ Modérateurs senior après transcription
|
||||
- ✅ Créateur peut contester via processus d'appel
|
||||
|
||||
**Affichage** :
|
||||
- Badge politique visible : **au choix de l'utilisateur** (paramètre "Afficher orientation politique")
|
||||
- Par défaut : badges masqués (UX neutre)
|
||||
|
||||
**Règles de diffusion (équilibre imposé)** :
|
||||
|
||||
| Préférence utilisateur | Répartition | Justification |
|
||||
|------------------------|-------------|---------------|
|
||||
| **Équilibré** (défaut) | 35% gauche / 35% droite / 30% centre-neutre | Neutralité plateforme |
|
||||
| **Plutôt gauche** | 50% gauche / 20% droite / 30% centre-neutre | Préférence respectée avec minimum opposition |
|
||||
| **Plutôt droite** | 50% droite / 20% gauche / 30% centre-neutre | Préférence respectée avec minimum opposition |
|
||||
| **Masquer politique** | 0% gauche / 0% droite / 100% centre-neutre + non politique | Option apolitique |
|
||||
|
||||
**Audit et conformité DSA** :
|
||||
- Rapport hebdomadaire automatique : % gauche/droite/centre diffusé par utilisateur
|
||||
- Alerte si déséquilibre global plateforme (>55% d'un bord)
|
||||
- Logs conservés **3 ans** (exigence Digital Services Act EU)
|
||||
- Dashboard admin : visualisation répartition temps réel
|
||||
|
||||
**Sanctions mauvaise classification** :
|
||||
- Classification volontairement incorrecte = Strike 1
|
||||
- Récidive = Strike 2 (suspension 7j)
|
||||
- Détection via signalements utilisateurs + audit modération
|
||||
|
||||
**Justification** :
|
||||
- **Conformité juridique DSA** (obligation neutralité plateforme EU)
|
||||
- Protection contre accusations de biais éditorial
|
||||
- Transparence auditable
|
||||
- Coût : temps modération humaine (incompressible)
|
||||
|
||||
---
|
||||
|
||||
### Conditions de réintégration
|
||||
|
||||
**Prérequis** :
|
||||
1. Base utilisateurs stable et revenus suffisants pour financer modération
|
||||
2. Équipe modération dédiée (2+ modérateurs senior formés)
|
||||
3. Dashboard admin audit DSA opérationnel
|
||||
4. Système de logs et archivage 3 ans en place
|
||||
5. Validation juridique du processus de classification
|
||||
|
||||
**Chronologie estimée** :
|
||||
- Phase 1 (Post-MVP+3 mois) : Validation demande utilisateurs via sondages
|
||||
- Phase 2 (Post-MVP+6 mois) : Recrutement modérateurs + développement dashboard
|
||||
- Phase 3 (Post-MVP+9 mois) : Tests bêta avec utilisateurs volontaires
|
||||
- Phase 4 (Post-MVP+12 mois) : Déploiement progressif si résultats positifs
|
||||
|
||||
---
|
||||
|
||||
## 2. Système de pourboires créateurs
|
||||
|
||||
> ⚠️ **Reporté post-MVP** - Fonctionnalité crypto (Lightning Network) prévue ultérieurement.
|
||||
|
||||
### Contexte du report
|
||||
|
||||
**Raisons** :
|
||||
- **Complexité technique** : Intégration Lightning Network, gestion wallets crypto
|
||||
- **Réglementation** : Incertitude juridique crypto en EU (MiCA 2025)
|
||||
- **Focus MVP** : Priorité sur monétisation via abonnements Premium et publicités
|
||||
- **Adoption utilisateurs** : Nécessite éducation et adoption crypto préalables
|
||||
|
||||
**Version MVP** (actuelle) :
|
||||
- Monétisation créateurs via :
|
||||
- Partage revenus publicités (3€ CPM)
|
||||
- 70% revenus abonnements Premium
|
||||
|
||||
---
|
||||
|
||||
### Spécifications complètes (future implémentation)
|
||||
|
||||
**Système prévu** : Micro-dons via Lightning Network (Bitcoin Layer 2)
|
||||
|
||||
**Fonctionnement** :
|
||||
1. Auditeur peut envoyer pourboire pendant ou après écoute
|
||||
2. Montants suggérés : 0.10€, 0.50€, 1€, 5€ (personnalisable)
|
||||
3. Transaction instantanée via Lightning Network (frais <0.01€)
|
||||
4. Créateur reçoit directement dans wallet Lightning
|
||||
5. Conversion EUR/BTC automatique (optionnelle)
|
||||
|
||||
**Avantages Lightning Network** :
|
||||
- ✅ Frais quasi-nuls (<1%) vs 1.8% Mangopay
|
||||
- ✅ Transactions instantanées (<1 seconde)
|
||||
- ✅ Micropaiements possibles (dès 0.01€)
|
||||
- ✅ International sans frais supplémentaires
|
||||
- ✅ Pas d'intermédiaire (peer-to-peer)
|
||||
|
||||
**Contraintes** :
|
||||
- ❌ Adoption crypto limitée (2-5% population EU en 2026)
|
||||
- ❌ Volatilité BTC (nécessite conversion EUR immédiate)
|
||||
- ❌ UX complexe pour utilisateurs non-crypto
|
||||
- ❌ Réglementation MiCA en évolution
|
||||
|
||||
**Alternatives étudiées** :
|
||||
- Ko-fi / Buy Me a Coffee : simple mais frais 5%
|
||||
- PayPal/Stripe : frais 2.9% + 0.30€ (non viable pour micropaiements)
|
||||
- Mangopay : déjà utilisé, mais frais élevés pour petits montants
|
||||
|
||||
---
|
||||
|
||||
### Conditions de réintégration
|
||||
|
||||
**Prérequis** :
|
||||
1. Réglementation MiCA stabilisée et conforme
|
||||
2. Adoption crypto suffisante dans la base utilisateurs (>10%)
|
||||
3. Intégration Lightning Network validée techniquement
|
||||
4. UX simplifiée pour utilisateurs non-crypto (onboarding dédié)
|
||||
5. Demande créateurs confirmée via sondages
|
||||
|
||||
**Chronologie estimée** :
|
||||
- Phase 1 (Post-MVP+6 mois) : Étude de marché et demande utilisateurs
|
||||
- Phase 2 (Post-MVP+12 mois) : Développement intégration Lightning
|
||||
- Phase 3 (Post-MVP+15 mois) : Tests bêta avec créateurs volontaires
|
||||
- Phase 4 (Post-MVP+18 mois) : Déploiement public si résultats positifs
|
||||
|
||||
---
|
||||
|
||||
## 3. Roulette de connexion live aléatoire
|
||||
|
||||
> ⚠️ **Reporté post-MVP** - Feature sociale avancée nécessitant masse critique d'utilisateurs et infrastructure WebRTC renforcée.
|
||||
|
||||
### Contexte du report
|
||||
|
||||
**Raisons** :
|
||||
- **Masse critique requise** : Nécessite pool suffisant d'utilisateurs simultanés (>500) pour matching rapide (<30s)
|
||||
- **Infrastructure WebRTC** : Coût serveurs TURN/STUN supplémentaire (~500€/mois pour 1000 utilisateurs actifs)
|
||||
- **Complexité modération** : Contenu live non enregistré = risques abus, nécessite système de confiance et signalement robuste
|
||||
- **Focus MVP** : Priorité sur le contenu asynchrone (radios, POIs audio) avant le live P2P
|
||||
- **UX conducteur** : Commandes vocales avancées nécessaires pour sécurité routière
|
||||
|
||||
**Version MVP** (actuelle) :
|
||||
- Radio live créateurs uniquement (1 vers N)
|
||||
- Pas de connexion P2P entre auditeurs
|
||||
- Chat textuel limité aux POIs et commentaires
|
||||
|
||||
---
|
||||
|
||||
### Spécifications complètes (future implémentation)
|
||||
|
||||
**Concept** : Permettre aux utilisateurs (conducteurs ou piétons) de se connecter aléatoirement en live audio avec d'autres utilisateurs pour des conversations spontanées de découverte.
|
||||
|
||||
**Fonctionnement** :
|
||||
|
||||
1. **Matching algorithmique** :
|
||||
- Pool unifié conducteurs + piétons (pas de séparation)
|
||||
- Matching pondéré : 70% centres d'intérêt communs + 30% aléatoire
|
||||
- Proximité géographique : préférence régionale (même région/département)
|
||||
- Temps d'attente cible : <30 secondes
|
||||
|
||||
2. **Format session** :
|
||||
- Durée initiale : 5 minutes
|
||||
- Prolongation par consentement mutuel (5 min supplémentaires, illimitée)
|
||||
- Skip limité : 3 skips/heure pour éviter abus
|
||||
- Audio uniquement (pas de vidéo, pas de texte pendant session)
|
||||
|
||||
3. **UX différenciée** :
|
||||
- **Conducteur** : Commandes vocales uniquement ("Roulette", "Suivant", "Terminer")
|
||||
- **Piéton** : Bouton dédié "Roulette" dans interface principale
|
||||
- Badges contextuels : "🚗 En voiture" / "🚶 À pied" visibles dès connexion
|
||||
- Message pré-session : "Vous parlez avec un conducteur. Soyez concis."
|
||||
|
||||
4. **Sécurité et modération** :
|
||||
- Score de confiance minimum requis : 50/100 (nouveaux utilisateurs exclus)
|
||||
- Enregistrement tampon 5 min glissantes (sauvegardé uniquement si signalement)
|
||||
- Signalement immédiat pendant session → déconnexion + revue modération
|
||||
- Sanctions progressives : avertissement → suspension 1 semaine → ban définitif
|
||||
|
||||
5. **Transition post-session** :
|
||||
- Option s'abonner mutuellement après bonne conversation
|
||||
- Statistiques personnelles : nombre sessions, durée totale, rencontres
|
||||
- Badge "En roulette" visible sur profil (transparence)
|
||||
|
||||
**Avantages** :
|
||||
- ✅ Sérendipité et découverte (esprit "Chatroulette audio")
|
||||
- ✅ Complémentarité conducteur/piéton (récit route vs récit urbain)
|
||||
- ✅ Fidèle concept RoadWave (usagers de la route connectés)
|
||||
- ✅ Réutilisation infrastructure WebRTC existante (radio live)
|
||||
|
||||
**Contraintes** :
|
||||
- ❌ Nécessite pool minimum 500 utilisateurs actifs simultanés
|
||||
- ❌ Modération temps réel complexe (contenu éphémère)
|
||||
- ❌ Coût infrastructure TURN/STUN significatif
|
||||
- ❌ Risque dérive (trolls, contenu inapproprié)
|
||||
- ❌ Commandes vocales avancées requises pour conducteurs
|
||||
|
||||
**Monétisation** :
|
||||
- Gratuit avec limitation : 3 sessions/jour de 5 min
|
||||
- Premium : sessions illimitées + matching prioritaire (moins d'attente)
|
||||
|
||||
**Aspects légaux** :
|
||||
- Âge minimum : 18 ans pour accès roulette
|
||||
- Charte d'utilisation spécifique (respect, pas de contenu sexuel/violent, pas de sollicitation commerciale)
|
||||
- Anonymat relatif : pseudo + ville visible, pas de photo
|
||||
|
||||
---
|
||||
|
||||
### Conditions de réintégration
|
||||
|
||||
**Prérequis** :
|
||||
1. Base utilisateurs active : >10 000 MAU (Monthly Active Users) dont >500 utilisateurs simultanés en heures pleines
|
||||
2. Infrastructure WebRTC stable : serveurs TURN/STUN dimensionnés, latence <500ms
|
||||
3. Système de confiance opérationnel : score utilisateur basé sur comportement, signalements
|
||||
4. Équipe modération : capacité traiter signalements en <2h
|
||||
5. Budget infrastructure : 500-1000€/mois selon volume
|
||||
6. Commandes vocales avancées implémentées pour conducteurs
|
||||
|
||||
**Chronologie estimée** :
|
||||
- Phase 1 (Post-MVP+3 mois) : Validation demande utilisateurs via sondages, analyse concurrence (Clubhouse, Twitter Spaces)
|
||||
- Phase 2 (Post-MVP+6 mois) : Développement matchmaking + WebRTC P2P renforcé
|
||||
- Phase 3 (Post-MVP+9 mois) : Tests bêta avec 100 utilisateurs volontaires
|
||||
- Phase 4 (Post-MVP+12 mois) : Déploiement progressif si KPI positifs (>70% satisfaction, <5% signalements)
|
||||
|
||||
**KPI de succès** :
|
||||
- Temps moyen d'attente matching : <30 secondes
|
||||
- Taux satisfaction post-session : >70%
|
||||
- Taux signalement : <5%
|
||||
- Durée moyenne session : >5 minutes (signe d'engagement)
|
||||
- Taux conversion abonnements mutuels : >10%
|
||||
|
||||
---
|
||||
|
||||
## 4. Vérification SMS anti-spam
|
||||
|
||||
> ⚠️ **Reporté post-MVP** - Ajout d'une vérification par SMS pour éviter les comptes créés avec des emails temporaires.
|
||||
|
||||
### Contexte du report
|
||||
|
||||
**Raisons** :
|
||||
- **Coût SMS** : ~0.04€/SMS en France via Brevo (400€/mois pour 10K inscriptions)
|
||||
- **Complexité UX** : Étape supplémentaire à l'inscription (friction)
|
||||
- **Focus MVP** : Priorité sur l'expérience utilisateur fluide
|
||||
- **Modération suffisante** : Système de strikes et signalements couvre les cas d'abus initiaux
|
||||
|
||||
**Version MVP** (actuelle) :
|
||||
- Inscription par email uniquement (via Zitadel)
|
||||
- Confirmation email obligatoire
|
||||
- Détection basique emails jetables (liste noire publique)
|
||||
- Modération réactive via signalements
|
||||
|
||||
---
|
||||
|
||||
### Spécifications complètes (future implémentation)
|
||||
|
||||
**Problématique** : Comptes créés avec emails temporaires (Yopmail, 10minutemail, etc.) pour contourner bans ou spammer du contenu.
|
||||
|
||||
**Solution** : Vérification numéro mobile par SMS lors de l'inscription.
|
||||
|
||||
**Fonctionnement** :
|
||||
|
||||
1. **Inscription initiale** :
|
||||
- Utilisateur crée compte avec email (Zitadel)
|
||||
- Email de confirmation envoyé (standard)
|
||||
|
||||
2. **Détection email suspect** :
|
||||
- Regex patterns emails temporaires (`.disposable.com`, `tempmail`, etc.)
|
||||
- API externe (kickbox.io, mailcheck.ai) pour validation domaine
|
||||
- Score confiance email < 50% → vérification SMS obligatoire
|
||||
|
||||
3. **Vérification SMS** :
|
||||
- Demande numéro mobile (+33, +32, etc.)
|
||||
- Envoi code 6 chiffres via Brevo SMS API
|
||||
- Expiration : 10 minutes
|
||||
- Maximum 3 tentatives/jour/numéro (anti-abus)
|
||||
|
||||
4. **Validation** :
|
||||
- Code correct → compte activé, badge "Vérifié ✓"
|
||||
- Code incorrect (3 fois) → blocage temporaire 24h
|
||||
|
||||
5. **Sanctions doublon** :
|
||||
- Détection numéro déjà utilisé pour autre compte
|
||||
- Limite : 3 comptes/numéro maximum
|
||||
- Au-delà → signalement automatique modération
|
||||
|
||||
**Affichage** :
|
||||
- Badge "Vérifié ✓" visible sur profil créateur
|
||||
- Non obligatoire pour auditeurs simples (seulement créateurs)
|
||||
- Option "Vérifier mon compte" dans paramètres
|
||||
|
||||
**Règles de diffusion** :
|
||||
- Contenus créateurs non-vérifiés : portée limitée à 10 km pendant 30 premiers jours
|
||||
- Après 30 jours sans signalement : levée restriction
|
||||
- Créateurs vérifiés : aucune restriction
|
||||
|
||||
**Avantages** :
|
||||
- ✅ Réduction spam et comptes multiples
|
||||
- ✅ Amélioration confiance plateforme
|
||||
- ✅ Conformité anti-fraude (KYC léger)
|
||||
- ✅ Réutilisation infrastructure Brevo (emails + SMS)
|
||||
|
||||
**Contraintes** :
|
||||
- ❌ Coût SMS : ~400€/mois pour 10K inscriptions/mois
|
||||
- ❌ Friction UX (étape supplémentaire)
|
||||
- ❌ Numéros virtuels (Twilio, etc.) contournent vérification
|
||||
- ❌ Certains utilisateurs réticents (vie privée)
|
||||
|
||||
**Alternatives étudiées** :
|
||||
- **Captcha reCAPTCHA v3** : efficace mais contournable, pas de coût
|
||||
- **Email reputation API** : ~0.01€/vérification (kickbox.io)
|
||||
- **Vérification bancaire** : trop contraignant pour MVP
|
||||
|
||||
---
|
||||
|
||||
### Conditions de réintégration
|
||||
|
||||
**Prérequis** :
|
||||
1. Base utilisateurs >10K avec taux spam/abus significatif (>5% comptes signalés)
|
||||
2. Budget SMS disponible (~400-800€/mois selon volume)
|
||||
3. Intégration Brevo SMS API opérationnelle
|
||||
4. UX optimisée (onboarding fluide)
|
||||
5. Conformité RGPD : consentement stockage numéro mobile
|
||||
|
||||
**Chronologie estimée** :
|
||||
- Phase 1 (Post-MVP+3 mois) : Analyse taux spam/abus, validation besoin
|
||||
- Phase 2 (Post-MVP+4 mois) : Développement détection emails temporaires + API Brevo SMS
|
||||
- Phase 3 (Post-MVP+5 mois) : Tests bêta avec créateurs volontaires
|
||||
- Phase 4 (Post-MVP+6 mois) : Déploiement progressif selon catégorie utilisateur (créateurs en priorité)
|
||||
|
||||
**KPI de succès** :
|
||||
- Réduction comptes spam : >50%
|
||||
- Taux vérification volontaire (créateurs) : >70%
|
||||
- Friction UX acceptable : taux abandon inscription <10%
|
||||
- Coût SMS : <2% revenus utilisateurs vérifiés
|
||||
|
||||
---
|
||||
|
||||
## 5. Synthèse vocale de documents (Text-to-Speech)
|
||||
|
||||
> ⚠️ **Reporté post-MVP** - Fonctionnalité Premium permettant aux utilisateurs d'écouter des documents (PDF, articles web, ebooks) convertis en audio.
|
||||
|
||||
### Contexte du report
|
||||
|
||||
**Raisons** :
|
||||
- **Complexité technique** : Intégration API TTS (Text-to-Speech), OCR pour PDF scannés, parsing multi-formats
|
||||
- **Coût infrastructure** : ~0.016€/1000 caractères (Google Cloud TTS) = ~1.60€ par livre moyen (100K caractères)
|
||||
- **Conformité droits d'auteur** : Risque juridique si conversion de contenus protégés sans licence
|
||||
- **Focus MVP** : Priorité sur contenu audio natif géolocalisé (podcasts, audio-guides, radios live)
|
||||
- **Usage limité** : Cas d'usage minoritaire vs contenu audio créé par la communauté
|
||||
|
||||
**Version MVP** (actuelle) :
|
||||
- Contenu audio uniquement créé par les créateurs
|
||||
- Pas de conversion automatique document → audio
|
||||
- Utilisateurs doivent uploader directement fichiers audio
|
||||
|
||||
---
|
||||
|
||||
### Spécifications complètes (future implémentation)
|
||||
|
||||
**Problématique** : Utilisateurs Premium veulent écouter des documents (articles, PDF, ebooks) pendant leurs trajets sans les lire.
|
||||
|
||||
**Solution** : Conversion Text-to-Speech (TTS) de documents en audio avec voix neurale haute qualité.
|
||||
|
||||
**Fonctionnement** :
|
||||
|
||||
1. **Upload document** :
|
||||
- Formats supportés : PDF, EPUB, TXT, DOCX, URLs articles web
|
||||
- Taille max : 50 MB par fichier
|
||||
- Détection automatique langue (FR, EN, ES, DE, IT)
|
||||
- OCR automatique si PDF scanné (Tesseract ou Google Vision API)
|
||||
|
||||
2. **Traitement et conversion** :
|
||||
- Nettoyage texte (enlever headers/footers, numéros page, notes de bas de page)
|
||||
- Détection structure (chapitres, sections) pour navigation
|
||||
- Génération audio via TTS (Google Cloud TTS ou AWS Polly)
|
||||
- Voix neurale professionnelle (qualité proche voix humaine)
|
||||
- Génération chapitres audio distincts (navigation facilitée)
|
||||
|
||||
3. **Stockage et synchronisation** :
|
||||
- Audio généré stocké sur OVH Object Storage (comme autres contenus)
|
||||
- Ajouté automatiquement à la bibliothèque utilisateur
|
||||
- Synchronisation multi-device (reprendre écoute où elle s'est arrêtée)
|
||||
- Conservation : 90 jours après génération, puis suppression automatique
|
||||
|
||||
4. **Lecture en voiture** :
|
||||
- Navigation par chapitres (suivant/précédent)
|
||||
- Vitesse de lecture ajustable (0.75x, 1x, 1.25x, 1.5x, 2x)
|
||||
- Signets audio (marquer un passage pour y revenir)
|
||||
- Reprise automatique au dernier point d'écoute
|
||||
|
||||
5. **Limitations et quotas** :
|
||||
- **Premium uniquement** (pas disponible en gratuit)
|
||||
- Quota mensuel : 10 documents ou 500K caractères/mois (soit ~5 livres moyens)
|
||||
- File d'attente : traitement sous 5-10 minutes selon longueur
|
||||
- Conservation temporaire (90 jours) pour limiter coûts stockage
|
||||
|
||||
**Formats supportés** :
|
||||
|
||||
| Format | Support | Limitations |
|
||||
|--------|---------|-------------|
|
||||
| **PDF texte** | ✅ Natif | Max 50 MB, extraction texte directe |
|
||||
| **PDF scanné** | ✅ OCR | Max 50 MB, nécessite OCR (plus lent) |
|
||||
| **EPUB** | ✅ Natif | Ebooks sans DRM uniquement |
|
||||
| **TXT** | ✅ Natif | UTF-8, max 10 MB |
|
||||
| **DOCX** | ✅ Natif | Max 20 MB |
|
||||
| **URLs web** | ✅ Parsing | Articles uniquement (pas de paywall) |
|
||||
|
||||
**Voix TTS disponibles** :
|
||||
|
||||
| Langue | Voix | Fournisseur |
|
||||
|--------|------|-------------|
|
||||
| **Français** | Léa (féminine), Thomas (masculine) | Google Cloud TTS WaveNet |
|
||||
| **Anglais** | Emily, James | Google Cloud TTS WaveNet |
|
||||
| **Espagnol** | Carmen, Diego | Google Cloud TTS WaveNet |
|
||||
| **Allemand** | Anna, Max | Google Cloud TTS WaveNet |
|
||||
|
||||
**Avantages** :
|
||||
- ✅ Différenciation Premium forte (feature exclusive)
|
||||
- ✅ Fidélisation utilisateurs (consommation contenu personnel)
|
||||
- ✅ Réutilisation infrastructure audio existante (HLS, NGINX Cache)
|
||||
- ✅ Cas d'usage trajets longs (livres, articles longs)
|
||||
|
||||
**Contraintes** :
|
||||
- ❌ Coût TTS : ~1.60€/livre moyen (Google Cloud TTS WaveNet)
|
||||
- ❌ Coût stockage : ~0.01€/GB/mois (temporaire 90 jours)
|
||||
- ❌ Risque juridique : conversion contenus protégés (livres, articles premium)
|
||||
- ❌ Qualité variable selon format source (PDF mal structurés)
|
||||
- ❌ Pas de géolocalisation (contenu personnel, pas communautaire)
|
||||
|
||||
**Conformité droits d'auteur** :
|
||||
|
||||
| Contenu | Autorisé | Restrictions |
|
||||
|---------|----------|--------------|
|
||||
| **Documents personnels** | ✅ Oui | Aucun problème légal |
|
||||
| **Articles web publics** | ✅ Oui | Fair use personnel uniquement |
|
||||
| **Ebooks DRM-free** | ✅ Oui | Usage privé uniquement (pas de partage) |
|
||||
| **Ebooks DRM** | ❌ Non | Violation DRM interdite |
|
||||
| **Livres sous copyright** | ⚠️ Tolérance | Usage strictement privé, pas de redistribution |
|
||||
| **Articles paywall** | ❌ Non | Bypass paywall interdit |
|
||||
|
||||
**Disclaimer utilisateur** :
|
||||
> "La conversion de documents en audio est réservée à un usage privé uniquement. Vous êtes responsable de vous assurer que vous possédez les droits nécessaires pour convertir et écouter ce contenu. RoadWave ne peut être tenu responsable de toute violation de droits d'auteur."
|
||||
|
||||
**Alternatives étudiées** :
|
||||
|
||||
| Solution | Coût | Qualité voix | Contraintes |
|
||||
|----------|------|--------------|-------------|
|
||||
| **Google Cloud TTS WaveNet** | 0.016€/1K caractères | ⭐⭐⭐⭐⭐ Excellente | API stable, voix neurales |
|
||||
| **AWS Polly Neural** | 0.016€/1K caractères | ⭐⭐⭐⭐⭐ Excellente | Similar à Google |
|
||||
| **Azure Cognitive Services** | 0.014€/1K caractères | ⭐⭐⭐⭐ Très bonne | Moins cher, voix correctes |
|
||||
| **Elevenlabs** | 0.30€/1K caractères | ⭐⭐⭐⭐⭐ Ultra-réaliste | Trop cher pour MVP |
|
||||
| **OpenAI TTS** | 0.015€/1K caractères | ⭐⭐⭐⭐ Très bonne | Nouveau (2024), à tester |
|
||||
|
||||
**Recommandation** : Google Cloud TTS WaveNet (équilibre coût/qualité, voix neurales professionnelles).
|
||||
|
||||
---
|
||||
|
||||
### Conditions de réintégration
|
||||
|
||||
**Prérequis** :
|
||||
1. Base utilisateurs Premium >1000 abonnés (justifier développement feature)
|
||||
2. Demande utilisateurs confirmée via sondages (>40% intérêt)
|
||||
3. Budget TTS + stockage disponible (~500-1000€/mois selon volume)
|
||||
4. Validation juridique : conformité droits d'auteur, disclaimer clair
|
||||
5. Infrastructure existante stable (HLS, CDN, backend Go)
|
||||
|
||||
**Chronologie estimée** :
|
||||
- Phase 1 (Post-MVP+6 mois) : Étude de marché, sondage utilisateurs Premium, validation juridique
|
||||
- Phase 2 (Post-MVP+9 mois) : Développement MVP TTS (PDF texte uniquement, FR/EN)
|
||||
- Phase 3 (Post-MVP+10 mois) : Tests bêta avec 100 utilisateurs Premium volontaires
|
||||
- Phase 4 (Post-MVP+12 mois) : Déploiement progressif si KPI positifs + ajout formats (EPUB, OCR)
|
||||
|
||||
**KPI de succès** :
|
||||
- Adoption feature : >30% utilisateurs Premium l'utilisent au moins 1 fois/mois
|
||||
- Satisfaction : >75% note positive (4-5/5)
|
||||
- Rétention Premium : augmentation >10% grâce à cette feature
|
||||
- Coût TTS : <5% revenus Premium
|
||||
- Taux d'erreur conversion : <5% (PDF mal structurés, OCR raté)
|
||||
|
||||
**Budget estimé** :
|
||||
|
||||
| Composant | Coût mensuel (1000 utilisateurs Premium actifs) |
|
||||
|-----------|--------------------------------------------------|
|
||||
| **Google Cloud TTS** | ~500€ (10 documents/user/mois, 30K caractères/document) |
|
||||
| **OCR (PDF scannés)** | ~100€ (30% documents nécessitent OCR) |
|
||||
| **Stockage** | ~50€ (documents audio temporaires 90 jours) |
|
||||
| **Bande passante** | Inclus dans infrastructure existante |
|
||||
| **Total** | **~650€/mois** |
|
||||
|
||||
**Rentabilité** :
|
||||
- Revenus Premium 1000 users : 4990€/mois (4.99€/mois × 1000)
|
||||
- Coût TTS : 650€/mois (13% revenus)
|
||||
- Marge après TTS : 4340€/mois (87%)
|
||||
- **Rentable si** adoption >30% et rétention +10% (soit +100 abonnés = +499€/mois)
|
||||
|
||||
---
|
||||
|
||||
## 6. Commandes vocales (CarPlay / Android Auto)
|
||||
|
||||
> ⚠️ **Reporté post-MVP** - Permettre aux conducteurs d'utiliser les actions complémentaires via assistants vocaux.
|
||||
|
||||
### Contexte du report
|
||||
|
||||
**Raisons** :
|
||||
- **Couverture limitée** : ~30-40% du parc automobile EU en 2026 (CarPlay/Android Auto)
|
||||
- **Complexité technique** : Intégration Siri Intents (iOS) + Google Actions (Android)
|
||||
- **Modération vocale** : Signalements vocaux nécessitent enregistrement + transcription audio
|
||||
- **Focus MVP** : Priorité sur like automatique et mode piéton avec actions manuelles
|
||||
- **Accessibilité secondaire** : Like automatique couvre déjà engagement conducteurs
|
||||
|
||||
**Version MVP** (actuelle) :
|
||||
- ❌ Pas de commandes vocales
|
||||
- ✅ Like automatique basé sur temps d'écoute (en voiture)
|
||||
- ✅ Actions manuelles disponibles seulement en mode piéton
|
||||
|
||||
---
|
||||
|
||||
### Spécifications complètes (future implémentation)
|
||||
|
||||
**Objectif** : Permettre conducteurs d'effectuer actions complémentaires via commandes vocales sans regarder écran.
|
||||
|
||||
**Commandes vocales supportées** :
|
||||
|
||||
**iOS (Apple Siri)** :
|
||||
```
|
||||
"Hey Siri, like ce contenu"
|
||||
→ Ajoute +2% jauge (like explicite)
|
||||
|
||||
"Hey Siri, abonne-moi à ce créateur"
|
||||
→ Ajoute +5% toutes jauges du créateur
|
||||
|
||||
"Hey Siri, signale ce contenu"
|
||||
→ Siri demande catégorie vocalement ("Spam", "Haine", etc.)
|
||||
|
||||
"Hey Siri, passe au contenu suivant"
|
||||
→ Même que bouton physique (déjà supporté)
|
||||
```
|
||||
|
||||
**Android (Google Assistant)** :
|
||||
```
|
||||
"OK Google, like ce podcast"
|
||||
→ Ajoute +2% jauge
|
||||
|
||||
"OK Google, abonne-moi au créateur"
|
||||
→ Ajoute +5% toutes jauges du créateur
|
||||
|
||||
"OK Google, signale ce contenu"
|
||||
→ Assistant demande catégorie vocalement
|
||||
|
||||
"OK Google, passe au suivant"
|
||||
→ Même que bouton physique
|
||||
```
|
||||
|
||||
**Implémentation technique** :
|
||||
|
||||
**iOS** :
|
||||
- Siri Intents (framework iOS 12+)
|
||||
- Clés Intent à ajouter dans `Info.plist` :
|
||||
```xml
|
||||
<key>INUserConfirmationConfiguration</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentClassName</key>
|
||||
<string>RoadWaveAddLikeIntent</string>
|
||||
</dict>
|
||||
</array>
|
||||
```
|
||||
- Paramètres vocaux : détection "like", "abonne", "signale"
|
||||
|
||||
**Android** :
|
||||
- Google Actions on Google Assistant (via assistant voice queries)
|
||||
- Intégration avec Android App Actions
|
||||
- Paremeters: Intent extras pour passer contenu actuel
|
||||
- Fallback : repérer contenu par titre + créateur
|
||||
|
||||
**Limitation importante** :
|
||||
- ⚠️ **CarPlay/Android Auto requis** : Fonctionalité non disponible sur interface mobile
|
||||
- ⚠️ **Reconnaissance vocale réseau** : Nécessite connexion data
|
||||
- ⚠️ **Latence acceptable** : <2 secondes entre commande et confirmation
|
||||
|
||||
**UX - Feedback utilisateur** :
|
||||
- Siri : "✓ J'ai ajouté ce contenu à vos favoris"
|
||||
- Google Assistant : "✓ Vous êtes maintenant abonné à [Créateur]"
|
||||
- Confirmation audio pour signalement : "Signalement envoyé. Catégorie : Spam"
|
||||
|
||||
**Signalements vocaux** :
|
||||
- Enregistrement automatique de la voix (tampon 30 secondes)
|
||||
- Transcription audio → texte (via Google Cloud Speech ou similaire)
|
||||
- Catégorie pré-remplie selon réponse vocale ("Spam" → catégorie Spam)
|
||||
- Commentaire optionnel supplémentaire (enregistrement audio conservé)
|
||||
|
||||
---
|
||||
|
||||
### Conditions de réintégration
|
||||
|
||||
**Prérequis** :
|
||||
1. MVP stabilis en production avec base utilisateurs
|
||||
2. Données télémétrie : >10K utilisateurs actifs CarPlay/Android Auto
|
||||
3. Intégration Siri Intents iOS et Google Actions validée
|
||||
4. Transcription vocale fiable (coût ~0.01€/minute)
|
||||
5. Système de confiance utilisateur en place (éviter abus signalements)
|
||||
|
||||
**Chronologie estimée** :
|
||||
- Phase 1 (Post-MVP+2 mois) : Validation demande utilisateurs (CarPlay/Android Auto)
|
||||
- Phase 2 (Post-MVP+4 mois) : Développement Siri Intents + Google Actions
|
||||
- Phase 3 (Post-MVP+6 mois) : Tests bêta avec conducteurs volontaires
|
||||
- Phase 4 (Post-MVP+8 mois) : Déploiement progressif si KPI positifs
|
||||
|
||||
**KPI de succès** :
|
||||
- Adoption commandes vocales : >30% utilisateurs CarPlay/Android Auto
|
||||
- Taux erreur reconnaissance vocale : <10%
|
||||
- Satisfaction utilisateurs : >75% (4-5/5)
|
||||
- Taux signalements abusifs : <2% (via détection anomalies)
|
||||
|
||||
**Budget estimé** :
|
||||
| Composant | Coût mensuel (10K utilisateurs actifs) |
|
||||
|-----------|----------------------------------------|
|
||||
| **Siri Intents** | Inclus iOS SDK |
|
||||
| **Google Actions** | Inclus Android SDK |
|
||||
| **Transcription vocale** | ~300€ (30K minutes/mois) |
|
||||
| **Modération signalements audio** | ~500€ (équipe part-time) |
|
||||
| **Total** | **~800€/mois** |
|
||||
|
||||
---
|
||||
|
||||
## Autres fonctionnalités candidates Post-MVP
|
||||
|
||||
Liste non exhaustive de fonctionnalités évoquées mais non encore spécifiées :
|
||||
|
||||
- **Mode offline avancé** : Téléchargement automatique zones fréquentes
|
||||
- **Playlists collaboratives** : Co-création de playlists géolocalisées
|
||||
- **API publique créateurs** : Intégration RSS, podcasts existants
|
||||
- **Gamification** : Badges, défis géolocalisés, leaderboards
|
||||
- **Mode nuit** : Interface sombre automatique
|
||||
- **Statistiques avancées créateurs** : Démographie, retention, heatmaps GPS
|
||||
|
||||
Ces fonctionnalités seront spécifiées et priorisées selon les retours utilisateurs MVP.
|
||||
|
||||
---
|
||||
|
||||
## Suivi et validation
|
||||
|
||||
**Responsable** : Product Owner
|
||||
**Révision** : Trimestrielle
|
||||
**Critères de priorisation** :
|
||||
1. Demande utilisateurs (votes, sondages)
|
||||
2. Impact business (revenus, rétention)
|
||||
3. Faisabilité technique (complexité, ressources)
|
||||
4. Conformité légale (RGPD, DSA, MiCA)
|
||||
5. Différenciation concurrentielle
|
||||
296
docs/regles-metier/README.md
Normal file
296
docs/regles-metier/README.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Règles métier RoadWave
|
||||
|
||||
> Documentation complète des règles métier validées pour l'application RoadWave.
|
||||
> Chaque section détaille les comportements, flux et décisions techniques.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Table des matières
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Fondations & Compte utilisateur
|
||||
|
||||
### [01. Authentification & Inscription](01-authentification-inscription.md)
|
||||
|
||||
**Contenu** : Inscription, connexion, récupération de compte
|
||||
|
||||
- Inscription : email/password uniquement (pas d'OAuth tiers)
|
||||
- Vérification email : optionnelle auditeurs (limite 5 contenus), obligatoire créateurs (lien expire 7j)
|
||||
- Connexion : 5 tentatives max, blocage 15 min, refresh token 30j
|
||||
- Récupération mot de passe : email, lien expire 1h
|
||||
|
||||
---
|
||||
|
||||
### [02. Conformité RGPD](02-conformite-rgpd.md)
|
||||
|
||||
**Contenu** : Consentements, anonymisation, export, suppression
|
||||
|
||||
- Consentement : Tarteaucitron.js + PostgreSQL versioning
|
||||
- GPS précis : 24h puis geohash 5 (~5km²)
|
||||
- Export : JSON + HTML + audio → ZIP, génération asynchrone sous 48h, expire 7j
|
||||
- Suppression : grace period 30j, contenus créés anonymisés (créateur = "Utilisateur supprimé")
|
||||
- Analytics : Matomo self-hosted, IP anonymisées, 0 cookie tiers
|
||||
- DPO : fondateur formé CNIL (non obligatoire <250 employés)
|
||||
|
||||
---
|
||||
|
||||
## 🎧 Consommation de contenu (Auditeur)
|
||||
|
||||
### [03. Centres d'intérêt et jauges](03-centres-interet-jauges.md)
|
||||
|
||||
**Contenu** : Évolution jauges, valeurs initiales
|
||||
|
||||
- Like automatique : écoute ≥80% → +2%, écoute 30-79% → +1%
|
||||
- Like explicite (manuel) : +2% (cumulable avec auto)
|
||||
- Abonnement : +5%
|
||||
- Skip rapide (<10s) : -0.5%
|
||||
- Valeur initiale : 50% (neutre)
|
||||
- Limites : 0-100% stricte, pas de dégradation temporelle
|
||||
|
||||
---
|
||||
|
||||
### [04. Algorithme de recommandation](04-algorithme-recommandation.md)
|
||||
|
||||
**Contenu** : Scoring, géolocalisation, orientation politique, mode Kids
|
||||
|
||||
- Classification géo : Ancré (70%) / Contextuel (50%) / Neutre (20%)
|
||||
- Engagement : 20%, Aléatoire : 10%
|
||||
- Orientation politique : 5 niveaux, équilibre imposé (40/40/20)
|
||||
- Mode Kids : 4 tranches (3-6 / 6-9 / 9-12 / 13-15 ans), activation auto <13 ans
|
||||
- Historique : >80% jamais reproposer, <10s ne pas reproposer
|
||||
|
||||
---
|
||||
|
||||
### [05. Interactions et navigation](05-interactions-navigation.md)
|
||||
|
||||
**Contenu** : Commandes Suivant/Précédent, interactions volant, lecture en boucle
|
||||
|
||||
- Suivant : pré-calcul 5 contenus, recalcul >10km ou 10 min
|
||||
- Précédent : <10s → contenu avant, ≥10s → replay début
|
||||
- Commandes volant : Suivant, Précédent, Play/Pause uniquement
|
||||
- Like automatique : ≥80% écoute → +2 points, 30-79% → +1 point
|
||||
- Actions manuelles : bouton cœur (arrêt véhicule) ou vocal (CarPlay/Android Auto)
|
||||
- Passage auto après 2s (1s mode Kids)
|
||||
|
||||
---
|
||||
|
||||
### [06. Audio-guides multi-séquences](06-audio-guides-multi-sequences.md)
|
||||
|
||||
**Contenu** : Modes déplacement, navigation, déclenchement GPS, publicités
|
||||
|
||||
- **4 modes** : 🚶 Piéton (manuel) / 🚗 Voiture (GPS auto + manuel) / 🚴 Vélo / 🚌 Transport
|
||||
- **Mode Piéton** : pause auto après chaque séquence, user clique Suivant, navigation libre
|
||||
- **Mode Voiture** : déclenchement GPS auto (rayon 30m), boutons manuels actifs, warning sécurité >10 km/h
|
||||
- **Affichage voiture** : distance temps réel + ETA + direction (flèche) + vitesse
|
||||
- **Rayons** : Voiture 30m, Vélo 50m, Transport 100m (configurable créateur 10-200m)
|
||||
- **Publicités** : 1/5 séquences tous modes, auto-play, skippable 5s
|
||||
- **Reprise** : sauvegarde auto (séquence + position exacte), popup si <30j, multi-device (sync cloud)
|
||||
|
||||
---
|
||||
|
||||
### [07. Contenus géolocalisés voiture](07-contenus-geolocalises-voiture.md)
|
||||
|
||||
**Contenu** : Expérience voiture, UI minimaliste, sécurité
|
||||
|
||||
- Interface voiture : minimaliste, boutons larges (80×80px min), lecture auto-play
|
||||
- Commandes vocales : OK Google/Siri + CarPlay/Android Auto
|
||||
- Notifications : in-app uniquement (pas de push), badge discret
|
||||
- Sécurité : warning si interaction manuelle >10 km/h
|
||||
- Mode nuit : automatique selon luminosité GPS ou heure (22h-6h)
|
||||
|
||||
---
|
||||
|
||||
### [08. Mode offline](08-mode-offline.md)
|
||||
|
||||
**Contenu** : Téléchargement, validité, synchronisation
|
||||
|
||||
- Zone géographique : choix manuel (autour de moi / ville / département / région)
|
||||
- Nombre contenus : gratuit 50 max, Premium illimité
|
||||
- WiFi par défaut, mobile avec confirmation + estimation volume
|
||||
- Validité : 30 jours, renouvellement auto si WiFi (contenus >25 jours)
|
||||
- Sync : likes/abonnements batch auto à reconnexion, queue actions 7j max
|
||||
|
||||
---
|
||||
|
||||
### [09. Abonnements et notifications](09-abonnements-notifications.md)
|
||||
|
||||
**Contenu** : Impact algorithme, notifications, audio-guides, limites
|
||||
|
||||
- Boost +30% au score final (pas priorité absolue)
|
||||
- Détection contexte : <5 km/h piéton, >10 km/h voiture
|
||||
- Voiture : in-app uniquement, Piéton : push actives
|
||||
- Limite 10 notifications push/jour (5-20), mode silencieux 22h-8h
|
||||
- Audio-guide piéton : détection <100m lieu, page sélection, navigation manuelle
|
||||
- Max 200 abonnements, +5% jauges tous tags créateur
|
||||
|
||||
---
|
||||
|
||||
### [10. Gestion des erreurs](10-gestion-erreurs.md)
|
||||
|
||||
**Contenu** : Aucun contenu, contenu supprimé, perte réseau, GPS désactivé
|
||||
|
||||
- Aucun contenu : élargissement auto 50km → 100km → département → région → national
|
||||
- Contenu supprimé : laisser terminer, passage auto suivant après 2s
|
||||
- Perte réseau : buffer adaptatif (WiFi 5-120s, 4G 10-120s, 3G 30-300s), retry 5s max 6×
|
||||
- GPS désactivé : mode dégradé (contenu national + neutre + téléchargé)
|
||||
|
||||
---
|
||||
|
||||
## 🎙️ Création de contenu (Créateur)
|
||||
|
||||
### [11. Création et publication de contenu](11-creation-publication-contenu.md)
|
||||
|
||||
**Contenu** : Upload, métadonnées, validation, modification
|
||||
|
||||
- Formats : MP3, AAC (.mp3, .aac, .m4a), max 200 MB, 4h
|
||||
- Métadonnées obligatoires : titre, type géo, zone, tags (1-3), classification âge
|
||||
- Validation 3 premiers contenus : 24-48h (modération RoadWave)
|
||||
- Modification : métadonnées uniquement, pas audio/zone/classification
|
||||
|
||||
---
|
||||
|
||||
### [12. Radio live](12-radio-live.md)
|
||||
|
||||
**Contenu** : Démarrage, arrêt, comportement auditeur
|
||||
|
||||
- Buffer 15s avant diffusion publique, durée max 8h
|
||||
- Notification push abonnés dans zone géo uniquement
|
||||
- Arrêt : compte à rebours 5s (manuel) ou auto si déco ≥60s
|
||||
- Enregistrement auto MP3 256 kbps → replay sous 5-10 min
|
||||
- Auditeur : buffer 15s, continuation si sortie zone, AUCUN chat
|
||||
|
||||
---
|
||||
|
||||
### [13. Détection de contenu protégé](13-detection-contenu-protege.md)
|
||||
|
||||
**Contenu** : Droits d'auteur, détection musique, sanctions, fair use
|
||||
|
||||
- Périmètre MVP : musique uniquement (films/livres exclus)
|
||||
- Fair use : extraits ≤30s autorisés si usage transformatif (critique/analyse)
|
||||
- Détection : manuelle lors des 3 premiers contenus (coût 0€)
|
||||
- Sanctions progressives : Avertissement → Strike 1 (3j) → Strike 2 (7j) → Strike 3 (30j) → Strike 4 (ban)
|
||||
- Réhabilitation : -1 strike tous les 6 mois sans incident
|
||||
- Appel : réutilise processus section 14, délai 72h, preuve licence acceptée
|
||||
- Post-MVP : Chromaprint + MusicBrainz si >50 signalements/mois (50-100€/mois)
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Modération & Sécurité
|
||||
|
||||
### [14. Modération - Flows opérationnels](14-moderation-flows.md)
|
||||
|
||||
**Contenu** : Signalement, traitement, sanctions
|
||||
|
||||
- Signalement : 7 catégories (haine, sexuel, illégalité, droits auteur, spam, fake news, autre)
|
||||
- IA pré-filtre : Whisper large-v3 (transcription) + NLP open source (1-10 min)
|
||||
- SLA : Critique <2h (24/7), Haute/Moyenne <24h, Basse <72h
|
||||
- Notification sanction : email + push + in-app (détail complet : catégorie, timestamp, transcription)
|
||||
- Appel : formulaire in-app, délai 7j max, réponse 72h garanti (standard)
|
||||
|
||||
---
|
||||
|
||||
### [15. Modération Communautaire - Badges et Récompenses](15-moderation-communautaire.md)
|
||||
|
||||
**Contenu** : Système badges, score fiabilité, récompenses, anti-abus
|
||||
|
||||
- **3 niveaux badges** : 🥉 Bronze (5 validés), 🥈 Argent (20 validés), 🥇 Or (50 validés)
|
||||
- **Modal découverte** : affichage unique au 1er signalement, message gratifiant
|
||||
- **Score fiabilité** : calcul auto pour priorisation algorithme (0-100 points)
|
||||
- **Utilisateurs de confiance** : statut auto Argent/Or, traitement prioritaire <12h
|
||||
- **Réduction Premium Or** : -50% pendant 3 mois (2.49€ au lieu de 4.99€), Post-MVP
|
||||
- **Anti-abus** : limite 10 signalements/24h, audit trimestriel, révocation badges
|
||||
- **ROI** : positif dès 2-3 utilisateurs Or (économie modération > coût réductions)
|
||||
- **Coût MVP** : 0€ / **Coût Post-MVP** : 0-200€/mois
|
||||
|
||||
---
|
||||
|
||||
## 💰 Monétisation & Business
|
||||
|
||||
### [16. Publicités](16-publicites.md)
|
||||
|
||||
**Contenu** : Campagnes, fréquence, insertion, facturation
|
||||
|
||||
- Interface self-service, budget min 50€, étalement paramétrable
|
||||
- Fréquence : 1/5 contenus (gratuits uniquement)
|
||||
- Durée : 10-60s (recommandé 15-30s), skippable après 5s
|
||||
- Validation manuelle 24-48h, prépaiement Mangopay
|
||||
- Facturation : écoute complète 0.05€, skip après 5s : 0.02€, skip immédiat : 0€
|
||||
|
||||
---
|
||||
|
||||
### [17. Premium](17-premium.md)
|
||||
|
||||
**Contenu** : Offre, multi-devices, avantages, gestion abonnement
|
||||
|
||||
- Prix : 4.99€/mois OU 49.99€/an (4.16€/mois effectif)
|
||||
- Pas d'essai gratuit, pas de partage familial (MVP)
|
||||
- Multi-devices : 1 seul stream actif, détection connexion simultanée
|
||||
- Avantages : 0 pub, contenus exclusifs 👑, qualité 64 kbps Opus, offline illimité
|
||||
- Paiement : Mangopay (web) ou IAP iOS/Android 5.99€/mois (+30% commission)
|
||||
|
||||
---
|
||||
|
||||
### [18. Monétisation créateurs](18-monetisation-createurs.md)
|
||||
|
||||
**Contenu** : Activation, KYC, sources revenus, paiement
|
||||
|
||||
- Conditions : compte ≥3 mois, ≥500 abonnés, ≥10K écoutes, 0 strike, ≥5 contenus/90j
|
||||
- KYC via Mangopay Connect : SIRET, TVA, RIB pro, pièce ID, Kbis <3 mois
|
||||
- Revenus pub : 3€ / 1000 écoutes complètes (6% CA pub)
|
||||
- Revenus Premium : 70% créateur, 30% plateforme (proportionnel temps écoute)
|
||||
- Paiement : seuil 50€, mensuel (15 du mois suivant), SEPA Mangopay
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Fonctionnalités transverses
|
||||
|
||||
### [19. Autres comportements](19-autres-comportements.md)
|
||||
|
||||
**Contenu** : Partage, profil créateur, recherche
|
||||
|
||||
- Partage : bouton partout, lien `roadwave.fr/share/c/[id]`, web player + deep link
|
||||
- Profil créateur : @pseudo, bio (300 car), stats publiques arrondies, badge vérifié ✓
|
||||
- Badge vérifié : KYC validé OU célébrité OU >10K abonnés
|
||||
- Recherche : full-text PostgreSQL (français, stemming), recherche géo (Nominatim OSM)
|
||||
- Filtres : type, durée, âge, géo, tags, date, premium (combinables)
|
||||
- Affichage : liste enrichie (20/page, infinite scroll) + vue carte Leaflet
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ Organisation
|
||||
|
||||
Chaque fichier de règles métier suit la structure :
|
||||
|
||||
1. **Décisions** : choix validés avec justifications
|
||||
2. **Comportements détaillés** : flux utilisateur, cas limites
|
||||
3. **Paramètres** : valeurs exactes, seuils, durées
|
||||
4. **Points d'attention Gherkin** : éléments à tester
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Utilisation
|
||||
|
||||
Ces documents servent de **référence unique** pour :
|
||||
|
||||
- ✅ Développement backend/frontend
|
||||
- ✅ Écriture des tests Gherkin (BDD)
|
||||
- ✅ Validation QA
|
||||
- ✅ Documentation produit
|
||||
|
||||
**Prochaine étape** : Création des fichiers `.feature` Gherkin dans `features/` basés sur ces règles.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Statistiques
|
||||
|
||||
- **19 sections** validées
|
||||
- **~14 500 lignes** de spécifications détaillées
|
||||
- **Coût infrastructure MVP** : ~50-250€/mois (hors salaires)
|
||||
- **Coût Post-MVP** : +0-200€/mois (réductions Premium contributeurs Or)
|
||||
- **Technologies** : 100% open source (sauf Mangopay paiements)
|
||||
|
||||
---
|
||||
|
||||
**Dernière mise à jour** : Janvier 2026
|
||||
**Statut** : ✅ Toutes sections validées + Modération communautaire ajoutée
|
||||
1
docs/technical.md
Symbolic link
1
docs/technical.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../TECHNICAL.md
|
||||
Reference in New Issue
Block a user