Le service Rezo Séniors côté API : mise en relation, notifications et tâches planifiées
Crédit : Photo by Kampus Production
Cet article fait partie d’une série consacrée à l’API REST de Rezo Pouce. Après avoir présenté l’architecture générale et l’authentification, ce volet entre dans le domaine fonctionnel le plus riche de l’API : le service Rezo Séniors. Un dernier article traitera du géocodage, des documents et de l’import de données.
Le pilotage de Rezo Séniors depuis le back-office présentait le service du point de vue de l’interface d’administration. Cet article s’intéresse à ce qui se passe côté serveur quand un trajet est créé, un conducteur sollicité, une notification envoyée.
Le SeniorsBundle
Le service Rezo Séniors est isolé dans son propre bundle Symfony — le SeniorsBundle. Il possède ses propres entités (UserProfile, TerritoryProfile, DriverDissociation, KmPrice, CityOutsideTerritory, NotificationContext, LogSms), ses propres services et ses propres routes. Cette isolation n’est pas un luxe : le service Séniors a ses propres règles métier, ses propres rôles, ses propres cycles de vie, et il peut évoluer sans impacter la gestion des territoires ou des points d’arrêt.
L’algorithme de mise en relation
Quand un passager soumet une demande de trajet, l’API doit identifier les conducteurs solidaires susceptibles d’y répondre. Le DriversManager orchestre cette recherche selon cinq critères cumulatifs :
- Disponibilité — Les préférences du conducteur couvrent le jour et la demi-journée du trajet. Chaque jour est codé : 0 (indisponible), 1 (matin, avant 14h), 2 (après-midi, à partir de 14h), 3 (journée complète).
- Commune d’intervention — Le conducteur dessert la commune du trajet, déclarée lors de son inscription.
- Absence de vacances — Aucune période d’indisponibilité ne chevauche la date du trajet.
- Absence de dissociation — Le conducteur n’a pas été dissocié de ce trajet par un gestionnaire (un conducteur dissocié ne sera plus jamais sollicité pour ce trajet précis).
- Pas de notification antérieure — Le conducteur n’a pas déjà été notifié pour ce trajet (sauf en cas de relance manuelle).
Ces critères sont volontairement simples. Dans un réseau de bénévoles locaux, un algorithme complexe basé sur la distance ou les itinéraires serait une fausse précision — les conducteurs déclarent les communes qu’ils fréquentent, et c’est cette déclaration qui fait foi.
Les classes de délai
Le délai entre la création du trajet et sa date d’exécution détermine la stratégie de notification. Le DelayManager classe chaque trajet :
- Délai maximal (plus de 48h) — Les conducteurs préférés du passager sont sollicités en priorité. Si aucun conducteur préféré n’est disponible, les conducteurs solidaires éligibles prennent le relais.
- Délai intermédiaire (24h à 48h) — Seuls les conducteurs solidaires éligibles sont sollicités.
- Délai minimal (moins de 24h) — Même stratégie, mais l’urgence est signalée aux gestionnaires.
Ce système de priorité reflète une réalité de terrain : un passager a souvent un conducteur de confiance qu’il préfère, et il est préférable de lui laisser le temps de répondre avant d’élargir la recherche.
Le flux complet d’un trajet
La création d’un trajet déclenche un événement SENIORS_JOURNEY_CREATE_SUCCESS qui active la chaîne de notification :
Le DelayManager calcule la classe de délai. Le DriversManager sélectionne les conducteurs éligibles selon les critères. Le service Notify envoie les notifications par email et SMS aux conducteurs sélectionnés.
Si un conducteur accepte, le gestionnaire l’associe au trajet via le back-office. L’API enregistre l’association, envoie une confirmation au conducteur et au passager (email et SMS), avec les détails pratiques — horaire, adresse, coût estimé basé sur la distance et le prix au kilomètre configuré par territoire.
Si un conducteur est dissocié d’un trajet, un enregistrement DriverDissociation est créé pour empêcher toute nouvelle sollicitation, et les conducteurs non encore notifiés sont automatiquement contactés.
Si personne n’accepte, la tâche planifiée entre en jeu.
La tâche planifiée
L’endpoint /crontasks/seniors/check_drivers_notifications, appelé périodiquement par un cron serveur, réévalue les trajets sans conducteur. À chaque exécution, il recalcule la classe de délai (qui a pu évoluer) et relance les notifications vers les conducteurs qui n’ont pas encore été sollicités.
Ce mécanisme de rattrapage est essentiel : un trajet créé à J-5 avec seulement des conducteurs préférés sollicités peut, à J-2, déclencher automatiquement la sollicitation de l’ensemble des conducteurs éligibles. Le gestionnaire peut aussi déclencher manuellement une relance — dans ce cas, même les conducteurs déjà notifiés sont re-sollicités.
Notifications multi-canal
Le NotificationManager orchestre l’envoi par email et par SMS selon une cascade de vérifications :
Pour l’email : activation globale du canal, autorisation individuelle de l’utilisateur (senior_notification_email), validité de l’adresse (les adresses fictives en @sans-rezopouce.fr1 sont exclues). Les templates sont gérés avec Twig et chaque envoi est tracé en base via l’entité Trace — destinataire, résultat, horodatage.
Pour le SMS : activation globale et territoriale (un territoire peut désactiver les SMS), autorisation individuelle (senior_notification_sms), validité du numéro de téléphone. Les SMS transitent par l’API OVH et chaque envoi est journalisé dans LogSms.
Les notifications de trajet suivent des templates spécifiques : sollicitation du conducteur (« Un passager a besoin d’un accompagnement… »), confirmation d’association (« Vous avez accepté d’accompagner… Coût : X euros »), annulation, dissociation. Les gestionnaires du territoire sont également notifiés selon leur configuration — gestionnaire principal systématiquement, autres gestionnaires selon leur autorisation.
Les communes hors territoire
Un cas particulier mérite d’être mentionné : les communes hors territoire. Un conducteur bénévole peut accepter de desservir une commune qui n’appartient pas à son territoire d’inscription — il habite dans l’intercommunalité A mais se rend régulièrement dans une commune de l’intercommunalité B. L’API gère ces communes via une entité dédiée (CityOutsideTerritory) et des endpoints spécifiques, permettant au gestionnaire d’élargir le rayon d’action des conducteurs sans modifier le périmètre du territoire.
Conducteurs préférés et conducteurs solidaires
L’API distingue deux types de conducteurs. Les conducteurs solidaires sont inscrits au service et déclarent leurs disponibilités et communes d’intervention. Les conducteurs préférés sont associés à un passager spécifique — un voisin, un proche, une personne de confiance. L’algorithme de matching privilégie les conducteurs préférés quand le délai le permet, avant d’élargir aux conducteurs solidaires.
Chaque conducteur possède un profil de préférences (disponibilités par jour et demi-journée), une liste de communes desservies, et peut déclarer des périodes de vacances pendant lesquelles il ne sera pas sollicité. La gestion de ces données est exposée par des endpoints dédiés consommés par le back-office d’administration.
Ce que ce module illustre
Le SeniorsBundle est le domaine fonctionnel le plus riche de l’API. Il combine de la logique métier non triviale (matching multi-critères, classes de délai), de l’orchestration événementielle (création de trajet → notification → association → confirmation), des communications multi-canal (email + SMS avec traçabilité), et des tâches planifiées asynchrones.
Ce qui rend ce module intéressant du point de vue de l’architecture, c’est qu’il fonctionne comme un système autonome au sein de l’API — ses propres entités, ses propres services, ses propres événements — tout en s’intégrant au reste de l’écosystème via les mécanismes standards de Symfony (événements, injection de dépendances, sécurité par rôles).
- Afin d’assurer la compatibilité avec tous les utilisateurs, y compris ceux qui ne disposent pas d’adresse e-mail personnelle, une adresse générique du type « @sans-rezopouce » est automatiquement attribuée. Cette solution permet d’intégrer tous les seniors dans le système de notifications, tout en préservant la continuité du service. ↩︎
Une question ou un projet ?
Je suis disponible pour en discuter.