# language: fr Fonctionnalité: Multi-devices Premium - Dernier device prend priorité (KISS) En tant que système Premium Je veux appliquer une règle simple: le dernier device à démarrer prend toujours la priorité Afin d'éviter la complexité des exceptions temporelles et géographiques Contexte: Étant donné qu'un utilisateur "UserA" a un abonnement Premium actif Et que l'utilisateur possède 2 devices: | device | type | | iPhone_123 | iOS | | iPad_456 | iOS | # Règle KISS : Dernier device = Priorité Scénario: Transition normale - Device 2 démarre après Device 1 Étant donné que "UserA" écoute un contenu sur iPhone_123 Et que le heartbeat iPhone_123 est actif (toutes les 30s) Quand "UserA" démarre la lecture sur iPad_456 (5 secondes après) Alors le serveur détecte une session active (iPhone_123) Et envoie un WebSocket close à iPhone_123 Et iPhone_123 affiche le message: """ ⚠️ 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] """ Et Redis met à jour active_streams:UserA = {device_id: "iPad_456", started_at: timestamp} Et la lecture démarre sur iPad_456 # Offline connecté internet Scénario: Device 1 offline MAIS connecté WiFi - Device 2 coupe Device 1 Étant donné que "UserA" écoute un contenu téléchargé en offline sur iPhone_123 Mais que iPhone_123 est connecté au WiFi Et que le heartbeat iPhone_123 est envoyé toutes les 30s Quand "UserA" démarre la lecture online sur iPad_456 Alors le serveur détecte une session active (iPhone_123, mode offline) Et envoie un WebSocket close à iPhone_123 Et iPhone_123 affiche le même message d'interruption Et iPad_456 démarre normalement # Offline déconnecté internet (exception technique) Scénario: Device 1 vraiment offline (mode avion) - Pas de détection possible Étant donné que "UserA" écoute un contenu offline sur iPhone_123 en mode avion Et qu'aucun heartbeat n'est envoyé (pas de connexion internet) Quand "UserA" démarre la lecture online sur iPad_456 Alors le serveur ne détecte AUCUNE session active Car aucun heartbeat depuis iPhone_123 Et iPad_456 démarre normalement Et iPhone_123 continue en offline (pas de coupure possible) Et c'est une exception technique acceptable ("Tant pis") Scénario: Device 1 offline reconnecte - Heartbeat reprend Étant donné que "UserA" écoutait offline sur iPhone_123 en mode avion Quand iPhone_123 sort du mode avion et se connecte au WiFi Alors le heartbeat reprend immédiatement Et Redis crée/met à jour active_streams:UserA = {device_id: "iPhone_123", started_at: timestamp} Et si iPad_456 démarre, iPhone_123 sera coupé normalement # Heartbeat et TTL Redis Scénario: Heartbeat toutes les 30s maintient la session active Étant donné que "UserA" écoute sur iPhone_123 Quand iPhone_123 envoie un heartbeat toutes les 30 secondes Alors Redis met à jour active_streams:UserA avec TTL 5 minutes Et la session reste active tant que les heartbeats arrivent Scénario: Pas de heartbeat pendant 5 min - Session expirée Étant donné que "UserA" écoutait sur iPhone_123 Mais que iPhone_123 a été éteint brutalement (pas de heartbeat final) Quand 5 minutes se sont écoulées sans heartbeat Alors Redis expire automatiquement l'entrée active_streams:UserA (TTL) Et iPad_456 peut démarrer sans détecter de conflit # Boutons message coupure Scénario: Bouton "Reprendre ici" sur device coupé Étant donné que iPhone_123 a été coupé par iPad_456 Et que le message d'interruption est affiché sur iPhone_123 Quand "UserA" clique sur [Reprendre ici] sur iPhone_123 Alors le serveur détecte que iPad_456 est actif Et envoie un WebSocket close à iPad_456 Et iPhone_123 reprend la lecture Et iPad_456 affiche le même message d'interruption Scénario: Bouton "Sécuriser mon compte" sur device coupé Étant donné que iPhone_123 a été coupé par iPad_456 Et que le message d'interruption est affiché sur iPhone_123 Quand "UserA" clique sur [Sécuriser mon compte] Alors l'utilisateur est redirigé vers: - Page changement mot de passe - Option "Déconnecter tous les appareils" Et un email de sécurité est envoyé Et un log de sécurité est créé (suspicion piratage) # Limite offline 30 jours Scénario: Contenus offline expirés >30j - Force reconnexion Étant donné que "UserA" a téléchargé 50 contenus il y a 32 jours Et qu'il n'a pas reconnecté son device depuis 32 jours Quand "UserA" essaie d'écouter un contenu offline Alors un message s'affiche: """ Contenus expirés (>30 jours) Reconnectez-vous au WiFi pour renouveler vos contenus. """ Et la lecture est bloquée Et cela force la reconnexion (détection multi-devices redevient possible) Scénario: Notification J-3 avant expiration contenus offline Étant donné que "UserA" a des contenus offline qui expirent dans 3 jours Quand le système détecte l'approche de l'expiration Alors une notification push est envoyée: """ ⚠️ 10 contenus offline expirent dans 3 jours Reconnectez-vous au WiFi pour renouveler automatiquement. """ # Détection abus post-MVP Scénario: Monitoring patterns suspects - Flag modération manuelle Étant donné que le système monitore les patterns d'utilisation Quand "UserA" change de device >10 fois par jour pendant 7 jours Alors un flag de suspicion est créé dans le dashboard modération Et un email d'investigation est envoyé à "UserA": """ Activité suspecte détectée sur votre compte Nous avons détecté des changements fréquents d'appareil. Si ce n'est pas vous, sécurisez votre compte immédiatement. [Sécuriser mon compte] [Contacter le support] """ Mais aucune action automatique n'est prise (évite faux positifs) Scénario: Connexions alternées villes éloignées - Flag modération Étant donné que le système monitore les localisations approximatives Quand "UserA" se connecte à Paris à 10h puis Marseille à 10h30 (même jour) Et que ce pattern se répète 5 fois en 1 semaine Alors un flag de suspicion est créé Et un modérateur humain enquête manuellement Mais aucun blocage automatique (évite faux positifs: TGV légitime) Scénario: Partage de compte confirmé - Suspension 7 jours Étant donné qu'un modérateur confirme manuellement un partage de compte Et que les preuves sont claires (signalements users + patterns suspects) Quand le modérateur applique la sanction Alors le compte est suspendu 7 jours Et un email de warning est envoyé: """ ⚠️ Suspension de compte (7 jours) Le partage de compte Premium viole nos CGU. Votre compte est suspendu jusqu'au [Date]. En cas de récidive, le compte sera définitivement fermé. """ Scénario: Récidive partage de compte - Ban définitif Étant donné qu'un compte a déjà été suspendu 7 jours pour partage Et que le partage continue après la réactivation Quand le modérateur confirme la récidive Alors le compte est définitivement fermé (ban) Et un email de fermeture définitive est envoyé Et le remboursement pro-rata de l'abonnement est effectué # Cas d'usage réels Scénario: User change de pièce (voiture → maison) rapidement Étant donné que "UserA" écoute dans sa voiture sur iPhone_123 Quand il arrive chez lui et passe sur iPad_456 après 30 secondes Alors iPhone_123 est coupé immédiatement Et iPad_456 prend le relai Et l'utilisateur comprend le message (device unique) Scénario: User oublie son téléphone en lecture et part Étant donné que "UserA" écoute sur iPhone_123 Et qu'il part en laissant iPhone_123 en lecture Quand il démarre la lecture sur iPad_456 au travail Alors iPhone_123 est coupé à distance (WebSocket close) Et iPad_456 prend la priorité Scénario: TGV Paris → Lyon (légitime) - Pas de faux positif Étant donné que "UserA" prend le TGV Paris → Lyon Et qu'il écoute pendant tout le trajet sur iPhone_123 Quand le système détecte un changement de localisation (GPS) Alors aucun flag de suspicion n'est créé Car c'est un déplacement continu légitime Et aucune action automatique n'est prise # Implémentation technique Redis Scénario: Stockage Redis - Structure active_streams Étant donné que "UserA" démarre la lecture sur iPhone_123 Quand le heartbeat est envoyé au serveur Alors Redis stocke: | clé | valeur | | active_streams:UserA | {"device_id": "iPhone_123", "started_at": 1707311400} | Et un TTL de 5 minutes (300 secondes) est défini Et la clé expire automatiquement si pas de heartbeat Scénario: Mise à jour Redis à chaque heartbeat Étant donné qu'une session active existe sur iPhone_123 Quand un heartbeat est envoyé toutes les 30 secondes Alors Redis met à jour le TTL à 5 minutes Et started_at reste inchangé (timestamp initial) Et la session reste active indéfiniment tant que les heartbeats arrivent # Justification KISS Scénario: Comparaison avec règle complexe - Avantages KISS Étant donné qu'on compare la règle KISS avec une règle complexe (exceptions temporelles, GPS, etc.) Quand on évalue les avantages Alors les avantages KISS sont: | avantage | description | | Simplicité technique | Pas de tracking GPS précis, pas de calcul distances | | Pas de faux positifs | TGV légitime ne déclenche pas d'alerte automatique | | Assume bonne foi | Majorité users honnêtes, gestion réactive suffit | | Message dissuasif clair | Avertissement CGU dans message, option sécurité | | Protection revenus créateurs | 1 abonnement = 1 personne = 1 écoute active | | UX claire | User comprend immédiatement le comportement |