2998 lines
66 KiB
Markdown
2998 lines
66 KiB
Markdown
# 📚 Documentation API - Plateforme de Gestion de Commandes
|
||
|
||
**Version:** 5.2.0
|
||
**Date:** 2026-05-01
|
||
**Base URL prod:** `https://mln-uber.club` (HTTPS via WAF nginx + ModSecurity)
|
||
**Base URL dev:** `http://localhost:8080`
|
||
**Technologies:** Go 1.24, Gin, PostgreSQL 16, Redis 7, React 19 + Vite, Expo 54 (React Native), TomTom API
|
||
|
||
---
|
||
|
||
## 📋 Table des Matières
|
||
|
||
1. [Vue d'ensemble](#vue-densemble)
|
||
2. [Déploiement Production](#-déploiement-production)
|
||
3. [Authentication](#authentication)
|
||
4. [API Client (v1)](#api-client-v1)
|
||
5. [API Admin (v2)](#api-admin-v2)
|
||
6. [API Cabine (v1)](#api-cabine-v1)
|
||
7. [API Livreur (v1)](#api-livreur-v1)
|
||
8. [Notifications Push & Telegram](#-notifications-push--telegram)
|
||
9. [Paiements Crypto](#-paiements-crypto)
|
||
10. [Systeme GPS Integre](#-systeme-gps-integre)
|
||
11. [Codes d'Erreur](#codes-derreur)
|
||
|
||
---
|
||
|
||
## 🎯 Vue d'ensemble
|
||
|
||
Cette API REST complète gère une plateforme de livraison avec assignation automatique GPS des livreurs, suivi en temps réel, et système de pénalités avancé.
|
||
|
||
### ✨ Fonctionnalités Principales
|
||
|
||
- **🔐 Authentication JWT** multi-rôles (Client, Admin, Livreur, Cabine) avec révocation de token
|
||
- **🚗 Auto-assignation GPS** des livreurs les plus proches (worker toutes les 1 minute)
|
||
- **📍 Suivi temps réel** avec ETA et géolocalisation
|
||
- **🔄 Système de queues** Redis optimisé pour les livraisons
|
||
- **⚡ Workers automatiques** (nettoyage, assignation, notifications)
|
||
- **🎯 Système de pénalités** pour la gestion des comportements
|
||
- **🔔 Notifications push** Expo (iOS/Android) pour clients et livreurs
|
||
- **🤖 Intégration Telegram** pour alertes et notifications admin/livreur
|
||
- **💸 Paiements crypto** via NowPayments (webhook HMAC)
|
||
- **🛡️ WAF nginx + ModSecurity** (OWASP CRS) en production
|
||
- **📱 Applications mobiles** Expo 54 (client + admin/livreur)
|
||
|
||
### 🏗️ Stack Technique
|
||
|
||
| Composant | Technologie |
|
||
|-----------|-------------|
|
||
| **Backend** | Go 1.24 + Gin · port 8080 |
|
||
| **Base de données** | PostgreSQL 16 |
|
||
| **Cache / Sessions / Queues** | Redis 7 |
|
||
| **Frontend web** | React 19 + TypeScript + Vite (`frontend-prep/`) |
|
||
| **App mobile client** | Expo 54 + React Native (`mobile/`) |
|
||
| **App mobile admin/livreur** | Expo 54 + React Native (`frontend-admin/`) |
|
||
| **WAF / Reverse proxy** | Nginx + ModSecurity OWASP CRS |
|
||
| **Géocodage** | Nominatim (OpenStreetMap) |
|
||
| **Routing / ETA** | TomTom Routing API |
|
||
| **Push notifications** | Expo Push Notifications |
|
||
| **Paiements** | NowPayments (crypto) |
|
||
| **Messagerie** | Telegram Bot API |
|
||
|
||
---
|
||
|
||
### 🏗️ Architecture Globale du Systeme
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph Frontend["🖥️ Frontend (React/TypeScript)"]
|
||
UI_CLIENT[Interface Client]
|
||
UI_ADMIN[Interface Admin]
|
||
UI_LIVREUR[Interface Livreur]
|
||
UI_CABINE[Interface Cabine]
|
||
TOMTOM_MAP[TomTomMap Component]
|
||
end
|
||
|
||
subgraph API_GATEWAY["🌐 API Gateway (Go/Gin)"]
|
||
AUTH[Middleware Auth JWT]
|
||
ROUTES[Router]
|
||
end
|
||
|
||
subgraph API_V1["📱 API v1"]
|
||
CLIENT_API["/api/v1/client"]
|
||
LIVREUR_API["/api/v1/livreur"]
|
||
CABINE_API["/api/v1/cabine"]
|
||
PUBLIC_API["/api/v1/public"]
|
||
end
|
||
|
||
subgraph API_V2["👨💼 API v2 Admin"]
|
||
ADMIN_API["/api/v2/admin"]
|
||
end
|
||
|
||
subgraph HANDLERS["⚙️ Handlers"]
|
||
H_AUTH[Auth Handler]
|
||
H_PRODUCT[Product Handler]
|
||
H_PANIER[Panier Handler]
|
||
H_COMMAND[Command Handler]
|
||
H_DELIVERY[Delivery Handler]
|
||
H_GPS[GPS Handler]
|
||
H_ETA[ETA Handler]
|
||
H_PENALTY[Penalty Handler]
|
||
end
|
||
|
||
subgraph SERVICES["🔧 Services"]
|
||
S_GEO[Geo Service]
|
||
S_TOMTOM[TomTom Service]
|
||
S_QUEUE[Queue Service]
|
||
S_NOTIF[Notification Service]
|
||
end
|
||
|
||
subgraph EXTERNAL["🌍 APIs Externes"]
|
||
NOMINATIM[Nominatim API]
|
||
TOMTOM_API[TomTom API]
|
||
end
|
||
|
||
subgraph STORAGE["💾 Stockage"]
|
||
POSTGRES[(PostgreSQL)]
|
||
REDIS[(Redis)]
|
||
end
|
||
|
||
subgraph WORKERS["⚡ Workers"]
|
||
W_ASSIGN[Auto-Assign Worker]
|
||
W_CLEANUP[Cleanup Worker]
|
||
W_ETA[ETA Update Worker]
|
||
end
|
||
|
||
%% Frontend connections
|
||
UI_CLIENT --> AUTH
|
||
UI_ADMIN --> AUTH
|
||
UI_LIVREUR --> AUTH
|
||
UI_CABINE --> AUTH
|
||
TOMTOM_MAP --> TOMTOM_API
|
||
|
||
%% API Gateway
|
||
AUTH --> ROUTES
|
||
ROUTES --> API_V1
|
||
ROUTES --> API_V2
|
||
|
||
%% API to Handlers
|
||
CLIENT_API --> H_AUTH
|
||
CLIENT_API --> H_PANIER
|
||
CLIENT_API --> H_COMMAND
|
||
LIVREUR_API --> H_DELIVERY
|
||
LIVREUR_API --> H_GPS
|
||
CABINE_API --> H_COMMAND
|
||
PUBLIC_API --> H_PRODUCT
|
||
ADMIN_API --> H_COMMAND
|
||
ADMIN_API --> H_DELIVERY
|
||
ADMIN_API --> H_GPS
|
||
ADMIN_API --> H_PENALTY
|
||
|
||
%% Handlers to Services
|
||
H_GPS --> S_GEO
|
||
H_GPS --> S_TOMTOM
|
||
H_ETA --> S_TOMTOM
|
||
H_DELIVERY --> S_QUEUE
|
||
H_COMMAND --> S_NOTIF
|
||
|
||
%% Services to External
|
||
S_GEO --> NOMINATIM
|
||
S_TOMTOM --> TOMTOM_API
|
||
|
||
%% Services to Storage
|
||
S_GEO --> REDIS
|
||
S_QUEUE --> REDIS
|
||
H_COMMAND --> POSTGRES
|
||
H_PRODUCT --> POSTGRES
|
||
H_DELIVERY --> POSTGRES
|
||
|
||
%% Workers
|
||
W_ASSIGN --> S_GEO
|
||
W_ASSIGN --> S_QUEUE
|
||
W_ASSIGN --> POSTGRES
|
||
W_CLEANUP --> REDIS
|
||
W_CLEANUP --> POSTGRES
|
||
W_ETA --> S_TOMTOM
|
||
W_ETA --> REDIS
|
||
```
|
||
|
||
### 🔄 Flux des Donnees par Role
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph Client["👤 Client"]
|
||
C1[Consulter Produits]
|
||
C2[Gerer Panier + Stock auto]
|
||
C3[Passer Commande]
|
||
C4[Suivre Livraison + ETA]
|
||
C5[Approuver/Annuler]
|
||
C6[Notifications Push]
|
||
C7[Solde Parrainage]
|
||
end
|
||
|
||
subgraph Admin["👨💼 Admin"]
|
||
A1[Gerer Produits/Clients]
|
||
A2[Voir/Modifier Commandes]
|
||
A3[Assigner Livreurs GPS]
|
||
A4[Gerer Penalites]
|
||
A5[Surveiller GPS + Queues]
|
||
A6[Parametres Globaux]
|
||
end
|
||
|
||
subgraph Livreur["🚚 Livreur"]
|
||
L1[Voir Mes Livraisons]
|
||
L2[Mettre a Jour Position GPS]
|
||
L3[Changer Statut]
|
||
L4[Voir Ma Queue]
|
||
L5[Alertes Police]
|
||
L6[Notifications Push]
|
||
end
|
||
|
||
subgraph Cabine["🏭 Cabine/Cuisine"]
|
||
K1[Voir Articles Commande]
|
||
K2[Preparer/Marquer Pret]
|
||
K3[Assigner Livreur]
|
||
K4[Gerer Penalites Client]
|
||
K5[Voir Alertes]
|
||
end
|
||
|
||
subgraph Backend["⚙️ Backend"]
|
||
API[API REST]
|
||
GPS[Service GPS]
|
||
QUEUE[Gestion Queue]
|
||
end
|
||
|
||
C1 & C2 & C3 & C4 & C5 & C6 & C7 --> API
|
||
A1 & A2 & A3 & A4 & A5 & A6 --> API
|
||
L1 & L2 & L3 & L4 & L5 & L6 --> API
|
||
K1 & K2 & K3 & K4 & K5 --> API
|
||
API --> GPS
|
||
API --> QUEUE
|
||
```
|
||
|
||
### 📡 Architecture des Endpoints API
|
||
|
||
```mermaid
|
||
graph TD
|
||
subgraph PUBLIC["🔓 Endpoints Publics (sans auth)"]
|
||
P1["GET /api/v1/products"]
|
||
P2["GET /api/v1/products/:id"]
|
||
P3["GET /api/v1/products/category/:category"]
|
||
P4["GET /api/v1/categories"]
|
||
P5["GET /api/v1/app-settings"]
|
||
P6["POST /api/v1/webhooks/nowpayments"]
|
||
P7["POST /webhook/telegram"]
|
||
end
|
||
|
||
subgraph AUTH_PUBLIC["🔑 Auth Publique (rate-limited)"]
|
||
AP1["POST /api/v1/auth/login"]
|
||
AP2["POST /api/v1/auth/logout"]
|
||
AP3["PUT /api/v1/auth/change-password (JWT)"]
|
||
end
|
||
|
||
subgraph CLIENT_AUTH["🔐 Client (JWT Required)"]
|
||
C1["POST /api/v1/panier/add"]
|
||
C2["GET /api/v1/panier/:username"]
|
||
C3["DELETE /api/v1/panier/remove"]
|
||
C4["DELETE /api/v1/panier/clear"]
|
||
C5["POST /api/v1/checkout"]
|
||
C6["GET /api/v1/my-commands"]
|
||
C7["GET /api/v1/my-commands/history"]
|
||
C8["GET /api/v1/my-commands/history/detailed"]
|
||
C9["GET /api/v1/commands/:id"]
|
||
C10["GET /api/v1/commands/:id/status"]
|
||
C11["GET /api/v1/commands/:id/tracking"]
|
||
C12["GET /api/v1/commands/:id/eta"]
|
||
C13["GET /api/v1/commands/:id/items"]
|
||
C14["GET /api/v1/commands/:id/history"]
|
||
C15["POST /api/v1/commands/:id/approve"]
|
||
C16["POST /api/v1/commands/:id/cancel"]
|
||
C17["POST /api/v1/commands/:id/address/respond"]
|
||
C18["GET /api/v1/my-cancellation-history"]
|
||
C19["GET /api/v1/penalties"]
|
||
C20["GET /api/v1/notifications"]
|
||
C21["POST /api/v1/notifications/read"]
|
||
C22["GET /api/v1/profile"]
|
||
C23["PUT /api/v1/profile/update"]
|
||
C24["GET /api/v1/referral/balance"]
|
||
C25["GET /api/v1/parrain"]
|
||
C26["GET /api/v1/commands/:id/payment-status"]
|
||
C27["POST /api/v1/telegram/link-token"]
|
||
C28["GET /api/v1/telegram/status"]
|
||
C29["DELETE /api/v1/telegram/unlink"]
|
||
end
|
||
|
||
subgraph ADMIN_AUTH["👨💼 Admin v2 (JWT Required)"]
|
||
A1["GET /api/v2/admin/protected/orders"]
|
||
A2["GET /api/v2/admin/protected/orders/:id"]
|
||
A3["PUT /api/v2/admin/protected/orders/:id/status"]
|
||
A4["PUT /api/v2/admin/protected/orders/:id/address"]
|
||
A5["POST /api/v2/admin/protected/orders/:id/notify-client"]
|
||
A6["POST /api/v2/admin/protected/orders/:id/auto-assign"]
|
||
A7["POST /api/v2/admin/protected/orders/auto-assign-all"]
|
||
A8["POST /api/v2/admin/protected/orders/:id/force-validate"]
|
||
A9["POST /api/v2/admin/protected/orders/:id/confirm-reception"]
|
||
A10["GET /api/v2/admin/protected/delivery-persons"]
|
||
A11["GET /api/v2/admin/protected/delivery-persons/:username"]
|
||
A12["GET /api/v2/admin/protected/delivery-persons/:username/location"]
|
||
A13["GET /api/v2/admin/protected/delivery/queues"]
|
||
A14["POST /api/v2/admin/protected/delivery/distances"]
|
||
A15["POST/GET /api/v2/admin/protected/products/:id"]
|
||
A16["POST /api/v2/admin/protected/categories"]
|
||
A17["POST /api/v2/admin/protected/penalty"]
|
||
A18["GET /api/v2/admin/protected/all/clients"]
|
||
A19["PUT /api/v2/admin/protected/clients/:id"]
|
||
A20["GET /api/v2/admin/protected/alerts"]
|
||
A21["GET/PUT /api/v2/admin/protected/settings"]
|
||
A22["GET /api/v2/admin/protected/penalties/all"]
|
||
A23["GET /api/v2/admin/protected/addresses"]
|
||
end
|
||
|
||
subgraph LIVREUR_AUTH["🚚 Livreur (JWT Required)"]
|
||
L1["GET /api/v1/livreur/deliveries"]
|
||
L2["GET /api/v1/livreur/deliveries/:id"]
|
||
L3["POST /api/v1/livreur/deliveries/:id/start"]
|
||
L4["PUT /api/v1/livreur/deliveries/:id/status"]
|
||
L5["GET /api/v1/livreur/deliveries/:id/nav-link"]
|
||
L6["POST /api/v1/livreur/location/update"]
|
||
L7["GET /api/v1/livreur/location"]
|
||
L8["POST /api/v1/livreur/update/status"]
|
||
L9["GET /api/v1/livreur/status"]
|
||
L10["GET /api/v1/livreur/queue"]
|
||
L11["POST /api/v1/livreur/alert"]
|
||
L12["DELETE /api/v1/livreur/alert/:id"]
|
||
L13["GET /api/v1/livreur/alerts"]
|
||
L14["GET /api/v1/livreur/notifications"]
|
||
L15["POST /api/v1/livreur/notifications/read"]
|
||
L16["POST /api/v1/livreur/telegram/link-token"]
|
||
L17["GET /api/v1/livreur/telegram/status"]
|
||
L18["DELETE /api/v1/livreur/telegram/unlink"]
|
||
end
|
||
|
||
subgraph CABINE_AUTH["🏭 Cabine (JWT Required)"]
|
||
K1["GET /api/v1/cabine/commands/:id/items"]
|
||
K2["PUT /api/v1/cabine/commands/:id/status"]
|
||
K3["PUT /api/v1/cabine/items/:item_id/status"]
|
||
K4["POST /api/v1/cabine/commands/:id/confirm-reception"]
|
||
K5["POST /api/v1/cabine/commands/:id/assign"]
|
||
K6["POST /api/v1/cabine/commands/:id/notify-client"]
|
||
K7["GET /api/v1/cabine/all/deliveryman"]
|
||
K8["GET /api/v1/cabine/alerts"]
|
||
K9["GET /api/v1/cabine/penalties/all"]
|
||
K10["GET /api/v1/cabine/addresses"]
|
||
K11["GET /api/v1/cabine/notifications"]
|
||
K12["POST /api/v1/cabine/telegram/link-token"]
|
||
end
|
||
|
||
GW[API Gateway :8080]
|
||
GW --> PUBLIC
|
||
GW --> AUTH_PUBLIC
|
||
GW --> CLIENT_AUTH
|
||
GW --> ADMIN_AUTH
|
||
GW --> LIVREUR_AUTH
|
||
GW --> CABINE_AUTH
|
||
```
|
||
|
||
### 🔐 Flux d'Authentification
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as Utilisateur
|
||
participant F as Frontend
|
||
participant API as Backend API
|
||
participant DB as PostgreSQL
|
||
participant R as Redis
|
||
participant TG as Telegram
|
||
|
||
rect rgb(200, 230, 200)
|
||
Note over U,R: Registration
|
||
U->>F: Remplir formulaire inscription
|
||
F->>API: POST /auth/register
|
||
API->>DB: Creer utilisateur
|
||
DB-->>API: User cree
|
||
API-->>F: 201 Created + User info
|
||
F-->>U: Compte cree
|
||
end
|
||
|
||
rect rgb(200, 200, 230)
|
||
Note over U,TG: Login Standard (2FA desactivee)
|
||
U->>F: Entrer credentials
|
||
F->>API: POST /auth/login
|
||
API->>DB: Verifier credentials
|
||
DB-->>API: User valide
|
||
API->>API: Generer JWT
|
||
API->>R: Stocker session
|
||
API-->>F: 200 OK + { access_token, user }
|
||
F->>F: Stocker token
|
||
F-->>U: Connecte
|
||
end
|
||
|
||
rect rgb(255, 240, 200)
|
||
Note over U,TG: Login avec 2FA (Telegram active)
|
||
U->>F: Entrer credentials
|
||
F->>API: POST /auth/login
|
||
API->>DB: Verifier credentials + verifier 2FA active
|
||
DB-->>API: User valide, 2FA requise
|
||
API->>R: Stocker session_token (TTL 5min)
|
||
API->>TG: Envoyer code 6 chiffres via bot Telegram
|
||
API-->>F: 200 OK + { requires_2fa: true, session_token }
|
||
F-->>U: Afficher saisie du code Telegram
|
||
U->>F: Entrer code recu sur Telegram
|
||
F->>API: POST /auth/2fa/verify { session_token, code }
|
||
API->>R: Verifier code + session_token
|
||
R-->>API: Code valide
|
||
API->>API: Generer JWT
|
||
API->>R: Stocker session
|
||
API-->>F: 200 OK + { access_token, user }
|
||
F->>F: Stocker token
|
||
F-->>U: Connecte
|
||
end
|
||
|
||
rect rgb(230, 200, 200)
|
||
Note over U,R: Requete Authentifiee
|
||
U->>F: Action protegee
|
||
F->>API: Request + Authorization: Bearer JWT
|
||
API->>API: Valider JWT
|
||
API->>R: Verifier session active
|
||
R-->>API: Session valide
|
||
API->>DB: Executer action
|
||
DB-->>API: Resultat
|
||
API-->>F: Response
|
||
F-->>U: Resultat affiche
|
||
end
|
||
```
|
||
|
||
### 📦 Flux Complet d'une Commande
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant C as Client
|
||
participant F as Frontend
|
||
participant API as Backend
|
||
participant GPS as Service GPS
|
||
participant Q as Queue Service
|
||
participant L as Livreur
|
||
participant DB as PostgreSQL
|
||
participant R as Redis
|
||
|
||
rect rgb(220, 240, 220)
|
||
Note over C,R: 1. Ajout au panier
|
||
C->>F: Ajouter produit
|
||
F->>API: POST /panier/add {product_id, quantity}
|
||
API->>DB: Verifier stock (GetProductStockByID)
|
||
DB-->>API: Stock OK
|
||
API->>DB: Decrementer stock (DecrementProductStockByID)
|
||
API->>DB: Ajouter ligne panier
|
||
API-->>F: 201 Created
|
||
F-->>C: Panier mis a jour
|
||
Note over C,R: Si suppression panier → stock restitue automatiquement
|
||
end
|
||
|
||
rect rgb(230, 245, 230)
|
||
Note over C,R: 2. Checkout
|
||
C->>F: Valider panier
|
||
F->>API: POST /checkout {address}
|
||
API->>GPS: Geocoder adresse
|
||
GPS->>R: Check cache
|
||
R-->>GPS: Miss
|
||
GPS->>GPS: Appel Nominatim
|
||
GPS->>R: Cache resultat (7j)
|
||
GPS-->>API: Coordonnees
|
||
API->>DB: Creer commande
|
||
API->>Q: Chercher livreur proche
|
||
Q->>R: Get positions livreurs
|
||
Q->>Q: Calcul distances Haversine
|
||
Q->>GPS: Calculer ETA (TomTom)
|
||
Q-->>API: Livreur assigne
|
||
API->>DB: Update commande
|
||
API->>R: Ajouter a queue livreur
|
||
API-->>F: Commande creee + ETA
|
||
F-->>C: Confirmation
|
||
end
|
||
|
||
rect rgb(230, 230, 245)
|
||
Note over C,R: 3. Livraison
|
||
L->>API: GET /livreur/deliveries
|
||
API-->>L: Liste commandes
|
||
L->>API: POST /deliveries/:id/start
|
||
L->>API: POST /location/update {lat, lng}
|
||
API->>R: Update position
|
||
API->>R: Publish update
|
||
R-->>F: Notification position
|
||
F-->>C: Carte mise a jour
|
||
L->>API: PUT /deliveries/:id/status {en_route}
|
||
Note over L,API: ETA calcule et stocke dans Redis
|
||
L->>API: PUT /deliveries/:id/status {arrived}
|
||
L->>API: PUT /deliveries/:id/status {livre}
|
||
end
|
||
|
||
rect rgb(245, 230, 230)
|
||
Note over C,R: 4. Finalisation
|
||
C->>F: Approuver livraison
|
||
F->>API: POST /commands/:id/approve {rating}
|
||
API->>DB: Update statut approved
|
||
API->>R: Retirer de queue
|
||
API-->>F: Confirmation
|
||
F-->>C: Livraison terminee
|
||
end
|
||
```
|
||
|
||
### 💾 Schema de la Base de Donnees
|
||
|
||
```mermaid
|
||
erDiagram
|
||
CLIENTS {
|
||
int id PK
|
||
string username UK
|
||
string password
|
||
string nom
|
||
string prenom
|
||
string telephone UK
|
||
int command
|
||
int point
|
||
int points_zipette
|
||
float amende
|
||
int cancellations_count
|
||
timestamp created_at
|
||
}
|
||
|
||
ADMINS {
|
||
int id PK
|
||
string username UK
|
||
string password
|
||
string role
|
||
timestamp created_at
|
||
}
|
||
|
||
LIVREURS {
|
||
int id PK
|
||
string username UK
|
||
string password
|
||
string status
|
||
timestamp created_at
|
||
}
|
||
|
||
CABINES {
|
||
int id PK
|
||
string username UK
|
||
string password
|
||
timestamp created_at
|
||
}
|
||
|
||
PRODUCTS {
|
||
int id PK
|
||
string nom
|
||
string description
|
||
string category
|
||
int stock
|
||
float prix
|
||
timestamp created_at
|
||
}
|
||
|
||
PRODUCT_PRICES {
|
||
int id PK
|
||
int product_id FK
|
||
int quantity
|
||
float price
|
||
}
|
||
|
||
PRODUCT_MEDIA {
|
||
int id PK
|
||
int product_id FK
|
||
string type
|
||
string url
|
||
}
|
||
|
||
PANIER {
|
||
int id PK
|
||
string username FK
|
||
string name_product
|
||
string category
|
||
int quantity
|
||
float price
|
||
timestamp created_at
|
||
}
|
||
|
||
COMMANDS {
|
||
int id PK
|
||
string username FK
|
||
string status
|
||
float total
|
||
string adresse
|
||
float dest_latitude
|
||
float dest_longitude
|
||
string livreur_assign FK
|
||
timestamp created_at
|
||
timestamp updated_at
|
||
}
|
||
|
||
COMMAND_ITEMS {
|
||
int id PK
|
||
int command_id FK
|
||
string produit
|
||
int quantite
|
||
float prix
|
||
string status
|
||
string category
|
||
}
|
||
|
||
RATINGS {
|
||
int id PK
|
||
int command_id FK
|
||
string deliveryman
|
||
int rating
|
||
string comment
|
||
timestamp created_at
|
||
}
|
||
|
||
CLIENTS ||--o{ PANIER : "possede"
|
||
CLIENTS ||--o{ COMMANDS : "passe"
|
||
PRODUCTS ||--o{ PRODUCT_PRICES : "a"
|
||
PRODUCTS ||--o{ PRODUCT_MEDIA : "a"
|
||
COMMANDS ||--o{ COMMAND_ITEMS : "contient"
|
||
COMMANDS ||--o| RATINGS : "a"
|
||
LIVREURS ||--o{ COMMANDS : "livre"
|
||
```
|
||
|
||
### 🗄️ Structure Redis
|
||
|
||
```mermaid
|
||
graph LR
|
||
subgraph Sessions["🔐 Sessions"]
|
||
S1["session:{token}<br/>TTL: 5h (client) / 2h (admin)"]
|
||
end
|
||
|
||
subgraph Positions["📍 Positions GPS"]
|
||
P1["delivery:location:{username}<br/>TTL: 2h"]
|
||
P2["delivery:status:{username}<br/>TTL: 1h"]
|
||
end
|
||
|
||
subgraph Queues["📋 Queues Livraison"]
|
||
Q1["livreur:queue:{username}<br/>List of command_ids"]
|
||
Q2["queue:size:{username}<br/>Integer"]
|
||
end
|
||
|
||
subgraph Cache["💾 Cache"]
|
||
C1["geocode:cache:{hash}<br/>TTL: 7j"]
|
||
C2["command:destination:{id}<br/>TTL: 4h"]
|
||
C3["product:cache:{id}<br/>TTL: 1h"]
|
||
end
|
||
|
||
subgraph PubSub["�� Pub/Sub Channels"]
|
||
PS1["channel:position_updates"]
|
||
PS2["channel:order_status"]
|
||
PS3["channel:notifications"]
|
||
end
|
||
|
||
REDIS[(Redis Server)]
|
||
REDIS --- Sessions
|
||
REDIS --- Positions
|
||
REDIS --- Queues
|
||
REDIS --- Cache
|
||
REDIS --- PubSub
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 Déploiement Production
|
||
|
||
### Prérequis
|
||
|
||
- Docker + Docker Compose
|
||
- Certificats SSL : `fullchain.pem` + `privkey.pem` (Let's Encrypt recommandé)
|
||
- Fichier `.env` complété (voir `.env.example`)
|
||
|
||
### Structure des fichiers
|
||
|
||
```
|
||
docker/
|
||
├── backend/
|
||
│ ├── Dockerfile # Stage builder (Go), runtime, waf (nginx+ModSec)
|
||
│ ├── nginx.conf # Config nginx hardened (TLS 1.2/1.3, HSTS, CSP)
|
||
│ ├── custom-rules.conf # Règles ModSecurity personnalisées
|
||
│ └── entrypoint.sh # Entrypoint backend Go
|
||
├── frontend/
|
||
│ ├── Dockerfile # Build React/Vite + nginx interne
|
||
│ └── nginx.conf # Nginx interne (HTTP, sans TLS)
|
||
├── certs/ # Certificats SSL (non versionnés)
|
||
│ ├── fullchain.pem
|
||
│ └── privkey.pem
|
||
└── docker-compose-prod.yml
|
||
```
|
||
|
||
### Déploiement
|
||
|
||
```bash
|
||
# 1. Copier les certificats
|
||
mkdir -p docker/certs
|
||
cp /etc/letsencrypt/live/votre-domaine/fullchain.pem docker/certs/
|
||
cp /etc/letsencrypt/live/votre-domaine/privkey.pem docker/certs/
|
||
chmod 644 docker/certs/privkey.pem
|
||
|
||
# 2. Configurer les variables d'environnement
|
||
cp docker/.env.example docker/.env
|
||
# Éditer docker/.env avec vos valeurs
|
||
|
||
# 3. Lancer la stack
|
||
docker compose -f docker/docker-compose-prod.yml up -d --build
|
||
|
||
# 4. Vérifier les logs
|
||
docker compose -f docker/docker-compose-prod.yml logs -f waf
|
||
```
|
||
|
||
### Services Docker
|
||
|
||
| Service | Image | Rôle |
|
||
|---------|-------|------|
|
||
| `waf` | owasp/modsecurity-crs:nginx-alpine | Point d'entrée HTTPS (ports 80/443) |
|
||
| `backend` | Go 1.24 alpine | API REST (port 8080 interne) |
|
||
| `frontend` | nginx:alpine | SPA React (port 80 interne) |
|
||
| `postgres` | postgres:16-alpine | Base de données |
|
||
| `redis` | redis:7-alpine | Cache + sessions + queues |
|
||
|
||
### Variables d'environnement requises
|
||
|
||
```bash
|
||
DB_PASSWORD= # Mot de passe PostgreSQL
|
||
USER_JWT_SECRET= # Secret JWT clients (min 32 chars)
|
||
ADMIN_JWT_SECRET= # Secret JWT admin/livreur/cabine (min 32 chars)
|
||
REDIS_PASSWORD= # Mot de passe Redis
|
||
TOMTOM_API_KEY= # Clé API TomTom
|
||
SESSION_SECRET= # Secret sessions
|
||
TELEGRAM_WEBHOOK_URL= # URL webhook Telegram
|
||
TELEGRAM_WEBHOOK_SECRET= # Secret webhook Telegram
|
||
NOWPAYMENTS_IPN_SECRET= # Secret IPN NowPayments
|
||
```
|
||
|
||
---
|
||
|
||
## 🔔 Notifications Push & Telegram
|
||
|
||
### Push Notifications (Expo)
|
||
|
||
Le système utilise Expo Push Notifications pour envoyer des notifications aux applications mobiles (client et livreur). Les tokens Expo sont envoyés directement via l'API Expo depuis le backend — il n'y a pas d'endpoint REST dédié à l'enregistrement du push token.
|
||
|
||
**Flux :**
|
||
1. L'app mobile obtient un `ExponentPushToken` via `expo-notifications`
|
||
2. Le backend envoie les notifications via `sendExpoPush()` dans `db/db_notifications.go`
|
||
3. Expo relay la notification vers le device cible (iOS/Android)
|
||
|
||
**Types de notifications envoyées :**
|
||
- `assigned` — Commande assignée à un livreur
|
||
- `en_route` — Livreur en route (avec ETA)
|
||
- `arrived` — Livreur arrivé (bouton "Le livreur est là")
|
||
- `livre` — Commande livrée
|
||
- `ready_pickup` — Notification cabine "descendez chercher"
|
||
- `address_proposal` — Proposition de changement d'adresse
|
||
|
||
### Notifications Telegram
|
||
|
||
Les admins et livreurs peuvent lier leur compte Telegram pour recevoir des alertes.
|
||
|
||
#### Générer un token de liaison (Admin)
|
||
|
||
```bash
|
||
POST /api/v2/admin/protected/telegram/link-token
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Réponse:**
|
||
```json
|
||
{
|
||
"link_token": "abc123xyz",
|
||
"expires_in": 300,
|
||
"bot_url": "https://t.me/votre_bot?start=abc123xyz"
|
||
}
|
||
```
|
||
|
||
#### Générer un token de liaison (Livreur)
|
||
|
||
```bash
|
||
POST /api/v1/livreur/telegram/link-token
|
||
Authorization: Bearer <livreur_token>
|
||
```
|
||
|
||
#### Statut Telegram (Livreur)
|
||
|
||
```bash
|
||
GET /api/v1/livreur/telegram/status
|
||
Authorization: Bearer <livreur_token>
|
||
```
|
||
|
||
#### Délier Telegram
|
||
|
||
```bash
|
||
DELETE /api/v1/livreur/telegram/unlink
|
||
Authorization: Bearer <livreur_token>
|
||
```
|
||
|
||
---
|
||
|
||
## 💸 Paiements Crypto
|
||
|
||
Le système intègre NowPayments pour les paiements en cryptomonnaie.
|
||
|
||
### Webhook IPN (Instant Payment Notification)
|
||
|
||
Endpoint public sécurisé par signature HMAC-SHA512.
|
||
|
||
```bash
|
||
POST /api/v1/webhooks/nowpayments
|
||
Content-Type: application/json
|
||
x-nowpayments-sig: <hmac_sha512_signature>
|
||
```
|
||
|
||
Le webhook met automatiquement à jour le statut de paiement de la commande correspondante.
|
||
|
||
#### Vérifier le statut de paiement d'une commande
|
||
|
||
```bash
|
||
GET /api/v1/commands/:id/payment-status
|
||
Authorization: Bearer <client_token>
|
||
```
|
||
|
||
**Réponse:**
|
||
```json
|
||
{
|
||
"command_id": 1234,
|
||
"payment_status": "confirmed",
|
||
"amount_paid": 25.00,
|
||
"currency": "USDT"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔐 Authentication
|
||
|
||
### Register Client
|
||
|
||
**Créer un nouveau compte client**
|
||
|
||
```bash
|
||
POST /api/v1/auth/register
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"username": "jean_dupont",
|
||
"password": "SecurePass123!",
|
||
"nom": "Dupont",
|
||
"prenom": "Jean",
|
||
"telephone": "+33612345678"
|
||
}
|
||
```
|
||
|
||
**Réponse (201 Created):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Client créé avec succès",
|
||
"client": {
|
||
"id": 42,
|
||
"username": "jean_dupont",
|
||
"nom": "Dupont",
|
||
"prenom": "Jean",
|
||
"telephone": "+33612345678",
|
||
"command": 0,
|
||
"point": 0,
|
||
"points_zipette": 0,
|
||
"amende": 0.0,
|
||
"cancellations_count": 0,
|
||
"created_at": "2025-01-18T14:30:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - Données invalides (validation échouée)
|
||
- `409` - Username ou téléphone déjà utilisé
|
||
|
||
---
|
||
|
||
### Login Client
|
||
|
||
**Se connecter et obtenir un token JWT**
|
||
|
||
```bash
|
||
POST /api/v1/auth/login
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"username": "jean_dupont",
|
||
"password": "SecurePass123!"
|
||
}
|
||
```
|
||
|
||
**Réponse standard (200 OK) — 2FA désactivée:**
|
||
```json
|
||
{
|
||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"token_type": "Bearer",
|
||
"expires_in": 18000,
|
||
"user": {
|
||
"username": "jean_dupont",
|
||
"role": "client"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Réponse avec 2FA (200 OK) — quand 2FA activée par le client ET Telegram lié ET admin l'a activée:**
|
||
```json
|
||
{
|
||
"requires_2fa": true,
|
||
"session_token": "550e8400-e29b-41d4-a716-446655440000"
|
||
}
|
||
```
|
||
> Un code à 6 chiffres est envoyé automatiquement sur le compte Telegram lié. Le `session_token` expire dans **5 minutes**.
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - Données manquantes
|
||
- `401` - Identifiants incorrects
|
||
|
||
---
|
||
|
||
### Vérifier le code 2FA
|
||
|
||
**Valider le code Telegram reçu pour finaliser la connexion**
|
||
|
||
```bash
|
||
POST /api/v1/auth/2fa/verify
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"session_token": "550e8400-e29b-41d4-a716-446655440000",
|
||
"code": "483721"
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"token_type": "Bearer",
|
||
"expires_in": 18000,
|
||
"user": {
|
||
"username": "jean_dupont",
|
||
"role": "client"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - `session_token` ou `code` manquant
|
||
- `401` - Code incorrect ou session expirée (TTL 5 min)
|
||
- `429` - Trop de tentatives (rate limiting)
|
||
|
||
---
|
||
|
||
### Statut 2FA du compte
|
||
|
||
**Obtenir l'état de la 2FA pour le compte connecté**
|
||
|
||
```bash
|
||
GET /api/v1/two-fa/status
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"two_fa_enabled": true,
|
||
"telegram_linked": true,
|
||
"admin_2fa_enabled": true
|
||
}
|
||
```
|
||
|
||
| Champ | Description |
|
||
|-------|-------------|
|
||
| `two_fa_enabled` | La 2FA est activée sur ce compte client |
|
||
| `telegram_linked` | Un compte Telegram est lié (prérequis pour activer) |
|
||
| `admin_2fa_enabled` | L'admin a activé la 2FA dans les paramètres globaux |
|
||
|
||
---
|
||
|
||
### Activer / Désactiver la 2FA
|
||
|
||
**Basculer l'état de la 2FA sur son propre compte**
|
||
|
||
```bash
|
||
POST /api/v1/two-fa/toggle
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"enabled": true
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"two_fa_enabled": true
|
||
}
|
||
```
|
||
|
||
**Prérequis pour activer (`"enabled": true`):**
|
||
1. Le client doit avoir lié son compte Telegram (via `/api/v1/auth/link-telegram`)
|
||
2. L'administrateur doit avoir activé `telegram_2fa_enabled` dans les paramètres globaux
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - Telegram non lié (impossible d'activer sans compte Telegram)
|
||
- `403` - La 2FA n'est pas autorisée par l'administrateur
|
||
- `401` - Non authentifié
|
||
|
||
> **Interface utilisateur:** Sur le frontend web (page profil) et l'app mobile, un toggle permet d'activer/désactiver la 2FA directement depuis les paramètres du compte.
|
||
|
||
---
|
||
|
||
### Logout Client
|
||
|
||
**Se déconnecter (invalider le token)**
|
||
|
||
```bash
|
||
POST /api/v1/auth/logout
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Déconnexion réussie"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🛒 API Client (v1)
|
||
|
||
**Toutes les routes clients nécessitent le header:**
|
||
```
|
||
Authorization: Bearer <client_token>
|
||
```
|
||
|
||
### Produits (Public - Pas d'auth requis)
|
||
|
||
#### Liste des produits
|
||
|
||
```bash
|
||
GET /api/v1/products
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"products": [
|
||
{
|
||
"id": 1,
|
||
"nom": "Pizza Margherita",
|
||
"description": "Pizza traditionnelle avec tomate, mozzarella et basilic",
|
||
"category": "pizza",
|
||
"stock": 50,
|
||
"prix": 12.50,
|
||
"prices": [
|
||
{
|
||
"id": 1,
|
||
"product_id": 1,
|
||
"quantity": 1,
|
||
"price": 12.50
|
||
}
|
||
],
|
||
"media": [
|
||
{
|
||
"id": 1,
|
||
"product_id": 1,
|
||
"type": "image",
|
||
"url": "/uploads/pizza_margherita.jpg"
|
||
}
|
||
],
|
||
"created_at": "2025-01-18T00:00:00Z"
|
||
}
|
||
],
|
||
"total": 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Produit par ID
|
||
|
||
```bash
|
||
GET /api/v1/products/:id
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"product": {
|
||
"id": 1,
|
||
"nom": "Pizza Margherita",
|
||
"description": "Pizza traditionnelle avec tomate, mozzarella et basilic",
|
||
"category": "pizza",
|
||
"stock": 50,
|
||
"prix": 12.50,
|
||
"prices": [...],
|
||
"media": [...],
|
||
"created_at": "2025-01-18T00:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `404` - Produit non trouvé
|
||
|
||
---
|
||
|
||
### Gestion du Panier
|
||
|
||
> **Note stock :** Le stock est décrémenté dès l'ajout au panier et restitué automatiquement lors de la suppression d'un article ou du vidage du panier.
|
||
|
||
#### Ajouter au panier
|
||
|
||
```bash
|
||
POST /api/v1/panier/add
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"name_product": "Pizza Margherita",
|
||
"category": "pizza",
|
||
"quantity": 2
|
||
}
|
||
```
|
||
|
||
**Réponse (201 Created):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Produit ajouté au panier avec succès",
|
||
"panier": {
|
||
"id": 15,
|
||
"username": "jean_dupont",
|
||
"name_product": "Pizza Margherita",
|
||
"category": "pizza",
|
||
"quantity": 2,
|
||
"price": 25.00,
|
||
"created_at": "2025-01-18T14:35:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - Données invalides ou stock insuffisant
|
||
- `401` - Non authentifié
|
||
- `404` - Produit non trouvé
|
||
|
||
---
|
||
|
||
#### Voir mon panier
|
||
|
||
```bash
|
||
GET /api/v1/panier/:username
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Panier récupéré avec succès",
|
||
"panier": [
|
||
{
|
||
"id": 15,
|
||
"username": "jean_dupont",
|
||
"name_product": "Pizza Margherita",
|
||
"category": "pizza",
|
||
"quantity": 2,
|
||
"price": 25.00,
|
||
"created_at": "2025-01-18T14:35:00Z"
|
||
}
|
||
],
|
||
"count": 1,
|
||
"total_amount": 25.00
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `403` - Accès au panier d'un autre utilisateur
|
||
- `404` - Utilisateur non trouvé
|
||
|
||
---
|
||
|
||
#### Supprimer du panier
|
||
|
||
```bash
|
||
DELETE /api/v1/panier/remove
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"id": 15
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Produit supprimé du panier avec succès",
|
||
"item_id": 15,
|
||
"stock_released": true
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `403` - Tentative de supprimer l'article d'un autre utilisateur
|
||
- `404` - Article non trouvé
|
||
|
||
---
|
||
|
||
#### Vider le panier
|
||
|
||
```bash
|
||
DELETE /api/v1/panier/clear
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Panier vidé avec succès",
|
||
"stock_released": 3
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Commandes
|
||
|
||
#### Valider le panier (Checkout avec Auto-assignation)
|
||
|
||
```bash
|
||
POST /api/v1/checkout
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"delivery_address": "15 Rue de la Paix, 75002 Paris, France"
|
||
}
|
||
```
|
||
|
||
**Réponse avec auto-assignation (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Commande créée et livreur assigné automatiquement",
|
||
"command_id": 1234,
|
||
"delivery_address": "15 Rue de la Paix, 75002 Paris, France",
|
||
"status": "assigned",
|
||
"auto_assigned": true,
|
||
"assigned_to": {
|
||
"username": "john_deliveryman",
|
||
"distance_km": 1.5,
|
||
"travel_time": 8
|
||
}
|
||
}
|
||
```
|
||
|
||
**Réponse sans auto-assignation (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Commande créée - En attente d'assignation",
|
||
"command_id": 1234,
|
||
"delivery_address": "15 Rue de la Paix, 75002 Paris, France",
|
||
"status": "pending",
|
||
"auto_assigned": false
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - Panier vide ou adresse manquante
|
||
- `401` - Non authentifié
|
||
- `500` - Erreur création commande
|
||
|
||
---
|
||
|
||
#### Mes commandes avec suivi
|
||
|
||
```bash
|
||
GET /api/v1/my-commands
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"commands": [
|
||
{
|
||
"id": 1234,
|
||
"username": "jean_dupont",
|
||
"status": "assigned",
|
||
"total": 25.00,
|
||
"delivery_address": "15 Rue de la Paix, 75002 Paris",
|
||
"livreur_assign": "john_deliveryman",
|
||
"created_at": "2025-01-18T14:40:00Z",
|
||
"updated_at": "2025-01-18T14:42:00Z",
|
||
"tracking": {
|
||
"eta_minutes": 25,
|
||
"estimated_arrival": "15:05",
|
||
"deliveryman_status": "en_route",
|
||
"queue_position": 2
|
||
}
|
||
}
|
||
],
|
||
"total": 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Statut temps réel d'une commande
|
||
|
||
```bash
|
||
GET /api/v1/commands/:id/status
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"command_id": 1234,
|
||
"status": "en_route",
|
||
"deliveryman": "john_deliveryman",
|
||
"updated_at": "2025-01-18T15:20:00Z"
|
||
}
|
||
```
|
||
|
||
**Statuts possibles:**
|
||
|
||
| Statut | Description | Qui peut le définir |
|
||
|--------|-------------|---------------------|
|
||
| `pending` | En attente d'assignation | Système (checkout) |
|
||
| `assigned` | Assignée à un livreur | Système (auto-assign), Admin |
|
||
| `en_route` | Livreur en chemin | Livreur, Admin |
|
||
| `arrived` | Livreur arrivé à destination | Livreur, Admin, Cabine (bouton "Le livreur est là") |
|
||
| `livre` | Livrée, en attente d'approbation | Livreur (validation GPS), Admin |
|
||
| `approved` | Approuvée et finalisée | Client (approve), Admin, Cabine |
|
||
| `cancelled` | Annulée | Client, Livreur, Admin |
|
||
|
||
---
|
||
|
||
#### Suivi détaillé avec ETA
|
||
|
||
```bash
|
||
GET /api/v1/commands/:id/tracking
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"command_id": 1234,
|
||
"status": "en_route",
|
||
"deliveryman": "john_deliveryman",
|
||
"eta": {
|
||
"minutes": 22,
|
||
"estimated_arrival": "15:02",
|
||
"arrival_time_unix": 1705669320,
|
||
"updated_at": "2025-01-18T14:40:00Z"
|
||
},
|
||
"queue_position": 2,
|
||
"total_queue_size": 5
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### ETA de livraison
|
||
|
||
```bash
|
||
GET /api/v1/commands/:id/eta
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"command_id": 1234,
|
||
"eta_minutes": 22,
|
||
"estimated_arrival": "15:02:00",
|
||
"queue_position": 2,
|
||
"updated_at": "2025-01-18T14:40:00Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Approuver une livraison
|
||
|
||
```bash
|
||
POST /api/v1/commands/:id/approve
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"rating": 5,
|
||
"comment": "Excellent service, livreur très professionnel !"
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Livraison approuvée avec succès",
|
||
"command_id": 1234,
|
||
"status": "approved",
|
||
"rating": {
|
||
"deliveryman": "john_deliveryman",
|
||
"rating": 5,
|
||
"comment": "Excellent service, livreur très professionnel !",
|
||
"created_at": "2025-01-18T15:05:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Annuler une commande
|
||
|
||
```bash
|
||
POST /api/v1/commands/:id/cancel
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"reason": "Changement de plans"
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Commande annulée avec succès",
|
||
"command_id": 1234,
|
||
"penalty_applied": true,
|
||
"penalty_details": {
|
||
"cancellations_count": 1,
|
||
"penalty_amount": 20.0,
|
||
"warning": "Pénalité appliquée pour annulation"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Profil et Historique
|
||
|
||
#### Mettre à jour mon profil
|
||
|
||
```bash
|
||
PUT /api/v1/profile/update
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"nom": "Nouveau Nom",
|
||
"prenom": "Nouveau Prénom",
|
||
"telephone": "+33687654321",
|
||
"password": "NewSecurePass456!"
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Profil mis à jour avec succès",
|
||
"client": {
|
||
"id": 42,
|
||
"username": "jean_dupont",
|
||
"nom": "Nouveau Nom",
|
||
"prenom": "Nouveau Prénom",
|
||
"telephone": "+33687654321",
|
||
"updated_at": "2025-01-18T15:30:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Mes pénalités
|
||
|
||
```bash
|
||
GET /api/v1/penalties
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"client": {
|
||
"username": "jean_dupont",
|
||
"amende": 20.0,
|
||
"cancellations_count": 1,
|
||
"last_penalty_reason": "Annulation tardive"
|
||
},
|
||
"penalty_scale": {
|
||
"1st": 20.0,
|
||
"2nd": 50.0,
|
||
"3rd": 100.0,
|
||
"4th+": 150.0
|
||
},
|
||
"next_penalty": 50.0,
|
||
"warning": "La prochaine annulation entraînera une pénalité de 50 points"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 👨💼 API Admin (v2)
|
||
|
||
**Toutes les routes admin nécessitent le header:**
|
||
```
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
### Authentication Admin
|
||
|
||
#### Login Admin
|
||
|
||
```bash
|
||
POST /api/v2/admin/auth/login
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"username": "admin_master",
|
||
"password": "AdminSecure123!"
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"token_type": "Bearer",
|
||
"expires_in": 7200,
|
||
"user": {
|
||
"username": "admin_master",
|
||
"role": "admin"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Gestion des Commandes
|
||
|
||
#### Liste des commandes
|
||
|
||
```bash
|
||
GET /api/v2/admin/protected/orders
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"orders": [
|
||
{
|
||
"id": 1234,
|
||
"username": "jean_dupont",
|
||
"status": "assigned",
|
||
"total": 25.00,
|
||
"delivery_address": "15 Rue de la Paix, 75002 Paris",
|
||
"livreur_assign": "john_deliveryman",
|
||
"dest_latitude": 48.8698,
|
||
"dest_longitude": 2.3311,
|
||
"created_at": "2025-01-18T14:40:00Z",
|
||
"updated_at": "2025-01-18T14:42:00Z"
|
||
}
|
||
],
|
||
"total": 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Modifier le statut d'une commande
|
||
|
||
```bash
|
||
PUT /api/v2/admin/protected/orders/:id/status
|
||
Authorization: Bearer <admin_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{ "status": "arrived" }
|
||
```
|
||
|
||
**Statuts acceptés:** `pending`, `assigned`, `en_route`, `arrived`, `livre`, `approved`, `cancelled`
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Statut mis à jour",
|
||
"command_id": 1234,
|
||
"new_status": "arrived"
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - Statut invalide
|
||
- `403` - Rôle insuffisant (admin ou cabine requis)
|
||
- `404` - Commande non trouvée
|
||
|
||
---
|
||
|
||
#### Notifier le client de descendre
|
||
|
||
Envoie une notification (Telegram + in-app) au client pour récupérer sa commande, puis passe le statut en `arrived`.
|
||
|
||
```bash
|
||
POST /api/v2/admin/protected/orders/:id/notify-client
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Client notifié",
|
||
"client_username": "jean_dupont"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Assignation automatique GPS
|
||
|
||
```bash
|
||
POST /api/v2/admin/protected/orders/:id/auto-assign
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Commande assignée automatiquement",
|
||
"command_id": 1234,
|
||
"assigned_to": "john_deliveryman",
|
||
"distance": {
|
||
"km": 1.5,
|
||
"eta_minutes": 8
|
||
},
|
||
"queue_position": 3
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `404` - Commande non trouvée
|
||
- `400` - Aucun livreur disponible
|
||
- `409` - Commande déjà assignée
|
||
|
||
---
|
||
|
||
#### Assigner toutes les commandes en attente
|
||
|
||
```bash
|
||
POST /api/v2/admin/protected/orders/auto-assign-all
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Auto-assignation effectuée",
|
||
"assigned_count": 5,
|
||
"failed_count": 0,
|
||
"details": [
|
||
{
|
||
"command_id": 1234,
|
||
"assigned_to": "john_deliveryman",
|
||
"distance_km": 1.5,
|
||
"eta_minutes": 8
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Gestion des Livreurs
|
||
|
||
#### Livreurs disponibles
|
||
|
||
```bash
|
||
GET /api/v2/admin/protected/delivery-persons
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"delivery_persons": [
|
||
{
|
||
"username": "john_deliveryman",
|
||
"status": "available",
|
||
"current_command": 0,
|
||
"queue_size": 2,
|
||
"location": {
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522,
|
||
"last_update": "2025-01-18T16:30:00Z"
|
||
}
|
||
}
|
||
],
|
||
"total": 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Position d'un livreur
|
||
|
||
```bash
|
||
GET /api/v2/admin/protected/delivery-persons/:username/location
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"deliveryman": "john_deliveryman",
|
||
"location": {
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522,
|
||
"last_update": "2025-01-18T16:25:00Z"
|
||
},
|
||
"status": "available"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Queues des livreurs
|
||
|
||
```bash
|
||
GET /api/v2/admin/protected/delivery/queues
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"queues": {
|
||
"john_deliveryman": {
|
||
"queue_size": 3,
|
||
"commands": [1234, 1235, 1236],
|
||
"can_accept_more": true,
|
||
"capacity": "3/10",
|
||
"status": "available"
|
||
}
|
||
},
|
||
"total_pending": 3
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Gestion des Produits
|
||
|
||
#### Créer un produit
|
||
|
||
```bash
|
||
POST /api/v2/admin/protected/products
|
||
Authorization: Bearer <admin_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"nom": "Pizza Pepperoni",
|
||
"category": "pizza",
|
||
"description": "Pizza avec pepperoni et mozzarella",
|
||
"stock": 30,
|
||
"prix": 14.50
|
||
}
|
||
```
|
||
|
||
**Réponse (201 Created):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Produit créé avec succès",
|
||
"product": {
|
||
"id": 10,
|
||
"nom": "Pizza Pepperoni",
|
||
"category": "pizza",
|
||
"description": "Pizza avec pepperoni et mozzarella",
|
||
"stock": 30,
|
||
"prix": 14.50,
|
||
"created_at": "2025-01-18T16:10:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Système de Pénalités
|
||
|
||
#### Appliquer une pénalité
|
||
|
||
```bash
|
||
POST /api/v2/admin/protected/penalty
|
||
Authorization: Bearer <admin_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"username": "jean_dupont",
|
||
"amount": 50.0,
|
||
"reason": "Comportement inapproprié"
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Pénalité appliquée",
|
||
"client": "jean_dupont",
|
||
"penalty": {
|
||
"amount": 50.0,
|
||
"reason": "Comportement inapproprié",
|
||
"applied_at": "2025-01-18T17:00:00Z"
|
||
},
|
||
"total_amende": 70.0
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🏪 API Cabine (v1)
|
||
|
||
**Toutes les routes cabine nécessitent le header:**
|
||
```
|
||
Authorization: Bearer <cabine_token>
|
||
```
|
||
|
||
#### Articles d'une commande
|
||
|
||
```bash
|
||
GET /api/v1/cabine/commands/:id/items
|
||
Authorization: Bearer <cabine_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"command_id": 1234,
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"command_id": 1234,
|
||
"produit": "Pizza Margherita",
|
||
"quantite": 2,
|
||
"prix": 25.0,
|
||
"status": "pending",
|
||
"category": "pizza"
|
||
}
|
||
],
|
||
"total": 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Modifier le statut d'une commande
|
||
|
||
```bash
|
||
PUT /api/v1/cabine/commands/:id/status
|
||
Authorization: Bearer <cabine_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{ "status": "arrived" }
|
||
```
|
||
|
||
**Statuts acceptés:** `pending`, `assigned`, `en_route`, `arrived`, `livre`, `approved`, `cancelled`
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Statut mis à jour",
|
||
"command_id": 1234,
|
||
"new_status": "arrived"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Notifier le client de descendre
|
||
|
||
```bash
|
||
POST /api/v1/cabine/commands/:id/notify-client
|
||
Authorization: Bearer <cabine_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Client notifié",
|
||
"client_username": "jean_dupont"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Modifier le statut d'un article
|
||
|
||
```bash
|
||
PUT /api/v1/cabine/items/:item_id/status
|
||
Authorization: Bearer <cabine_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"status": "prepared"
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Statut mis à jour",
|
||
"item": {
|
||
"id": 1,
|
||
"command_id": 1234,
|
||
"status": "prepared",
|
||
"updated_at": "2025-01-18T17:15:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Statuts disponibles:** `pending`, `prepared`, `packed`, `ready`
|
||
|
||
---
|
||
|
||
#### Confirmer réception (approbation finale)
|
||
|
||
```bash
|
||
POST /api/v1/cabine/commands/:id/confirm-reception
|
||
Authorization: Bearer <cabine_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Réception confirmée",
|
||
"command_id": 1234,
|
||
"new_status": "approved"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Assigner un livreur
|
||
|
||
```bash
|
||
POST /api/v1/cabine/commands/:id/assign
|
||
Authorization: Bearer <cabine_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{ "username": "john_deliveryman" }
|
||
```
|
||
|
||
---
|
||
|
||
#### Livreurs disponibles
|
||
|
||
```bash
|
||
GET /api/v1/cabine/all/deliveryman
|
||
Authorization: Bearer <cabine_token>
|
||
```
|
||
|
||
---
|
||
|
||
## 🚚 API Livreur (v1)
|
||
|
||
**Toutes les routes livreur nécessitent le header:**
|
||
```
|
||
Authorization: Bearer <livreur_token>
|
||
```
|
||
|
||
### Livraisons
|
||
|
||
#### Mes livraisons (données filtrées)
|
||
|
||
```bash
|
||
GET /api/v1/livreur/deliveries
|
||
Authorization: Bearer <livreur_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"deliveries": [
|
||
{
|
||
"id": 1234,
|
||
"status": "assigned",
|
||
"adresse": "15 Rue de la Paix, 75002 Paris",
|
||
"total_prix": 25.0,
|
||
"created_at": "2025-01-18T14:40:00Z",
|
||
"client_info": {
|
||
"nom": "Dupont",
|
||
"prenom": "Jean"
|
||
},
|
||
"items": [
|
||
{
|
||
"produit": "Pizza Margherita",
|
||
"quantite": 2,
|
||
"prix": 25.0
|
||
}
|
||
],
|
||
"items_count": 1,
|
||
"eta": {
|
||
"minutes": 25,
|
||
"estimated_arrival": "15:05"
|
||
}
|
||
}
|
||
],
|
||
"count": 1
|
||
}
|
||
```
|
||
|
||
**Note:** Les données sensibles comme le téléphone client sont filtrées pour les livreurs.
|
||
|
||
---
|
||
|
||
#### Mettre à jour le statut d'une livraison
|
||
|
||
```bash
|
||
PUT /api/v1/livreur/deliveries/:id/status
|
||
Authorization: Bearer <livreur_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"status": "en_route",
|
||
"notes": "En route vers le client",
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Statut mis à jour",
|
||
"command_id": 1234,
|
||
"status": "en_route",
|
||
"eta_minutes": 15,
|
||
"eta_message": "Arrivée prévue dans 15 minutes"
|
||
}
|
||
```
|
||
|
||
**Statuts valides pour livreur (via cet endpoint) :**
|
||
- `en_route` — En route vers le client (déclenche calcul ETA + notification Telegram avec ETA)
|
||
- `arrived` — Arrivé à destination (notifie le client de descendre)
|
||
- `livre` — Livré (via validation GPS uniquement, voir endpoint `force-validate`)
|
||
- `cancelled` — Annulation par le livreur
|
||
|
||
> **Note :** Le passage à `livre` via ce endpoint requiert des coordonnées GPS valides pour la validation de proximité.
|
||
|
||
---
|
||
|
||
### Position GPS
|
||
|
||
#### Mettre à jour ma position
|
||
|
||
```bash
|
||
POST /api/v1/livreur/location/update
|
||
Authorization: Bearer <livreur_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Position mise à jour",
|
||
"location": {
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522,
|
||
"updated_at": "2025-01-18T17:35:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Ma position actuelle
|
||
|
||
```bash
|
||
GET /api/v1/livreur/location
|
||
Authorization: Bearer <livreur_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"location": {
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522,
|
||
"last_update": "2025-01-18T17:35:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Statut et Queue
|
||
|
||
#### Mettre à jour mon statut
|
||
|
||
```bash
|
||
POST /api/v1/livreur/update/status
|
||
Authorization: Bearer <livreur_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requête:**
|
||
```json
|
||
{
|
||
"status": "available"
|
||
}
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Statut mis à jour",
|
||
"status": {
|
||
"username": "john_deliveryman",
|
||
"status": "available",
|
||
"updated_at": "2025-01-18T17:40:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Statuts disponibles:** `available`, `busy`, `offline`
|
||
|
||
---
|
||
|
||
#### Ma queue de livraisons
|
||
|
||
```bash
|
||
GET /api/v1/livreur/queue
|
||
Authorization: Bearer <livreur_token>
|
||
```
|
||
|
||
**Réponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"deliveryman": "john_deliveryman",
|
||
"queue_size": 3,
|
||
"commands": [
|
||
{
|
||
"position": 1,
|
||
"command_id": 1234,
|
||
"address": "15 Rue de la Paix, 75002 Paris",
|
||
"estimated_eta": 12,
|
||
"total_prix": 25.0
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🗺️ Systeme GPS Integre
|
||
|
||
Le systeme GPS est au coeur de la plateforme de livraison. Il permet l'assignation automatique des livreurs, le calcul d'ETA en temps reel, et le suivi des livraisons.
|
||
|
||
### Architecture GPS
|
||
|
||
#### Flux GPS Complet
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[1. Commande creee par le Client] --> B[2. Geocodage adresse]
|
||
B --> |Nominatim API| C[(Cache Redis 7 jours)]
|
||
C --> D[3. Worker Auto-Assignation]
|
||
D --> |Toutes les 5 min| E[4. Recherche livreur le plus proche]
|
||
E --> |Haversine + TomTom| F[5. Calcul ETA avec trafic reel]
|
||
F --> G[6. Assignation a la queue du livreur]
|
||
G --> H[7. Livreur accepte et met a jour sa position]
|
||
H --> |Redis Pub/Sub| I[8. Client/Admin recoivent les mises a jour]
|
||
I --> J[9. Navigation vers destination]
|
||
J --> |TomTom Routing| K[10. Livraison terminee]
|
||
```
|
||
|
||
#### Architecture des Services GPS
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph Frontend
|
||
MAP[TomTomMap Component]
|
||
UI[Interface Utilisateur]
|
||
end
|
||
|
||
subgraph Backend
|
||
GEO[Service Geocodage]
|
||
DIST[Service Distance]
|
||
ETA[Service ETA]
|
||
ASSIGN[Service Auto-Assignation]
|
||
end
|
||
|
||
subgraph APIs Externes
|
||
NOM[Nominatim API]
|
||
TOM[TomTom API]
|
||
end
|
||
|
||
subgraph Stockage
|
||
REDIS[(Redis Cache)]
|
||
PG[(PostgreSQL)]
|
||
end
|
||
|
||
UI --> MAP
|
||
MAP --> TOM
|
||
GEO --> NOM
|
||
GEO --> REDIS
|
||
DIST --> GEO
|
||
ETA --> TOM
|
||
ETA --> DIST
|
||
ASSIGN --> DIST
|
||
ASSIGN --> ETA
|
||
ASSIGN --> PG
|
||
ASSIGN --> REDIS
|
||
```
|
||
|
||
### Services GPS Utilises
|
||
|
||
| Service | Utilisation | Cache |
|
||
|---------|-------------|-------|
|
||
| **Nominatim** (OpenStreetMap) | Geocodage d'adresses | Redis 7 jours |
|
||
| **TomTom Routing API** | ETA avec trafic reel, itineraires | Non |
|
||
| **Haversine** (local) | Calcul de distance a vol d'oiseau | Non |
|
||
|
||
### Configuration Requise
|
||
|
||
**Variables d'environnement Backend:**
|
||
```bash
|
||
TOMTOM_API_KEY=<votre_cle_api_tomtom>
|
||
```
|
||
|
||
**Variables d'environnement Frontend:**
|
||
```bash
|
||
VITE_TOMTOM_API_KEY=<votre_cle_api_tomtom>
|
||
```
|
||
|
||
> **Note:** Obtenez une cle API TomTom gratuite sur [developer.tomtom.com](https://developer.tomtom.com/)
|
||
|
||
---
|
||
|
||
### Geocodage d'Adresses
|
||
|
||
Le geocodage convertit une adresse textuelle en coordonnees GPS (latitude/longitude).
|
||
|
||
#### Geocoder une adresse
|
||
|
||
```bash
|
||
POST /api/v1/geocode
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requete:**
|
||
```json
|
||
{
|
||
"address": "15 Rue de la Paix, 75002 Paris, France"
|
||
}
|
||
```
|
||
|
||
**Reponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"address": "15 Rue de la Paix, 75002 Paris, France",
|
||
"coordinates": {
|
||
"latitude": 48.8698,
|
||
"longitude": 2.3311
|
||
},
|
||
"cached": false
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - Adresse manquante ou invalide
|
||
- `404` - Adresse non trouvee (geocodage echoue)
|
||
|
||
---
|
||
|
||
#### Valider une adresse
|
||
|
||
Verifie si une adresse peut etre geocodee sans la stocker.
|
||
|
||
```bash
|
||
POST /api/v1/validate-address
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requete:**
|
||
```json
|
||
{
|
||
"address": "15 Rue de la Paix, 75002 Paris"
|
||
}
|
||
```
|
||
|
||
**Reponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"valid": true,
|
||
"address": "15 Rue de la Paix, 75002 Paris",
|
||
"coordinates": {
|
||
"latitude": 48.8698,
|
||
"longitude": 2.3311
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Calcul de Distance et ETA
|
||
|
||
#### Formule Haversine
|
||
|
||
Le systeme utilise la formule Haversine pour calculer la distance a vol d'oiseau entre deux points GPS:
|
||
|
||
```
|
||
a = sin²(Δlat/2) + cos(lat1) × cos(lat2) × sin²(Δlon/2)
|
||
c = 2 × atan2(√a, √(1-a))
|
||
distance = R × c
|
||
|
||
Ou R = 6371 km (rayon de la Terre)
|
||
```
|
||
|
||
#### ETA avec Trafic (TomTom)
|
||
|
||
L'ETA est calcule en utilisant l'API TomTom qui prend en compte:
|
||
- Les conditions de trafic en temps reel
|
||
- Les incidents routiers
|
||
- Les travaux
|
||
- L'heure de la journee
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[Demande ETA] --> B{TomTom API disponible?}
|
||
B -->|Oui| C[Appel TomTom Routing API]
|
||
C --> D[ETA avec trafic reel]
|
||
B -->|Non| E[Calcul Haversine]
|
||
E --> F[Distance a vol d'oiseau]
|
||
F --> G[Vitesse moyenne 30 km/h]
|
||
G --> H[ETA estime]
|
||
D --> I[Retourner ETA]
|
||
H --> I
|
||
I --> J{Fallback utilise?}
|
||
J -->|Oui| K[Ajouter flag fallback_used: true]
|
||
J -->|Non| L[Response standard]
|
||
```
|
||
|
||
**Fallback:** Si l'API TomTom est indisponible, le systeme utilise un calcul local base sur:
|
||
- Distance Haversine
|
||
- Vitesse moyenne estimee (30 km/h en ville)
|
||
|
||
---
|
||
|
||
### Auto-Assignation GPS
|
||
|
||
Le systeme assigne automatiquement les commandes au livreur le plus proche.
|
||
|
||
#### Processus d'Auto-Assignation
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
A[Nouvelle Commande] --> B{Adresse geocodee?}
|
||
B -->|Non| C[Geocoder adresse]
|
||
C --> D
|
||
B -->|Oui| D[Recuperer livreurs disponibles]
|
||
D --> E[Calculer distances Haversine]
|
||
E --> F[Trier par proximite]
|
||
F --> G{Livreur avec capacite?}
|
||
G -->|Oui| H[Calculer ETA TomTom]
|
||
G -->|Non| I{Forcer assignation?}
|
||
I -->|Oui| H
|
||
I -->|Non| J[Commande en attente]
|
||
H --> K[Assigner a la queue]
|
||
K --> L[Notifier livreur]
|
||
```
|
||
|
||
#### Regles de Capacite
|
||
|
||
- Chaque livreur peut avoir jusqu'a **10 commandes** dans sa queue
|
||
- Si tous les livreurs sont a capacite maximale, le systeme peut forcer l'assignation
|
||
- Les livreurs avec le statut `offline` ne recoivent pas de commandes
|
||
|
||
#### Auto-assigner une commande specifique
|
||
|
||
```bash
|
||
POST /api/v2/admin/protected/orders/:id/auto-assign
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Reponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Commande assignee automatiquement",
|
||
"command_id": 1234,
|
||
"assigned_to": "john_deliveryman",
|
||
"assignment_details": {
|
||
"distance_km": 1.5,
|
||
"eta_minutes": 8,
|
||
"queue_position": 3,
|
||
"method": "nearest_available"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Lister les livreurs par distance
|
||
|
||
Obtient la liste des livreurs tries par distance depuis une adresse.
|
||
|
||
```bash
|
||
POST /api/v2/admin/protected/delivery/distances
|
||
Authorization: Bearer <admin_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requete:**
|
||
```json
|
||
{
|
||
"address": "15 Rue de la Paix, 75002 Paris"
|
||
}
|
||
```
|
||
|
||
**Reponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"address": "15 Rue de la Paix, 75002 Paris",
|
||
"destination": {
|
||
"latitude": 48.8698,
|
||
"longitude": 2.3311
|
||
},
|
||
"delivery_persons": [
|
||
{
|
||
"username": "john_deliveryman",
|
||
"distance_km": 1.5,
|
||
"eta_minutes": 8,
|
||
"status": "available",
|
||
"queue_size": 2,
|
||
"location": {
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522
|
||
}
|
||
},
|
||
{
|
||
"username": "jane_delivery",
|
||
"distance_km": 3.2,
|
||
"eta_minutes": 15,
|
||
"status": "available",
|
||
"queue_size": 5,
|
||
"location": {
|
||
"latitude": 48.8456,
|
||
"longitude": 2.3789
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Liens de Navigation
|
||
|
||
Le systeme genere des liens vers plusieurs applications de cartographie.
|
||
|
||
#### Obtenir les liens carte d'un livreur
|
||
|
||
```bash
|
||
GET /api/v2/admin/protected/delivery-persons/:username/map-links
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Reponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"deliveryman": "john_deliveryman",
|
||
"location": {
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522
|
||
},
|
||
"map_links": {
|
||
"google_maps": "https://www.google.com/maps?q=48.8566,2.3522",
|
||
"google_maps_app": "comgooglemaps://?center=48.8566,2.3522",
|
||
"waze": "https://www.waze.com/ul?ll=48.8566,2.3522&navigate=yes",
|
||
"waze_app": "waze://?ll=48.8566,2.3522&navigate=yes",
|
||
"apple_maps": "https://maps.apple.com/?ll=48.8566,2.3522",
|
||
"openstreetmap": "https://www.openstreetmap.org/?mlat=48.8566&mlon=2.3522",
|
||
"bing_maps": "https://www.bing.com/maps?cp=48.8566~2.3522",
|
||
"here_maps": "https://share.here.com/l/48.8566,2.3522"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### Obtenir les liens de navigation pour une commande
|
||
|
||
Genere des liens de navigation depuis la position du livreur vers la destination de livraison.
|
||
|
||
```bash
|
||
GET /api/v2/admin/protected/commands/:id/navigation-links
|
||
Authorization: Bearer <admin_token>
|
||
```
|
||
|
||
**Reponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"command_id": 1234,
|
||
"origin": {
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522,
|
||
"description": "Position du livreur"
|
||
},
|
||
"destination": {
|
||
"latitude": 48.8698,
|
||
"longitude": 2.3311,
|
||
"address": "15 Rue de la Paix, 75002 Paris"
|
||
},
|
||
"navigation_links": {
|
||
"google_maps": "https://www.google.com/maps/dir/48.8566,2.3522/48.8698,2.3311",
|
||
"waze": "https://www.waze.com/ul?ll=48.8698,2.3311&navigate=yes&from=48.8566,2.3522",
|
||
"apple_maps": "https://maps.apple.com/?saddr=48.8566,2.3522&daddr=48.8698,2.3311"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Gestion des Positions en Temps Reel
|
||
|
||
#### Flux de Mise a Jour de Position
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant L as Livreur (App)
|
||
participant API as Backend API
|
||
participant R as Redis
|
||
participant PS as Redis Pub/Sub
|
||
participant A as Admin/Client
|
||
|
||
L->>API: POST /location/update {lat, lng}
|
||
API->>API: Valider coordonnees
|
||
API->>R: SET delivery:location:{username}
|
||
API->>PS: PUBLISH position_update
|
||
PS-->>A: Notification temps reel
|
||
API-->>L: 200 OK {location, updated_at}
|
||
```
|
||
|
||
#### Mise a jour de position (Livreur)
|
||
|
||
```bash
|
||
POST /api/v1/livreur/location/update
|
||
Authorization: Bearer <livreur_token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**Requete:**
|
||
```json
|
||
{
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522
|
||
}
|
||
```
|
||
|
||
**Validation des coordonnees:**
|
||
- Latitude: entre -90 et +90
|
||
- Longitude: entre -180 et +180
|
||
|
||
**Reponse (200 OK):**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"message": "Position mise a jour",
|
||
"location": {
|
||
"latitude": 48.8566,
|
||
"longitude": 2.3522,
|
||
"updated_at": "2025-01-18T17:35:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Erreurs possibles:**
|
||
- `400` - Coordonnees invalides (hors limites)
|
||
- `401` - Non authentifie
|
||
|
||
---
|
||
|
||
### Stockage Redis des Positions
|
||
|
||
Les positions GPS sont stockees dans Redis pour un acces rapide:
|
||
|
||
```mermaid
|
||
graph LR
|
||
subgraph Redis Cache
|
||
A[delivery:location:username<br/>TTL: 2h]
|
||
B[delivery:status:username<br/>TTL: 1h]
|
||
C[geocode:cache:address_hash<br/>TTL: 7j]
|
||
D[command:destination:id<br/>TTL: 4h]
|
||
end
|
||
|
||
subgraph Donnees
|
||
A --> A1[latitude, longitude, updated_at]
|
||
B --> B1[status, latitude, longitude]
|
||
C --> C1[latitude, longitude, address]
|
||
D --> D1[dest_lat, dest_lng]
|
||
end
|
||
```
|
||
|
||
|
||
| `command:destination:{command_id}` | Coordonnees destination | 4 heures |
|
||
|
||
---
|
||
|
||
### Composant Carte Frontend (TomTomMap)
|
||
|
||
Le frontend inclut un composant React/TypeScript pour afficher la carte interactive.
|
||
|
||
#### Fonctionnalites du Composant
|
||
|
||
- **Carte interactive** avec marqueurs livreur/destination
|
||
- **Calcul d'itineraire** en temps reel
|
||
- **Instructions de navigation** (tourner a gauche, a droite, rond-point, etc.)
|
||
- **Affichage distance/duree** du trajet
|
||
- **Mode 3D** avec orientation selon la direction
|
||
- **Support francais** pour toutes les instructions
|
||
|
||
#### Exemple d'Utilisation
|
||
|
||
```tsx
|
||
import TomTomMap from './components/TomTomMap';
|
||
|
||
function DeliveryTracking() {
|
||
return (
|
||
<TomTomMap
|
||
driverPosition={{ lat: 48.8566, lng: 2.3522 }}
|
||
destinationPosition={{ lat: 48.8698, lng: 2.3311 }}
|
||
showRoute={true}
|
||
showInstructions={true}
|
||
/>
|
||
);
|
||
}
|
||
```
|
||
|
||
#### Props du Composant
|
||
|
||
| Prop | Type | Description |
|
||
|------|------|-------------|
|
||
| `driverPosition` | `{lat: number, lng: number}` | Position du livreur |
|
||
| `destinationPosition` | `{lat: number, lng: number}` | Destination de livraison |
|
||
| `showRoute` | `boolean` | Afficher l'itineraire |
|
||
| `showInstructions` | `boolean` | Afficher le panneau d'instructions |
|
||
| `onEtaUpdate` | `(eta: number) => void` | Callback quand l'ETA change |
|
||
|
||
---
|
||
|
||
### Worker d'Auto-Assignation
|
||
|
||
Un worker CRON s'execute automatiquement pour assigner les commandes en attente.
|
||
|
||
#### Configuration
|
||
|
||
- **Frequence:** Toutes les 1 minute
|
||
- **Fichier:** `backend/gestion/workers/cron_auto_assign.go`
|
||
|
||
#### Processus du Worker
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
START((Demarrage CRON)) --> A[Recuperer commandes pending]
|
||
A --> B{Commandes a traiter?}
|
||
B -->|Non| END((Fin))
|
||
B -->|Oui| C[Prendre commande suivante]
|
||
C --> D{Adresse geocodee?}
|
||
D -->|Non| E[Geocoder via Nominatim]
|
||
E --> F
|
||
D -->|Oui| F[Recuperer livreurs disponibles]
|
||
F --> G[Calculer distances]
|
||
G --> H[Selectionner le plus proche]
|
||
H --> I{Capacite disponible?}
|
||
I -->|Non| J[Marquer pour retry]
|
||
I -->|Oui| K[Calculer ETA TomTom]
|
||
K --> L[Assigner commande]
|
||
L --> M[Mettre a jour statut]
|
||
M --> N{Autres commandes?}
|
||
J --> N
|
||
N -->|Oui| C
|
||
N -->|Non| O[Logger resultats]
|
||
O --> END
|
||
```
|
||
|
||
#### Logs d'Exemple
|
||
|
||
```
|
||
[AUTO-ASSIGN] Processing 5 pending orders
|
||
[AUTO-ASSIGN] Order #1234: Geocoded to (48.8698, 2.3311)
|
||
[AUTO-ASSIGN] Order #1234: Nearest driver is john_deliveryman (1.5 km)
|
||
[AUTO-ASSIGN] Order #1234: ETA calculated: 8 minutes
|
||
[AUTO-ASSIGN] Order #1234: Assigned successfully
|
||
[AUTO-ASSIGN] Completed: 5 assigned, 0 failed
|
||
```
|
||
|
||
---
|
||
|
||
### Cycle de Vie d'une Livraison (GPS)
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> pending: Commande creee (checkout)
|
||
pending --> assigned: Auto-assignation GPS / Admin
|
||
pending --> cancelled: Annulation client ou admin
|
||
|
||
assigned --> en_route: Livreur demarre (start)
|
||
assigned --> cancelled: Annulation
|
||
|
||
en_route --> arrived: Livreur arrive (GPS ou Admin/Cabine)
|
||
en_route --> en_route: Mise a jour position GPS
|
||
en_route --> livre: Validation GPS livreur / Admin
|
||
|
||
arrived --> livre: Remise au client (validation GPS livreur)
|
||
arrived --> approved: Admin/Cabine force approbation
|
||
|
||
livre --> approved: Client confirme / Admin / Cabine
|
||
|
||
approved --> [*]
|
||
cancelled --> [*]
|
||
|
||
note right of arrived
|
||
Declenche par :
|
||
- Livreur PUT status=arrived
|
||
- Admin/Cabine bouton "Le livreur est la"
|
||
Notifie le client (Telegram + push)
|
||
end note
|
||
|
||
note right of en_route
|
||
ETA calcule et stocke dans Redis
|
||
(command:eta:{id})
|
||
Utilise pour les notifications Telegram
|
||
end note
|
||
```
|
||
|
||
---
|
||
|
||
### Gestion des Erreurs GPS
|
||
|
||
#### Geocodage Echoue
|
||
|
||
Si une adresse ne peut pas etre geocodee:
|
||
- La commande reste en statut `pending`
|
||
- Un log d'erreur est genere
|
||
- L'admin peut corriger l'adresse manuellement
|
||
|
||
#### API TomTom Indisponible
|
||
|
||
Le systeme bascule automatiquement sur le calcul local:
|
||
- Utilise la formule Haversine pour la distance
|
||
- Estime l'ETA avec une vitesse moyenne de 30 km/h
|
||
- Un flag `fallback_used: true` est ajoute a la reponse
|
||
|
||
|
||
|
||
---
|
||
|
||
### Bonnes Pratiques
|
||
|
||
#### Pour les Livreurs
|
||
|
||
1. **Mettre a jour la position** frequemment (toutes les 30 secondes recommande)
|
||
2. **Verifier le statut** avant de commencer une livraison
|
||
3. **Utiliser les liens de navigation** generes par l'API
|
||
|
||
#### Pour les Admins
|
||
|
||
1. **Surveiller les queues** des livreurs pour eviter la surcharge
|
||
2. **Verifier les adresses** qui echouent au geocodage
|
||
3. **Utiliser l'endpoint distances** pour l'assignation manuelle si necessaire
|
||
|
||
#### Pour les Developpeurs
|
||
|
||
1. **Toujours valider** les coordonnees avant stockage
|
||
2. **Utiliser le cache** Redis pour les adresses frequentes
|
||
3. **Implementer un fallback** si TomTom est indisponible
|
||
4. **Logger les erreurs** de geocodage pour analyse
|
||
|
||
---
|
||
|
||
## ❌ Codes d'Erreur
|
||
|
||
### Erreurs HTTP Standards
|
||
|
||
| Code | Signification | Description |
|
||
|------|---------------|-------------|
|
||
| 200 | OK | Succès |
|
||
| 201 | Created | Ressource créée |
|
||
| 400 | Bad Request | Données invalides |
|
||
| 401 | Unauthorized | Non authentifié |
|
||
| 403 | Forbidden | Non autorisé |
|
||
| 404 | Not Found | Ressource introuvable |
|
||
| 409 | Conflict | Conflit (username déjà pris) |
|
||
| 422 | Unprocessable Entity | Validation échouée |
|
||
| 500 | Internal Server Error | Erreur serveur |
|
||
|
||
### Exemples d'Erreurs
|
||
|
||
#### 400 - Bad Request
|
||
```json
|
||
{
|
||
"error": "Données invalides",
|
||
"details": "Le champ 'username' est requis"
|
||
}
|
||
```
|
||
|
||
#### 401 - Unauthorized
|
||
```json
|
||
{
|
||
"error": "Token invalide ou expiré",
|
||
"message": "Veuillez vous reconnecter"
|
||
}
|
||
```
|
||
|
||
#### 403 - Forbidden
|
||
```json
|
||
{
|
||
"error": "Accès refusé",
|
||
"message": "Vous n'avez pas les permissions nécessaires"
|
||
}
|
||
```
|
||
|
||
#### 404 - Not Found
|
||
```json
|
||
{
|
||
"error": "Ressource introuvable",
|
||
"message": "Commande avec l'ID 9999 introuvable"
|
||
}
|
||
```
|
||
|
||
#### 409 - Conflict
|
||
```json
|
||
{
|
||
"error": "Conflit",
|
||
"message": "Ce nom d'utilisateur est déjà pris"
|
||
}
|
||
```
|
||
|
||
#### 422 - Unprocessable Entity
|
||
```json
|
||
{
|
||
"error": "Validation échouée",
|
||
"details": {
|
||
"field": "telephone",
|
||
"message": "Format de téléphone invalide"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 500 - Internal Server Error
|
||
```json
|
||
{
|
||
"error": "Erreur serveur interne",
|
||
"message": "Une erreur inattendue s'est produite"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Notes Importantes
|
||
|
||
### Sécurité
|
||
- **Tokens JWT** : Expiration variable selon le rôle (Client: 5h · Admin/Livreur/Cabine: 10h)
|
||
- **Validation GPS** : Requis pour certaines actions de livraison
|
||
- **Filtrage des données** : Les livreurs n'ont pas accès aux téléphones clients
|
||
- **Rate Limiting** : Protection contre les abus
|
||
|
||
### Format des Données
|
||
- **Dates** : Format ISO 8601 (`2025-01-18T14:30:00Z`)
|
||
- **Coordonnées** : Latitude/Longitude en décimal (WGS84)
|
||
- **Prix** : En euros avec 2 décimales
|
||
- **Téléphones** : Format international (`+33612345678`)
|
||
|
||
### Workers Automatiques
|
||
- **Auto-Assignment Cron** : Toutes les 1 minute (`workers/cron_auto_assign.go`)
|
||
- **Queue Cleanup** : Toutes les 5 minutes
|
||
- **ETA Updates** : Toutes les 30 secondes
|
||
- **Stock Cleanup** : Toutes les 5 minutes
|
||
|
||
### Fonctionnalités Avancées
|
||
- **Assignation GPS automatique** au checkout
|
||
- **Calcul ETA temps réel** avec TomTom API
|
||
- **Système de queues optimisé** pour les livreurs
|
||
- **Nettoyage automatique** des commandes invalides
|
||
- **Notifications temps réel** via Redis Pub/Sub
|
||
|
||
---
|
||
|
||
**Documentation mise à jour le :** 2026-05-01
|
||
**Version API :** 5.2.0
|
||
**Technologies :** Go 1.24, Gin, PostgreSQL 16, Redis 7, React 19, Expo 54, TomTom API, ModSecurity WAF
|
||
**Déploiement :** Docker Compose · Nginx + ModSecurity OWASP CRS · TLS 1.2/1.3
|
||
**Base URL prod :** `https://mln-uber.club`
|