refactor(docs): réorganiser la documentation selon principes DDD
Réorganise la documentation du projet selon les principes du Domain-Driven Design (DDD) pour améliorer la cohésion, la maintenabilité et l'alignement avec l'architecture modulaire du backend. **Structure cible:** ``` docs/domains/ ├── README.md (Context Map) ├── _shared/ (Core Domain) ├── recommendation/ (Supporting Subdomain) ├── content/ (Supporting Subdomain) ├── moderation/ (Supporting Subdomain) ├── advertising/ (Generic Subdomain) ├── premium/ (Generic Subdomain) └── monetization/ (Generic Subdomain) ``` **Changements effectués:** Phase 1: Création de l'arborescence des 7 bounded contexts Phase 2: Déplacement des règles métier (01-19) vers domains/*/rules/ Phase 3: Déplacement des diagrammes d'entités vers domains/*/entities/ Phase 4: Déplacement des diagrammes flux/états/séquences vers domains/*/ Phase 5: Création des README.md pour chaque domaine Phase 6: Déplacement des features Gherkin vers domains/*/features/ Phase 7: Création du Context Map (domains/README.md) Phase 8: Mise à jour de mkdocs.yml pour la nouvelle navigation Phase 9: Correction automatique des liens internes (script fix-markdown-links.sh) Phase 10: Nettoyage de l'ancienne structure (regles-metier/, diagrammes/, features/) **Configuration des tests:** - Makefile: godog run docs/domains/*/features/ - scripts/generate-bdd-docs.py: features_dir → docs/domains **Avantages:** ✅ Cohésion forte: toute la doc d'un domaine au même endroit ✅ Couplage faible: domaines indépendants, dépendances explicites ✅ Navigabilité améliorée: README par domaine = entrée claire ✅ Alignement code/docs: miroir de backend/internal/ ✅ Onboarding facilité: exploration domaine par domaine ✅ Tests BDD intégrés: features au plus près des règles métier Voir docs/REFACTOR-DDD.md pour le plan complet.
This commit is contained in:
319
docs/domains/premium/rules/abonnements-notifications.md
Normal file
319
docs/domains/premium/rules/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** : APNS/FCM natifs (gratuit, aucune limite)
|
||||
|
||||
---
|
||||
|
||||
### 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** (voir [Règle 05 - Section 5.3](05-interactions-navigation.md#actions-complémentaires-mode-piéton-uniquement))
|
||||
- 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
|
||||
225
docs/domains/premium/rules/mode-offline.md
Normal file
225
docs/domains/premium/rules/mode-offline.md
Normal file
@@ -0,0 +1,225 @@
|
||||
## 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)
|
||||
|
||||
**Justification** :
|
||||
- **Pas de conflit possible** : actions unilatérales user (likes/abonnements)
|
||||
- **UX fluide** : pas de blocage offline
|
||||
- **Batch = économie** : requêtes HTTP groupées
|
||||
|
||||
---
|
||||
|
||||
### 11.4 Contenus supprimés pendant offline
|
||||
|
||||
**Problème** : Que se passe-t-il si un utilisateur télécharge des contenus, part offline plusieurs jours, et pendant ce temps certains contenus sont supprimés par les créateurs ou la modération ?
|
||||
|
||||
**Décision** : Suppression immédiate à la reconnexion (Option A - KISS)
|
||||
|
||||
#### Processus de synchronisation
|
||||
|
||||
```
|
||||
User se reconnecte (WiFi détecté)
|
||||
↓
|
||||
1. API sync : GET /offline/validate
|
||||
Backend retourne :
|
||||
{
|
||||
"valid_ids": [id1, id2, id3, ...],
|
||||
"deleted_ids": [id10, id12, id15],
|
||||
"metadata_updates": [{id: id5, new_title: "..."}]
|
||||
}
|
||||
|
||||
2. App mobile compare avec contenus locaux :
|
||||
- valid_ids : renouvelle validité 30j
|
||||
- deleted_ids : suppression immédiate fichiers locaux
|
||||
- metadata_updates : mise à jour titre/créateur/tags
|
||||
|
||||
3. Notification user :
|
||||
Toast : "3 contenus supprimés ont été retirés"
|
||||
```
|
||||
|
||||
#### Gestion contenu en cours d'écoute
|
||||
|
||||
**Si contenu supprimé en cours de lecture** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ Contenu supprimé │
|
||||
├────────────────────────────────────────┤
|
||||
│ Ce contenu n'est plus disponible │
|
||||
│ et a été retiré par le créateur. │
|
||||
│ │
|
||||
│ Passage au contenu suivant... │
|
||||
│ │
|
||||
│ [OK] │
|
||||
└────────────────────────────────────────┘
|
||||
|
||||
→ Lecture s'arrête
|
||||
→ Fichier supprimé localement
|
||||
→ Passage automatique au contenu suivant (après 2s)
|
||||
```
|
||||
|
||||
#### Message récapitulatif
|
||||
|
||||
**Si plusieurs contenus supprimés** :
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ Contenus supprimés │
|
||||
├────────────────────────────────────────┤
|
||||
│ 3 contenus téléchargés ne sont plus │
|
||||
│ disponibles et ont été retirés. │
|
||||
│ │
|
||||
│ Les créateurs peuvent supprimer ou │
|
||||
│ modifier leurs contenus à tout moment. │
|
||||
│ │
|
||||
│ [Voir la liste] [OK] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Bouton "Voir la liste"** :
|
||||
- Affiche titres + créateurs des contenus supprimés
|
||||
- Permet comprendre ce qui a disparu
|
||||
- Historique conservé 7 jours (puis purge)
|
||||
|
||||
**Justification KISS** :
|
||||
- ✅ **Simplicité technique** : pas de grace period complexe, pas de gestion d'états intermédiaires
|
||||
- ✅ **Respect créateur** : si créateur supprime = volonté claire immédiate, pas de diffusion prolongée
|
||||
- ✅ **Conformité légale** : contenu modéré (illégal, violation CGU) retiré immédiatement, pas de risque juridique
|
||||
- ✅ **Cas rare** : peu de créateurs suppriment contenus après publication, impact user limité
|
||||
|
||||
**Post-MVP** : Si feedback négatifs users ("J'étais en train d'écouter et ça s'est coupé brutalement !"), ajouter grace period UNIQUEMENT pour suppression créateur volontaire :
|
||||
- Motif suppression = "modération RoadWave" → Suppression immédiate (sécurité/légalité)
|
||||
- Motif suppression = "créateur volontaire" → Grace period 7 jours + badge "Bientôt retiré"
|
||||
- Motif suppression = "passage Premium" → Si user Premium : conserve accès, si gratuit : grace period 7j
|
||||
|
||||
**Mais attendre feedback réel avant d'ajouter cette complexité.**
|
||||
|
||||
---
|
||||
|
||||
## 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 |
|
||||
|
||||
---
|
||||
245
docs/domains/premium/rules/premium.md
Normal file
245
docs/domains/premium/rules/premium.md
Normal file
@@ -0,0 +1,245 @@
|
||||
## 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 - Priorité au dernier device (KISS)
|
||||
|
||||
**Règle simple** : Le dernier device à démarrer prend toujours la priorité, sans exception temporelle ni géolocalisation.
|
||||
|
||||
#### Comportement multi-devices
|
||||
|
||||
| Scénario | Device 1 | Device 2 | Résultat | Message Device 1 |
|
||||
|----------|----------|----------|----------|------------------|
|
||||
| **Transition normale** | Écoute online | Démarre 5s après | Device 1 coupé, Device 2 actif | "Lecture interrompue : compte utilisé sur autre appareil" |
|
||||
| **Offline connecté internet** | Écoute offline (avec WiFi) | Démarre online | Device 1 coupé, Device 2 actif | Même message |
|
||||
| **Offline déconnecté internet** | Écoute offline (mode avion) | Démarre online | Device 1 continue, Device 2 actif | Pas de détection possible (exception technique) |
|
||||
|
||||
#### Implémentation technique détaillée
|
||||
|
||||
**1. Stream online** :
|
||||
```
|
||||
Device 1 écoute online
|
||||
→ Heartbeat 30s vers serveur
|
||||
→ Redis : active_streams:{user_id} = {device_id: "iPhone_123", started_at: timestamp}
|
||||
→ TTL : 5 minutes
|
||||
|
||||
Device 2 démarre
|
||||
→ Détection : active_stream existe
|
||||
→ Action : Envoi WebSocket close à Device 1
|
||||
→ Redis : mise à jour active_streams:{user_id} = {device_id: "iPad_456", started_at: timestamp}
|
||||
→ Device 2 lecture démarre
|
||||
```
|
||||
|
||||
**2. Offline connecté internet** :
|
||||
```
|
||||
Device 1 écoute offline MAIS connecté WiFi/4G
|
||||
→ Heartbeat 30s envoyé quand même
|
||||
→ Redis : active_streams:{user_id} = {device_id: "iPhone_123", started_at: timestamp, mode: "offline"}
|
||||
→ Même mécanique que online : Device 2 coupe Device 1
|
||||
```
|
||||
|
||||
**3. Offline déconnecté internet** :
|
||||
```
|
||||
Device 1 vraiment offline (mode avion, tunnel)
|
||||
→ Pas de heartbeat possible
|
||||
→ Redis : aucune entrée OU expiration TTL après 5 min
|
||||
→ Device 2 démarre : pas de détection Device 1
|
||||
→ "Tant pis" (exception technique acceptable)
|
||||
```
|
||||
|
||||
#### Message utilisateur (device coupé)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ ⚠️ Lecture interrompue │
|
||||
├────────────────────────────────────────┤
|
||||
│ Votre compte est utilisé sur un │
|
||||
│ autre appareil. │
|
||||
│ │
|
||||
│ Un seul appareil peut écouter à la │
|
||||
│ fois avec votre compte Premium. │
|
||||
│ │
|
||||
│ Le partage de compte viole nos CGU │
|
||||
│ et peut entraîner une suspension. │
|
||||
│ │
|
||||
│ [Reprendre ici] [Sécuriser mon compte] │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Boutons** :
|
||||
- **Reprendre ici** : Coupe l'autre device, reprend lecture sur ce device
|
||||
- **Sécuriser mon compte** : Lien vers changement mot de passe + déconnexion tous devices
|
||||
|
||||
#### Limite offline 30 jours
|
||||
|
||||
**Référence** : [08-mode-offline.md](08-mode-offline.md) section 11.2
|
||||
|
||||
```
|
||||
Contenus téléchargés valides 30 jours
|
||||
→ Après 30j sans connexion : contenus bloqués
|
||||
→ Reconnexion WiFi : renouvellement auto si <30j
|
||||
→ Notification J-3 : "X contenus expirent dans 3 jours"
|
||||
```
|
||||
|
||||
**Cohérence** : User offline déconnecté >30j ne peut plus écouter → force reconnexion → détection multi-devices redevient possible.
|
||||
|
||||
#### Détection abus (post-MVP)
|
||||
|
||||
Monitoring patterns suspects (backend analytics) :
|
||||
- >10 changements devices/jour (suspect)
|
||||
- Connexions alternées 2 villes éloignées répétées
|
||||
- Signalements users : "je n'ai jamais été à Marseille"
|
||||
|
||||
Action :
|
||||
→ Email investigation : "Activité suspecte, sécurisez votre compte"
|
||||
→ Si confirmé partage : suspension 7j + warning
|
||||
→ Récidive : ban définitif
|
||||
|
||||
**Pas d'action automatique** (évite faux positifs), juste flag modération manuelle.
|
||||
|
||||
**Justification KISS** :
|
||||
- ✅ **Simplicité technique** : pas de tracking GPS précis, pas de calcul distances, pas de faux positifs (TGV Paris→Lyon = légitime)
|
||||
- ✅ **Assume bonne foi** : majorité users honnêtes, partage compte = minorité, gestion réactive suffit
|
||||
- ✅ **Message dissuasif clair** : avertissement CGU dans message coupure, possibilité "Sécuriser compte" si suspicion piratage
|
||||
- ✅ **Protection revenus créateurs** : 1 abonnement = 1 personne = 1 écoute active
|
||||
- ✅ **UX claire** : message explicite, user comprend immédiatement
|
||||
|
||||
---
|
||||
|
||||
### 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
|
||||
Reference in New Issue
Block a user