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é :Couvert parSELECT status, COUNT(*) FROM campaign_contracts WHERE tenant_id=:t AND campaign_id=:c GROUP BY statusidx_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_idJWT. - En V0.0.2 (OP-010),
agency_idfilter 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¶
CampaignStatusITAC-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}/statusretourne 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