← Retour à l'index

07 — Événements et Notifications

28 types d'événements en 5 domaines, 17 comportements système. Cycle de vie à 6 états, escalade, auto-résolution, notification 2 phases, WhatsApp bidirectionnel, heures de repos, cooldown et idempotence.

28types d'événements
17comportements système
6états du cycle de vie
4canaux de notification
Sommaire
  1. Automate du cycle de vie
  2. Trois niveaux de sévérité
  3. Matrice complète des 28 types
  4. Routage des notifications (28 types x 4 canaux)
  5. Déclencheurs d'auto-résolution
  6. Événements sans auto-résolution
  7. 17 comportements système
  8. Notification 2 phases
  9. Heures de repos
  10. Fenêtres de cooldown
  11. Idempotence des événements info
  12. WhatsApp bidirectionnel
  13. Contexte JSONB et colonnes promues

1. Automate du cycle de vie des événements

Chaque événement warning ou critical suit un cycle de vie à 6 états. Les événements info sont clos immédiatement et ne passent jamais par ce cycle.

Diagramme 1 Cycle de vie des événements (Decision T2-3) Détection moteur / cron 5 min confirm notification_status toujours ouvert critical: bypass immédiat open en attente escalade cron 30 min sans action warning → critical humain investigating en cours humain note action action_taken action faite humain clôture resolved résolu manuellement auto_resolved condition disparue condition disparue condition disparue closed info seulement info immédiat WhatsApp #K{id} + contexte En attente d'action Investigation en cours Résolu (auto ou humain) Info (pas de cycle de vie) action_taken jamais auto-résolu
Automate du cycle de vie : 6 états. Les événements info sont clos immédiatement. L'escalade cible uniquement open. L'auto-résolution cible open et investigating.

2. Trois niveaux de sévérité

Sévérité Cycle de vie Notification Heures de repos
info Aucun. status = 'closed' immédiatement à l'insertion. Selon config tenant (souvent none) Supprimé
warning Workflow complet : open → investigating → action_taken → resolved Selon config tenant File d'attente → envoi à quiet_hours_end
critical Même workflow, notification immédiate Toujours immédiat Bypass — envoi immédiat même à 3h du matin

3. Matrice complète des 28 types d'événements

Chaque événement est défini par son domaine, sa sévérité par défaut, son déclencheur, son mode de notification par défaut, s'il peut s'auto-résoudre et sa fenêtre de cooldown.

critical immédiate + bypass quiet hours
warning workflow complet, escalade possible
info clos immédiatement, pas de lifecycle
Type Sévérité Déclencheur Mode Auto-resolve Cooldown Source
Sécurité (6 types)
unauthorized_stop warning Arrêt à un lieu non enregistré. L'alerte se déclenche contextuellement : arrêt pendant déviation, arrêt nocturne + non enregistré + longue durée, ou zone restreinte. Escalade à critical après 30 min sans action. immédiate Oui — véhicule repart (StopClosed) Consumer
driver_alarm_violation critical Mode park actif (driver app) + vitesse > 5 km/h immédiate Non Consumer
towing_detected critical Vitesse > 10 km/h + contact OFF (multi-signal) immédiate Oui — contact ON ou vitesse = 0 Consumer
power_disconnection critical Tension externe chute à batterie seule, véhicule toujours en émission immédiate Oui — tension externe restaurée Consumer
tracker_anomaly critical Gap classifié tampering_suspect (contact ON, batterie OK, GSM perdu) immédiate Non Consumer
unauthorized_movement warning Mode park actif + vitesse > seuil, OU hors heures de conduite configurées + vitesse > seuil immédiate Oui — park mode désactivé, arrêt, ou heures de conduite reprennent Consumer
Carburant (8 types)
fuel_drain_anomaly critical Dérivé de fuel_drain_detected + contexte anormal (pas en station, durée courte) immédiate Non Consumer
fuel_drain_detected info Niveau carburant baisse > fuel_drain_threshold_percent (10%) sur la fenêtre configurée none N/A (info) Consumer
off_station_refueling warning Dérivé de refueling_detected quand le lieu ne correspond pas à une station enregistrée immédiate Non Consumer
refueling_detected info Niveau carburant augmente > fuel_refill_threshold_liters (5L) none N/A (info) Consumer
low_fuel_driving warning Carburant < low_fuel_threshold_percent (15%) + vitesse > 0. Risque de dommage pompe. immédiate Oui — niveau remonte au-dessus du seuil Consumer
low_fuel info Carburant < seuil, véhicule à l'arrêt none N/A (info) Consumer
fuel_gauge_discrepancy warning Écart capteur vs reçu chauffeur après ravitaillement immédiate Non Consumer
fuel_consumption_anomaly warning L/100km dévie > 2 écarts-types du baseline véhicule sur le même segment digest Non (différé) Consumer
Corridor (3 types)
corridor_deviation warning 3+ positions acceptées consécutives hors polygone corridor immédiate Oui — véhicule rentre dans le corridor (DeviationReturned) 30 min Consumer
deviation_with_stop critical Arrêt détecté pendant une déviation active. Se déclenche à CHAQUE arrêt hors corridor. immédiate Non Consumer
border_crossing info Visite de waypoint de type border (GPS + MCC comme confirmation secondaire) none N/A (info) Consumer
Operations (7 types)
speeding warning Vitesse dépasse les seuils configurés (60 / 90 / 120 km/h, 3 paliers) digest Oui — 5 positions consécutives sous le seuil 15 min Consumer
excessive_idle warning Moteur en marche, véhicule à l'arrêt, durée excessive digest Oui — vitesse > 0 ou contact OFF 30 min Consumer
device_offline warning Cron : aucun message depuis gapAlertThresholdSeconds (1h par défaut) immédiate Oui — prochaine position reçue Cron
vehicle_offline_extended warning Gap ouvert > 1h. Escalade à critical si gap > 4h. immédiate Oui — gap ferme (données reprennent) Cron
mission_sla_breach critical Cron : ETA dépasse la deadline de mission immédiate Non — un SLA violé est un fait Cron
mission_delay_predicted warning ETA projetée dépasse la deadline, calculée à chaque entrée waypoint à partir des moyennes historiques immédiate Oui — véhicule rattrape son retard (mission_delay_cleared) Engine
night_driving info Vitesse > 20 km/h pendant les heures de nuit configurées none N/A (info) Consumer
Logistique (9 types)
destination_reached info Visite de waypoint avec role destination immédiate N/A (info) Consumer
trip_started info Entité trajet créée none N/A (info) Consumer
trip_completed info Entité trajet fermée none N/A (info) Consumer
stop_labelled info Chauffeur labellise un arrêt via l'application mobile none N/A (info) Driver
breakdown_reported warning Chauffeur signale une panne via l'application mobile immédiate N/A Driver
delivery_confirmed info Chauffeur prend une photo de preuve de livraison à destination none N/A (info) Driver
fuel_receipt_submitted info Chauffeur soumet un reçu carburant (photo + litres) none N/A (info) Driver
mission_paused info Mission en pause (panne, route barree, attente, etc.) immédiate N/A (info) Engine/Owner
mission_resumed info Mission reprise après pause none N/A (info) Engine/Owner

4. Routage des notifications (28 types x 4 canaux)

Chaque type d'événement est route vers un ou plusieurs canaux. Le propriétaire de flotte configuré les surcharges via tenant_notification_config.

O Canal actif
Canal inactif
Diagramme 2 TYPE WHATSAPP DASHBOARD PORTAL DRIVER PUSH MODE SECURITE unauthorized_stop O O O imm. driver_alarm_violation O O O imm. towing_detected O O O imm. power_disconnection O O imm. tracker_anomaly O O imm. unauthorized_movement O O imm. CARBURANT fuel_drain_anomaly O O imm. fuel_drain_detected O none off_station_refueling O O imm. refueling_detected O none low_fuel_driving O O O imm. low_fuel O none fuel_gauge_discrepancy O O imm. fuel_consumption_anomaly O O digest CORRIDOR corridor_deviation O O imm. deviation_with_stop O O imm. border_crossing O O none OPERATIONS speeding O O digest excessive_idle O O digest device_offline O O imm. vehicle_offline_extended O O imm. mission_sla_breach O O O imm. mission_delay_predicted O O O imm. night_driving O none LOGISTIQUE destination_reached O O imm. trip_started / trip_completed O O none breakdown_reported O O imm. mission_paused O O O O imm. mission_resumed O O O none stop_labelled / delivery_confirmed / fuel_receipt_submitted O none
Matrice de routage : 28 types x 4 canaux. Les cerclés pleins indiquent un canal actif par défaut. Les surcharges sont configurables par tenant.

5. Déclencheurs d'auto-résolution

L'auto-résolution cible les événements en statut open et investigating uniquement. Les événements en action_taken nécessitent une résolution humaine. L'auto-résolution a toujours priorité sur l'escalade.

Diagramme 3 Déclencheurs d'auto-résolution unauthorized_stop corridor_deviation vehicle_offline_extended unauthorized_movement speeding excessive_idle device_offline towing_detected low_fuel_driving mission_delay_predicted Véhicule repart (StopClosed) Retour dans le corridor (DeviationReturned) Gap ferme (données reprennent) Park mode OFF / arrêt / heures reprennent 5 positions consécutives sous le seuil Vitesse > 0 ou contact OFF Prochaine position reçue Contact ON ou vitesse = 0 Niveau carburant remonte au-dessus du seuil ETA projetée de nouveau dans les délais CONDITION DISPARUE
10 types d'événements avec auto-résolution. L'événement passe en auto_resolved quand la condition de gauche est satisfaite. Aussi inclus : power_disconnection (tension restaurée).

6. Événements sans auto-résolution (7 types)

Ces événements nécessitent une investigation manuelle. Même si la condition physique disparaît, le fait reste et demande une vérification humaine.

Type Raison
driver_alarm_violation L'arrêt du véhicule n'explique pas pourquoi il a bouge avec le mode park actif. Nécessite une investigation.
tracker_anomaly Nécessite une inspection physique du traceur GPS.
fuel_drain_anomaly Nécessite une investigation du contexte de baisse de carburant anormale.
off_station_refueling Nécessite une vérification du lieu de ravitaillement.
deviation_with_stop Chaque arrêt pendant une déviation est un incident séparé nécessitant une investigation.
mission_sla_breach Un SLA violé est un fait qui ne peut pas être annule.
fuel_gauge_discrepancy L'écart entre capteur et reçu nécessite une vérification humaine.

7. Les 17 comportements système

1Trois niveaux de sévérité
info = pas de cycle de vie, clos immédiatement. warning = workflow complet avec escalade possible. critical = immédiat, bypass heures de repos.
2Cycle de vie à 6 états
openinvestigatingaction_takenresolved. Branche parallele : open ou investigatingauto_resolved. Les info vont directement en closed.
3Escalade par cron
Warning non acquitté après 30 min → escalade à critical. Cible uniquement open, jamais investigating ni action_taken (quelqu'un s'en occupe).
4Auto-résolution prioritaire
L'auto-résolution a toujours priorité sur l'escalade. Si la condition disparaît a T+29 min et le cron tourne a T+30 min, l'événement est déjà résolu. Pas de "CRITICAL" suivi de "auto-résolu".
57 types sans auto-résolution
driver_alarm_violation, tracker_anomaly, fuel_drain_anomaly, off_station_refueling, deviation_with_stop, mission_sla_breach, fuel_gauge_discrepancy. Faits qui nécessitent une investigation humaine.
6Notification 2 phases
Détection → attente 5 min → confirmation que l'événement est toujours ouvert → envoi. Les événements critical contournent l'attente de 5 min.
7Heures de repos
22h-6h : critical = immédiat. warning = mis en file, envoyé à 6h. info = supprimé jusqu'à la fin des heures de repos.
8Fenêtres de cooldown
Après auto-résolution, un nouvel événement du même type est supprimé pendant la fenêtre : speeding 15 min, corridor_deviation 30 min, excessive_idle 30 min, low_battery 60 min.
9Idempotence des info
Cles de bucketing par type : night_driving:{vehicleId}:{date}, border_crossing:{vehicleId}:{waypointId}:{tripId}, etc. ON CONFLICT DO NOTHING sur la cle d'idempotence.
10Contexte JSONB discrimine
Union discriminée par event_subtype. Cles indépendantes de la langue. Le titre français est assemblé côté UI à partir de la structure. Pas de reverse geocoding dans le hot path.
11Colonnes promues pour analytics
financial_impact_fcfa, duration_seconds, fuel_delta_liters existent en colonnes réelles en plus du JSONB. JSONB = affichage, colonnes = requêtes analytics.
12WhatsApp bidirectionnel
Codes #K{id} séquentiels par tenant. Le propriétaire repond avec le code pour commenter. action_notes en tableau JSONB append-only : [{note, by, at}].
13Traçage de source
4 sources : engine (pipeline temps réel), cron (détections temporelles), driver (app mobile), owner (actions propriétaire).
14Liaison d'événements
related_event_id FK, un seul niveau. Exemples : refueling_detectedoff_station_refueling, fuel_drain_detectedfuel_drain_anomaly.
15Liaison par FK d'entité
trip_id, stop_id, gap_id, visit_id (waypoint_visit), deviation_id, fuel_event_id. Chaque FK est nullable et lie l'événement à l'entité qui l'a déclenche.
16Notifications push chauffeur
6 types envoyés par push Expo : unauthorized_stop (label prompt), low_fuel_driving, towing_detected, driver_alarm_violation, mission_paused, mission_resumed.
17Échec de livraison
L'échec de livraison WhatsApp déclenche un mécanisme de retry. L'échec ne provoque jamais une escalade de sévérité. Retry uniquement, pas d'amplification.

8. Notification 2 phases (detect-then-confirm)

Diagramme 4 Phase 1 : Détection INSERT vehicle_events notification_status = 'pending' 5 min scheduled_at = now() + 5m Phase 2 : Confirm Cron vérifie : status encore 'open' ? Si résolu → suppressed toujours ouvert Envoi WhatsApp notification_status = 'sent' CRITICAL : bypass immédiat (pas d'attente 5 min) Colonnes: notification_status ('pending' | 'confirmed' | 'sent' | 'suppressed'), notification_scheduled_at, notification_sent_at, quiet_hours_deferred

9. Heures de repos (22h — 6h)

Sévérité Pendant heures de repos Exemple
critical Immédiat — envoi même à 3h du matin towing_detected à 2h47 → WhatsApp envoyé immédiatement
warning File d'attente — envoyé à quiet_hours_end (6h par défaut) speeding à 23h15 → envoyé à 6h00 si toujours ouvert
info Supprimé — pas de notification night_driving → dashboard uniquement

La configuration est par tenant : quietHoursEnabled (défaut: false), quietHoursStart (défaut: 22), quietHoursEnd (défaut: 6). Timezone: Africa/Douala (UTC+1, Cameroun et Tchad).

10. Fenêtres de cooldown

Après auto-résolution, un nouvel événement du même type pour le même véhicule est supprimé pendant la durée du cooldown. Le cooldown est appliqué dans le consumer, pas dans le moteur. Le moteur émet toujours ; le consumer déduplique.

Type Cooldown Raison
speeding 15 min Vitesse qui oscille autour du seuil
corridor_deviation 30 min Retour bref au corridor puis re-sortie
excessive_idle 30 min Moteur en marche/arrêt répété
low_battery 60 min Tension qui oscille près du seuil

Le consumer vérifie resolved_at sur le dernier événement résolu du même type pour le véhicule. Si now() - resolved_at < cooldown, le nouvel événement est supprimé (pas inséré). La contrainte unique idempotency_key gère la déduplication exacte ; le cooldown gère la déduplication temporelle après résolution.

11. Idempotence des événements info

Les événements info (status = 'closed') contournent l'index partiel de dedup (qui porte sur open/investigating/action_taken). Des clés de bucketing par type empechent l'inondation :

Type Cle de bucketing (idempotency_key) Portee
night_driving night_driving:{vehicle_id}:{calendar_date} 1 par véhicule par nuit
border_crossing border_crossing:{vehicle_id}:{waypoint_id}:{trip_id} 1 par véhicule par frontiere par trajet
trip_started trip_started:{vehicle_id}:{trip_id} Naturellement unique (1 par trajet)
trip_completed trip_completed:{vehicle_id}:{trip_id} Naturellement unique
destination_reached destination_reached:{vehicle_id}:{waypoint_id}:{mission_id} 1 par destination par mission
refueling_detected refueling_detected:{fuel_event_id} Naturellement unique (1 par fuel_event)
fuel_drain_detected fuel_drain_detected:{fuel_event_id} Naturellement unique
low_fuel low_fuel:{vehicle_id}:{calendar_date} 1 par véhicule par jour
low_battery low_battery:{vehicle_id}:{calendar_date} 1 par véhicule par jour

12. WhatsApp bidirectionnel (#K{id})

Chaque événement warning/critical envoyé par WhatsApp inclut un code de référence séquentiel par tenant. Le propriétaire de flotte peut répondre avec ce code pour commenter l'événement.

ALERTE Arrêt non autorise
Véhicule : K12 (AB-1234-CM)
Lieu : quartier résidentiel, Ngaoundéré
Durée : 2h40
Moteur OFF depuis 2h35
Lieu enregistré le plus proche : Dépôt Ngaoundéré (4.2 km)
Répondez avec #K47 pour commenter.
#K47 chauffeur dit route barree, attend réouverture
Note enregistrée pour . Statut : investigation en cours.

Structure action_notes (append-only JSONB array)

ChampTypeDescription
notestringTexte du commentaire
bystringIdentifiant de l'auteur (owner, system, driver)
atISO 8601Horodatage du commentaire

Si la réponse ne contient pas de code #K{id}, le bot fait un fallback sur l'événement ouvert le plus récent pour ce véhicule.

13. Contexte JSONB et colonnes promues

Chaque type d'événement porte un contexte JSONB (EventContext) avec des clés indépendantes de la langue. Le UI assemblé les titres en français à partir de event_subtype + les champs structurés.

Colonnes promues pour analytics

Colonne Type Usage
financial_impact_fcfa integer Impact financier en FCFA (écart carburant, déviation, ravitaillement non autorisé)
duration_seconds integer Durée de l'événement (arrêt, déviation, idle)
fuel_delta_liters numeric Variation de carburant associée (drain, ravitaillement)

FK d'entités liées (nullable)

FKLie a
trip_idTrajet qui a déclenche l'événement
stop_idArrêt qui a déclenche l'événement
gap_idGap qui a déclenche l'événement
visit_idVisite de waypoint associée
deviation_idDéviation de corridor associée
fuel_event_idÉvénement carburant associe (fuel_events)
related_event_idÉvénement parent (1 seul niveau : refueling → off_station, drain → anomaly)

Colonnes du cycle de vie des notifications

ColonneValeurs
notification_status'pending' | 'confirmed' | 'sent' | 'suppressed'
notification_scheduled_atDetect time + 5 min
notification_sent_atHorodatage d'envoi effectif
quiet_hours_deferredBoolean (défaut false)

Source : async-ops.md (section 10), state-machines.md (section 3.6), config.md (section 11.3), types.md (section 6b)
← Retour à l'index