Aller au contenu

Story OP-006: Backend polling status endpoint — GET /campaigns/{id}/status

Module: operator-console Slice: Architecture §11 OP-002 (vue statut post-envoi polling) Side: [BACKEND] Version target: V0.0.1 Priority: 7 Depends on: OP-003 (campagnes ENVOYÉE existent) Can develop concurrently with: OP-004, OP-007 Merge order: Before OP-009 (frontend détail ENVOYÉE consomme cet endpoint) Estimated complexity: S PRD User Stories: US-OP-05 AC1 (subset polling, sans SSE) Wireframe: N/A — backend only


Objective

Exposer les compteurs agrégés d'une campagne pour le polling 30 s frontend (OP-009). SSE/temps réel reporté V1 (OP-ADR-06).


Backend Scope

Entities

Lecture seule campaign_contracts.

Migrations

Aucune.

Service Layer

  • CampaignStatusController.getStatus(id) : GET /api/operator/campaigns/{id}/status — agrège les compteurs par statut.
  • CampaignStatusQuery : SQL agrégé :
    SELECT status, COUNT(*)
    FROM campaign_contracts
    WHERE tenant_id=:t AND campaign_id=:c
    GROUP BY status
    
    Couvert par idx_cc_tenant_campaign.
  • Calcul conversionPct = round((paye / max(total - exclu, 1)) * 100).
  • polledAt = Instant.now().

API Endpoints

Method Path Request Response Auth
GET /api/operator/campaigns/{id}/status 200 { campaignId, total, inclus, notifEnAttente, notifLivre, notifEchoue, lienOuvert, paye, paymentEchoue, horsPlateforme, lienExpire, exclu, lienRevoque, conversionPct, polledAt } Tous rôles OP

Validation Rules

  • Path {id} UUID valide ; sinon 400.
  • Campagne hors tenant → 404 OP_CAMPAIGN_NOT_FOUND (jamais 403 — leakage prevention).

Multi-Tenant Considerations

  • Scope tenant_id JWT.
  • En V0.0.2 (OP-010), agency_id filter activé : agency_operator ne voit le statut que des campagnes de son agence.

Audit & Logging

  • Pas d'audit sur la lecture.
  • Cache HTTP : Cache-Control: no-store (compteurs temps réel).

Acceptance Criteria

AC-001: Retourne les compteurs agrégés exacts
Given une campagne avec 12 cc (2 NOTIF_EN_ATTENTE, 8 NOTIF_LIVRÉ, 1 NOTIF_ÉCHOUÉ, 1 PAYÉ)
When GET /api/operator/campaigns/{id}/status
Then la réponse 200 contient total=12, notifEnAttente=2, notifLivre=8, notifEchoue=1, paye=1
And conversionPct = round((1/(12-0)) * 100) = 8.

AC-002: 404 sur campagne hors tenant (prévention leakage)
Given une campagne du tenant A
When un operator du tenant B appelle GET /campaigns/{id}/status
Then la réponse est 404 OP_CAMPAIGN_NOT_FOUND (pas 403 — évite la détection par énumération).

Compliance Rules

  • PRD §A.4 : latence temps réel polling 30s max. VÉRIFIÉ.
  • OP-ADR-06 : polling V0.0.0–V0.0.5 ; SSE V1. VÉRIFIÉ.

Standards & Conventions

  • docs/standards/api-guidelines.md — 404 pour leakage prevention.
  • docs/standards/database-guidelines.md — agrégat sur index couvrant.

Testing Requirements

Unit Tests

  • CampaignStatusQueryTest : calcul conversionPct, gestion division par zéro.

Integration Tests

  • CampaignStatusIT AC-001 et AC-002.

What QA Will Validate

  • Polling toutes les 30s côté frontend → endpoint répond < 500 ms p99 sur campagne 100 contrats.

Out of Scope

  • SSE temps réel /events → V1.
  • WebSocket → V1.
  • Cache serveur → React Query suffit côté client.

Definition of Done

  • [ ] GET /api/operator/campaigns/{id}/status retourne JSON conforme
  • [ ] AC-001 + AC-002 passent
  • [ ] p99 < 500 ms sur 1000 cc
  • [ ] Tenant isolation prouvée
  • [ ] Cache-Control no-store
  • [ ] Tests intégration verts