Ajout documentation
This commit is contained in:
@@ -0,0 +1,843 @@
|
||||
# Cahier des Charges — API & Base de Données
|
||||
## Projet : Laverie Connectée — Backend
|
||||
**Version :** 2.0
|
||||
**Stack principale :** Laravel 11, MySQL 8, Redis, Docker
|
||||
**Positionnement :** V1 démontrable, extensible vers V2 sans refonte majeure
|
||||
|
||||
---
|
||||
|
||||
## 1. Contexte & Objectifs
|
||||
|
||||
### 1.1 Contexte
|
||||
Développement d'une API centrale servant :
|
||||
- une application utilisateur multi-plateforme,
|
||||
- un back-office exploitant,
|
||||
- une couche d'intégration avec les systèmes machines fournis par le client ou ses partenaires techniques.
|
||||
|
||||
L'API est le **point d'entrée unique de la logique métier**. Les clients applicatifs ne communiquent jamais directement avec la base de données.
|
||||
|
||||
### 1.2 Objectifs V1
|
||||
- authentifier les utilisateurs finaux et les exploitants,
|
||||
- gérer plusieurs laveries et plusieurs exploitants dans une même plateforme,
|
||||
- exposer les données établissements / machines / disponibilités,
|
||||
- gérer un porte-monnaie électronique avec traçabilité,
|
||||
- permettre la réservation d'un créneau,
|
||||
- permettre le démarrage d'un lavage via intégration machine,
|
||||
- historiser les événements machines,
|
||||
- fournir un socle d'audit, de sécurité et de statistiques simples.
|
||||
|
||||
### 1.3 Principes de conception
|
||||
- **MVP strict** : seules les fonctionnalités nécessaires à la démonstration et à la première mise en service sont incluses en V1.
|
||||
- **Extensibilité** : fidélité, abonnements, parrainage, campagnes marketing et analytics avancées sont prévues mais non implémentées en V1.
|
||||
- **Traçabilité** : tout flux critique doit être rejouable et auditable.
|
||||
- **Isolation métier** : chaque exploitant ne voit que ses établissements, ses machines et ses chiffres.
|
||||
- **Intégration externe souple** : la couche machine doit supporter aussi bien un modèle où notre système appelle une API partenaire qu'un modèle où le partenaire pousse des événements vers notre API.
|
||||
|
||||
---
|
||||
|
||||
## 2. Périmètre fonctionnel
|
||||
|
||||
### 2.1 MVP V1
|
||||
|
||||
#### Utilisateur final
|
||||
- inscription / connexion,
|
||||
- consultation des laveries,
|
||||
- consultation des machines et de leur statut,
|
||||
- consultation du solde wallet,
|
||||
- historique simple des transactions,
|
||||
- rechargement du wallet,
|
||||
- réservation d'un créneau,
|
||||
- démarrage d'un lavage,
|
||||
- historique des lavages,
|
||||
- notifications transactionnelles.
|
||||
|
||||
#### Exploitant
|
||||
- connexion superviseur,
|
||||
- consultation du parc machines,
|
||||
- consultation d'un tableau de bord simple,
|
||||
- consultation des réservations et lavages,
|
||||
- gestion simple de la tarification,
|
||||
- visualisation des alertes techniques de base.
|
||||
|
||||
#### Plateforme
|
||||
- multi-exploitants,
|
||||
- audit logs,
|
||||
- agrégats journaliers simples,
|
||||
- intégration machine simulable pour démonstration.
|
||||
|
||||
### 2.2 V2 prévue
|
||||
- fidélité,
|
||||
- parrainage,
|
||||
- abonnements,
|
||||
- campagnes marketing,
|
||||
- promotions avancées,
|
||||
- exports RGPD complets,
|
||||
- analytics détaillées,
|
||||
- règles tarifaires complexes,
|
||||
- exports métier avancés.
|
||||
|
||||
### 2.3 Hors périmètre V1
|
||||
- chat,
|
||||
- avis,
|
||||
- FAQ dynamique,
|
||||
- recommandation de cycle par photo,
|
||||
- moteur prédictif / deep learning,
|
||||
- orchestration matérielle bas niveau.
|
||||
|
||||
---
|
||||
|
||||
## 3. Stack Technique
|
||||
|
||||
| Composant | Technologie | Version | Commentaire |
|
||||
|-----------|-------------|---------|-------------|
|
||||
| Framework API | Laravel | 11.x | Coeur métier principal |
|
||||
| Base de données | MySQL | 8.0 | Référentiel transactionnel |
|
||||
| Cache / queues | Redis | 7.x | Queues, verrous applicatifs, cache court |
|
||||
| Workers | Laravel Horizon | 5.x | Supervision des jobs |
|
||||
| Scheduler | Laravel Scheduler | natif | Cron applicatif |
|
||||
| Documentation API | Scramble ou L5-Swagger | — | OpenAPI |
|
||||
| Conteneurisation | Docker | — | Environnements reproductibles |
|
||||
| CI/CD | GitHub Actions | — | Tests + build |
|
||||
| Monitoring erreurs | Sentry | — | Backend |
|
||||
| Logs | JSON structurés | — | Corrélation et exploitation |
|
||||
|
||||
### 3.1 Authentification recommandée
|
||||
- **App mobile / web utilisateur** : access token court + refresh token.
|
||||
- **Back-office exploitant** : session cookie Laravel.
|
||||
- **Intégrations machines / partenaires** : API key technique ou signature HMAC.
|
||||
|
||||
Cette séparation évite de mélanger les contraintes des apps utilisateurs, du back-office et des systèmes techniques.
|
||||
|
||||
---
|
||||
|
||||
## 4. Architecture logique
|
||||
|
||||
### 4.1 Domaines métier principaux
|
||||
- Authentification
|
||||
- Organisations / exploitants
|
||||
- Établissements
|
||||
- Machines
|
||||
- Tarification
|
||||
- Wallet & paiements
|
||||
- Réservations
|
||||
- Lavages
|
||||
- Notifications
|
||||
- Intégrations machines
|
||||
- Audit & statistiques
|
||||
|
||||
### 4.2 Multi-tenant logique
|
||||
Le système n'est pas multi-base ni multi-schémas. L'isolation est assurée au niveau applicatif par :
|
||||
- `organization_id`,
|
||||
- policies Laravel,
|
||||
- query scopes explicites,
|
||||
- ressources d'API filtrées selon l'utilisateur connecté.
|
||||
|
||||
### 4.3 Structure recommandée Laravel
|
||||
|
||||
```
|
||||
app/
|
||||
├── Domain/
|
||||
│ ├── Auth/
|
||||
│ ├── Organization/
|
||||
│ ├── Machine/
|
||||
│ ├── Wallet/
|
||||
│ ├── Booking/
|
||||
│ ├── Wash/
|
||||
│ ├── Pricing/
|
||||
│ ├── Notification/
|
||||
│ └── Integration/
|
||||
├── Http/
|
||||
│ ├── Controllers/
|
||||
│ ├── Middleware/
|
||||
│ └── Requests/
|
||||
├── Jobs/
|
||||
├── Events/
|
||||
├── Listeners/
|
||||
├── Policies/
|
||||
├── Models/
|
||||
└── Support/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Base de Données — Schéma révisé
|
||||
|
||||
## 5.0 Connexion bdd de test
|
||||
|
||||
hostname: localhost
|
||||
port: 3306
|
||||
user: root
|
||||
password:
|
||||
shema: laverie
|
||||
|
||||
## 5.1 Exploitants et périmètre
|
||||
|
||||
### Table `organizations`
|
||||
```sql
|
||||
CREATE TABLE organizations (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
code VARCHAR(50) UNIQUE NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL
|
||||
);
|
||||
```
|
||||
|
||||
### Table `establishments`
|
||||
```sql
|
||||
CREATE TABLE establishments (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
organization_id BIGINT UNSIGNED NOT NULL,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
city VARCHAR(100) NULL,
|
||||
zip_code VARCHAR(10) NULL,
|
||||
latitude DECIMAL(10,8) NULL,
|
||||
longitude DECIMAL(11,8) NULL,
|
||||
timezone VARCHAR(50) NOT NULL DEFAULT 'Europe/Paris',
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_establishments_organization FOREIGN KEY (organization_id) REFERENCES organizations(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `supervisors`
|
||||
```sql
|
||||
CREATE TABLE supervisors (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
organization_id BIGINT UNSIGNED NOT NULL,
|
||||
establishment_id BIGINT UNSIGNED NULL,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
first_name VARCHAR(100) NOT NULL,
|
||||
last_name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
role ENUM('platform_admin','owner','manager','viewer') NOT NULL DEFAULT 'manager',
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
last_login_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_supervisors_organization FOREIGN KEY (organization_id) REFERENCES organizations(id),
|
||||
CONSTRAINT fk_supervisors_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id)
|
||||
);
|
||||
```
|
||||
|
||||
## 5.2 Utilisateurs finaux
|
||||
|
||||
### Table `users`
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
first_name VARCHAR(100) NOT NULL,
|
||||
last_name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
phone VARCHAR(20) UNIQUE NULL,
|
||||
birthdate DATE NULL,
|
||||
email_verified_at TIMESTAMP NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
locale VARCHAR(10) NOT NULL DEFAULT 'fr',
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
anonymized_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
deleted_at TIMESTAMP NULL
|
||||
);
|
||||
```
|
||||
|
||||
### Table `gdpr_consents`
|
||||
```sql
|
||||
CREATE TABLE gdpr_consents (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
type ENUM('data_processing','marketing','analytics') NOT NULL,
|
||||
accepted BOOLEAN NOT NULL,
|
||||
policy_version VARCHAR(50) NOT NULL,
|
||||
policy_text_hash VARCHAR(255) NOT NULL,
|
||||
source ENUM('mobile','web','backoffice') NOT NULL,
|
||||
consent_language VARCHAR(10) NOT NULL DEFAULT 'fr',
|
||||
ip_address VARCHAR(45) NULL,
|
||||
user_agent TEXT NULL,
|
||||
accepted_at TIMESTAMP NOT NULL,
|
||||
revoked_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_gdpr_consents_user FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `user_devices`
|
||||
```sql
|
||||
CREATE TABLE user_devices (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
platform ENUM('android','ios','web') NOT NULL,
|
||||
push_token VARCHAR(255) NOT NULL,
|
||||
app_version VARCHAR(50) NULL,
|
||||
device_name VARCHAR(100) NULL,
|
||||
last_seen_at TIMESTAMP NULL,
|
||||
revoked_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
UNIQUE KEY uniq_user_device_token (push_token),
|
||||
CONSTRAINT fk_user_devices_user FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `notification_preferences`
|
||||
```sql
|
||||
CREATE TABLE notification_preferences (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
transaction_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
reminder_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
marketing_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
system_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
UNIQUE KEY uniq_notification_preferences_user (user_id),
|
||||
CONSTRAINT fk_notification_preferences_user FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
```
|
||||
|
||||
## 5.3 Machines et intégrations
|
||||
|
||||
### Table `machines`
|
||||
```sql
|
||||
CREATE TABLE machines (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
establishment_id BIGINT UNSIGNED NOT NULL,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
type ENUM('washer_small','washer_large','dryer_small','dryer_large') NOT NULL,
|
||||
qr_code VARCHAR(255) UNIQUE NOT NULL,
|
||||
status ENUM('available','reserved','running','maintenance','offline','error') NOT NULL DEFAULT 'available',
|
||||
current_user_id BIGINT UNSIGNED NULL,
|
||||
cycle_started_at TIMESTAMP NULL,
|
||||
cycle_ends_at TIMESTAMP NULL,
|
||||
last_heartbeat_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_machines_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id),
|
||||
CONSTRAINT fk_machines_current_user FOREIGN KEY (current_user_id) REFERENCES users(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `machine_integrations`
|
||||
```sql
|
||||
CREATE TABLE machine_integrations (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
machine_id BIGINT UNSIGNED NOT NULL,
|
||||
provider VARCHAR(100) NOT NULL,
|
||||
external_machine_id VARCHAR(100) NOT NULL,
|
||||
external_site_id VARCHAR(100) NULL,
|
||||
mode ENUM('pull','push','hybrid','simulated') NOT NULL DEFAULT 'simulated',
|
||||
config JSON NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
UNIQUE KEY uniq_machine_provider_external (provider, external_machine_id),
|
||||
CONSTRAINT fk_machine_integrations_machine FOREIGN KEY (machine_id) REFERENCES machines(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `machine_commands`
|
||||
```sql
|
||||
CREATE TABLE machine_commands (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
machine_id BIGINT UNSIGNED NOT NULL,
|
||||
provider VARCHAR(100) NOT NULL,
|
||||
command_type ENUM('start_cycle','stop_cycle','refresh_status') NOT NULL,
|
||||
payload JSON NULL,
|
||||
status ENUM('pending','sent','acknowledged','failed','timeout','cancelled') NOT NULL DEFAULT 'pending',
|
||||
external_reference VARCHAR(255) NULL,
|
||||
correlation_id VARCHAR(255) NULL,
|
||||
requested_by_user_id BIGINT UNSIGNED NULL,
|
||||
requested_by_supervisor_id BIGINT UNSIGNED NULL,
|
||||
sent_at TIMESTAMP NULL,
|
||||
responded_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_machine_commands_machine FOREIGN KEY (machine_id) REFERENCES machines(id),
|
||||
CONSTRAINT fk_machine_commands_user FOREIGN KEY (requested_by_user_id) REFERENCES users(id),
|
||||
CONSTRAINT fk_machine_commands_supervisor FOREIGN KEY (requested_by_supervisor_id) REFERENCES supervisors(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `machine_events`
|
||||
```sql
|
||||
CREATE TABLE machine_events (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
machine_id BIGINT UNSIGNED NOT NULL,
|
||||
provider VARCHAR(100) NOT NULL,
|
||||
external_event_id VARCHAR(255) NULL,
|
||||
event_type ENUM('heartbeat','machine_online','machine_offline','cycle_started','cycle_completed','cycle_failed','error_reported','status_changed') NOT NULL,
|
||||
payload JSON NULL,
|
||||
occurred_at TIMESTAMP NOT NULL,
|
||||
received_at TIMESTAMP NOT NULL,
|
||||
processed_at TIMESTAMP NULL,
|
||||
processing_status ENUM('pending','processed','failed','ignored') NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_machine_events_machine FOREIGN KEY (machine_id) REFERENCES machines(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `machine_status_history`
|
||||
```sql
|
||||
CREATE TABLE machine_status_history (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
machine_id BIGINT UNSIGNED NOT NULL,
|
||||
previous_status VARCHAR(50) NULL,
|
||||
new_status VARCHAR(50) NOT NULL,
|
||||
source VARCHAR(100) NOT NULL,
|
||||
reason VARCHAR(255) NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_machine_status_history_machine FOREIGN KEY (machine_id) REFERENCES machines(id)
|
||||
);
|
||||
```
|
||||
|
||||
## 5.4 Tarification
|
||||
|
||||
### Table `pricing_rules`
|
||||
```sql
|
||||
CREATE TABLE pricing_rules (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
establishment_id BIGINT UNSIGNED NOT NULL,
|
||||
machine_id BIGINT UNSIGNED NULL,
|
||||
machine_type ENUM('washer_small','washer_large','dryer_small','dryer_large') NULL,
|
||||
day_type ENUM('weekday','weekend','holiday','all') NOT NULL DEFAULT 'all',
|
||||
slot_start TIME NOT NULL,
|
||||
slot_end TIME NOT NULL,
|
||||
price DECIMAL(8,2) NOT NULL,
|
||||
label VARCHAR(100) NULL,
|
||||
requires_app BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
priority SMALLINT NOT NULL DEFAULT 100,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_pricing_rules_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id),
|
||||
CONSTRAINT fk_pricing_rules_machine FOREIGN KEY (machine_id) REFERENCES machines(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `promotions`
|
||||
```sql
|
||||
CREATE TABLE promotions (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
establishment_id BIGINT UNSIGNED NOT NULL,
|
||||
machine_type ENUM('washer_small','washer_large','dryer_small','dryer_large','all') NOT NULL DEFAULT 'all',
|
||||
discount_type ENUM('percent','fixed') NOT NULL,
|
||||
discount_value DECIMAL(8,2) NOT NULL,
|
||||
starts_at TIMESTAMP NOT NULL,
|
||||
ends_at TIMESTAMP NOT NULL,
|
||||
description TEXT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_promotions_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id)
|
||||
);
|
||||
```
|
||||
|
||||
## 5.5 Wallet et paiements
|
||||
|
||||
### Table `wallets`
|
||||
```sql
|
||||
CREATE TABLE wallets (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
currency CHAR(3) NOT NULL DEFAULT 'EUR',
|
||||
current_balance DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
status ENUM('active','blocked','closed') NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
UNIQUE KEY uniq_wallets_user (user_id),
|
||||
CONSTRAINT fk_wallets_user FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `payment_transactions`
|
||||
```sql
|
||||
CREATE TABLE payment_transactions (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
provider VARCHAR(50) NOT NULL,
|
||||
provider_payment_id VARCHAR(255) NULL,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
currency CHAR(3) NOT NULL DEFAULT 'EUR',
|
||||
status ENUM('initiated','pending','succeeded','failed','cancelled','refunded') NOT NULL DEFAULT 'initiated',
|
||||
idempotency_key VARCHAR(100) NOT NULL,
|
||||
return_url VARCHAR(255) NULL,
|
||||
raw_payload JSON NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
UNIQUE KEY uniq_payment_idempotency (idempotency_key),
|
||||
CONSTRAINT fk_payment_transactions_user FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `wallet_transactions`
|
||||
```sql
|
||||
CREATE TABLE wallet_transactions (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
wallet_id BIGINT UNSIGNED NOT NULL,
|
||||
type ENUM('credit','debit','hold','release','refund','adjustment') NOT NULL,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
balance_before DECIMAL(10,2) NOT NULL,
|
||||
balance_after DECIMAL(10,2) NOT NULL,
|
||||
source_type VARCHAR(100) NOT NULL,
|
||||
source_id BIGINT UNSIGNED NULL,
|
||||
idempotency_key VARCHAR(100) NULL,
|
||||
metadata JSON NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_wallet_transactions_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id)
|
||||
);
|
||||
```
|
||||
|
||||
## 5.6 Réservations et lavages
|
||||
|
||||
### Table `bookings`
|
||||
```sql
|
||||
CREATE TABLE bookings (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
machine_id BIGINT UNSIGNED NOT NULL,
|
||||
slot_start TIMESTAMP NOT NULL,
|
||||
slot_end TIMESTAMP NOT NULL,
|
||||
booking_fee DECIMAL(8,2) NOT NULL DEFAULT 0.00,
|
||||
reserved_amount DECIMAL(8,2) NOT NULL DEFAULT 0.00,
|
||||
penalty_amount DECIMAL(8,2) NOT NULL DEFAULT 0.00,
|
||||
status ENUM('pending','confirmed','cancelled','expired','active','completed','no_show') NOT NULL DEFAULT 'pending',
|
||||
cancelled_at TIMESTAMP NULL,
|
||||
penalty_applied_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_bookings_user FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
CONSTRAINT fk_bookings_machine FOREIGN KEY (machine_id) REFERENCES machines(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `washes`
|
||||
```sql
|
||||
CREATE TABLE washes (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
uuid CHAR(36) UNIQUE NOT NULL,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
machine_id BIGINT UNSIGNED NOT NULL,
|
||||
booking_id BIGINT UNSIGNED NULL,
|
||||
machine_command_id BIGINT UNSIGNED NULL,
|
||||
trigger_method ENUM('qr_code','booking','supervisor','system') NOT NULL,
|
||||
status ENUM('pending_start','running','completed','failed','cancelled') NOT NULL DEFAULT 'pending_start',
|
||||
program VARCHAR(100) NULL,
|
||||
started_at TIMESTAMP NULL,
|
||||
ended_at TIMESTAMP NULL,
|
||||
duration_minutes INT UNSIGNED NULL,
|
||||
cost DECIMAL(8,2) NOT NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_washes_user FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
CONSTRAINT fk_washes_machine FOREIGN KEY (machine_id) REFERENCES machines(id),
|
||||
CONSTRAINT fk_washes_booking FOREIGN KEY (booking_id) REFERENCES bookings(id),
|
||||
CONSTRAINT fk_washes_command FOREIGN KEY (machine_command_id) REFERENCES machine_commands(id)
|
||||
);
|
||||
```
|
||||
|
||||
## 5.7 Notifications, audit et statistiques
|
||||
|
||||
### Table `push_notifications`
|
||||
```sql
|
||||
CREATE TABLE push_notifications (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NULL,
|
||||
type VARCHAR(100) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
data JSON NULL,
|
||||
status ENUM('pending','sent','failed') NOT NULL DEFAULT 'pending',
|
||||
sent_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
CONSTRAINT fk_push_notifications_user FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Table `audit_logs`
|
||||
```sql
|
||||
CREATE TABLE audit_logs (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
actor_type VARCHAR(50) NOT NULL,
|
||||
actor_id BIGINT UNSIGNED NOT NULL,
|
||||
organization_id BIGINT UNSIGNED NULL,
|
||||
establishment_id BIGINT UNSIGNED NULL,
|
||||
action VARCHAR(100) NOT NULL,
|
||||
target_type VARCHAR(100) NOT NULL,
|
||||
target_id BIGINT UNSIGNED NULL,
|
||||
before_data JSON NULL,
|
||||
after_data JSON NULL,
|
||||
ip_address VARCHAR(45) NULL,
|
||||
created_at TIMESTAMP NULL
|
||||
);
|
||||
```
|
||||
|
||||
### Table `daily_establishment_stats`
|
||||
```sql
|
||||
CREATE TABLE daily_establishment_stats (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
establishment_id BIGINT UNSIGNED NOT NULL,
|
||||
stat_date DATE NOT NULL,
|
||||
total_washes INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
total_revenue DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
bookings_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
no_show_count INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
top_up_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
occupancy_rate DECIMAL(5,2) NOT NULL DEFAULT 0.00,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
UNIQUE KEY uniq_daily_establishment_stats (establishment_id, stat_date),
|
||||
CONSTRAINT fk_daily_establishment_stats_establishment FOREIGN KEY (establishment_id) REFERENCES establishments(id)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Contrat d'intégration machine
|
||||
|
||||
### 6.1 Principe
|
||||
Le backend doit supporter deux scénarios :
|
||||
- **pull** : notre système appelle une API partenaire pour envoyer une commande ou lire un statut,
|
||||
- **push** : le partenaire pousse des événements vers notre système,
|
||||
- **hybrid** : combinaison des deux,
|
||||
- **simulated** : mode démonstration / sandbox.
|
||||
|
||||
### 6.2 Abstraction applicative
|
||||
|
||||
```php
|
||||
interface MachineProviderInterface {
|
||||
public function startCycle(Machine $machine, array $payload): MachineCommandResult;
|
||||
public function stopCycle(Machine $machine, array $payload = []): MachineCommandResult;
|
||||
public function refreshStatus(Machine $machine): MachineStatusSnapshot;
|
||||
public function handleInboundEvent(array $payload): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 Événements entrants attendus
|
||||
- `heartbeat`
|
||||
- `machine_online`
|
||||
- `machine_offline`
|
||||
- `cycle_started`
|
||||
- `cycle_completed`
|
||||
- `cycle_failed`
|
||||
- `error_reported`
|
||||
- `status_changed`
|
||||
|
||||
### 6.4 Endpoints d'intégration proposés
|
||||
|
||||
| Méthode | Endpoint | Usage |
|
||||
|---------|----------|-------|
|
||||
| POST | `/api/v1/integrations/machines/events` | Réception d'événements machine |
|
||||
| POST | `/api/v1/integrations/machines/heartbeat` | Heartbeat passerelle / machine |
|
||||
| POST | `/api/v1/integrations/machines/commands/{uuid}/ack` | Accusé de réception optionnel |
|
||||
|
||||
### 6.5 Payload minimal d'événement
|
||||
```json
|
||||
{
|
||||
"provider": "client_gateway",
|
||||
"event_type": "cycle_completed",
|
||||
"occurred_at": "2026-06-27T10:15:00Z",
|
||||
"machine_external_id": "MACH-001",
|
||||
"correlation_id": "cmd_12345",
|
||||
"data": {
|
||||
"status": "available",
|
||||
"duration_minutes": 42,
|
||||
"error_code": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.6 Exigences minimales côté partenaire
|
||||
- identifiant machine externe stable,
|
||||
- horodatage fiable,
|
||||
- type d'événement,
|
||||
- état courant de la machine,
|
||||
- identifiant de corrélation si une commande a été envoyée par notre système,
|
||||
- stratégie d'authentification technique,
|
||||
- documentation des erreurs métiers.
|
||||
|
||||
### 6.7 Mode démonstration
|
||||
Un provider simulé doit permettre :
|
||||
- démarrage immédiat d'un cycle,
|
||||
- passage en statut `running`,
|
||||
- émission d'un faux événement `cycle_completed` après un délai configurable,
|
||||
- retour en statut `available`.
|
||||
|
||||
Ce mode permet une démo sans matériel réel.
|
||||
|
||||
---
|
||||
|
||||
## 7. Endpoints API — V1
|
||||
|
||||
### 7.1 Auth utilisateur
|
||||
| Méthode | Endpoint | Description |
|
||||
|---------|----------|-------------|
|
||||
| POST | `/api/v1/auth/register` | Inscription |
|
||||
| POST | `/api/v1/auth/login` | Connexion utilisateur |
|
||||
| POST | `/api/v1/auth/refresh` | Renouvellement token |
|
||||
| POST | `/api/v1/auth/logout` | Déconnexion |
|
||||
| GET | `/api/v1/auth/me` | Profil courant |
|
||||
|
||||
### 7.2 Wallet
|
||||
| Méthode | Endpoint | Description |
|
||||
|---------|----------|-------------|
|
||||
| GET | `/api/v1/wallet` | Solde |
|
||||
| GET | `/api/v1/wallet/transactions` | Historique |
|
||||
| POST | `/api/v1/wallet/top-up/initiate` | Démarrer un rechargement |
|
||||
| POST | `/api/v1/wallet/top-up/confirm` | Confirmation de rechargement |
|
||||
| POST | `/api/v1/wallet/top-up/webhook/{provider}` | Webhook PSP |
|
||||
|
||||
### 7.3 Établissements & machines
|
||||
| Méthode | Endpoint | Description |
|
||||
|---------|----------|-------------|
|
||||
| GET | `/api/v1/establishments` | Liste établissements |
|
||||
| GET | `/api/v1/establishments/{uuid}` | Détail établissement |
|
||||
| GET | `/api/v1/machines/{uuid}` | Détail machine |
|
||||
| GET | `/api/v1/machines/{uuid}/availability` | Créneaux disponibles |
|
||||
| GET | `/api/v1/machines/{uuid}/pricing` | Tarif actif |
|
||||
|
||||
### 7.4 Réservations
|
||||
| Méthode | Endpoint | Description |
|
||||
|---------|----------|-------------|
|
||||
| POST | `/api/v1/bookings` | Créer une réservation |
|
||||
| GET | `/api/v1/bookings` | Mes réservations |
|
||||
| GET | `/api/v1/bookings/{uuid}` | Détail réservation |
|
||||
| PATCH | `/api/v1/bookings/{uuid}/cancel` | Annuler |
|
||||
| PATCH | `/api/v1/bookings/{uuid}/move` | Déplacer |
|
||||
|
||||
### 7.5 Lavages
|
||||
| Méthode | Endpoint | Description |
|
||||
|---------|----------|-------------|
|
||||
| POST | `/api/v1/washes/start` | Démarrer un lavage |
|
||||
| GET | `/api/v1/washes` | Historique |
|
||||
| GET | `/api/v1/washes/{uuid}` | Détail |
|
||||
|
||||
### 7.6 Superviseur
|
||||
| Méthode | Endpoint | Description |
|
||||
|---------|----------|-------------|
|
||||
| POST | `/api/v1/supervisor/auth/login` | Connexion superviseur |
|
||||
| POST | `/api/v1/supervisor/auth/logout` | Déconnexion |
|
||||
| GET | `/api/v1/supervisor/dashboard` | Dashboard |
|
||||
| GET | `/api/v1/supervisor/machines` | Parc machines |
|
||||
| GET | `/api/v1/supervisor/bookings` | Réservations |
|
||||
| GET | `/api/v1/supervisor/washes` | Lavages |
|
||||
| GET | `/api/v1/supervisor/pricing` | Tarifs |
|
||||
| POST | `/api/v1/supervisor/pricing` | Création tarif |
|
||||
| PUT | `/api/v1/supervisor/pricing/{id}` | Modification tarif |
|
||||
| GET | `/api/v1/supervisor/promotions` | Promotions |
|
||||
|
||||
---
|
||||
|
||||
## 8. Règles métier critiques
|
||||
|
||||
### 8.1 Wallet
|
||||
- aucune écriture directe de solde hors `WalletService`,
|
||||
- toute opération critique porte une clé d'idempotence,
|
||||
- séparation stricte entre transaction de paiement externe et mouvement de wallet interne.
|
||||
|
||||
### 8.2 Réservations
|
||||
- un créneau machine ne peut jamais être réservé deux fois,
|
||||
- création de réservation sous transaction avec verrou logique,
|
||||
- pénalité et remboursement centralisés dans `BookingService`.
|
||||
|
||||
### 8.3 Lavages
|
||||
- un lavage ne peut démarrer que si la machine est éligible,
|
||||
- toute commande machine doit être historisée,
|
||||
- toute fin de cycle doit être confirmée par événement ou simulation contrôlée.
|
||||
|
||||
### 8.4 Scoping exploitant
|
||||
- toute requête superviseur doit être filtrée par `organization_id`,
|
||||
- si `establishment_id` est renseigné sur le superviseur, la visibilité est limitée à cet établissement.
|
||||
|
||||
---
|
||||
|
||||
## 9. Tâches planifiées
|
||||
|
||||
| Fréquence | Job | Description |
|
||||
|-----------|-----|-------------|
|
||||
| Toutes les 5 min | `CheckNoShowBookings` | Détection des no-shows |
|
||||
| Toutes les 5 min | `RefreshOfflineMachines` | Contrôle heartbeat / statuts |
|
||||
| Toutes les heures | `SendScheduledNotifications` | Notifications en attente |
|
||||
| Chaque nuit | `GenerateDailyStats` | Agrégats journaliers |
|
||||
| Chaque nuit | `PurgeRevokedTokens` | Nettoyage technique |
|
||||
| Chaque semaine | `AnonymizeInactiveUsers` | Politique RGPD |
|
||||
|
||||
---
|
||||
|
||||
## 10. Sécurité
|
||||
|
||||
### 10.1 Général
|
||||
- HTTPS obligatoire,
|
||||
- validation stricte des entrées,
|
||||
- rate limiting sur auth, paiements et endpoints techniques,
|
||||
- headers de sécurité côté web,
|
||||
- UUID publics dans l'API.
|
||||
|
||||
### 10.2 Paiements
|
||||
- vérification systématique des webhooks,
|
||||
- aucune confiance dans le retour client seul,
|
||||
- conservation du payload brut du PSP.
|
||||
|
||||
### 10.3 Intégrations machines
|
||||
- API key dédiée ou signature HMAC,
|
||||
- journalisation des appels,
|
||||
- horodatage et contrôle anti-rejeu si possible.
|
||||
|
||||
---
|
||||
|
||||
## 11. Observabilité
|
||||
|
||||
- Sentry backend,
|
||||
- logs structurés JSON,
|
||||
- suivi des jobs Laravel Horizon,
|
||||
- métriques minimales :
|
||||
- taux d'échec commandes machines,
|
||||
- taux d'échec paiements,
|
||||
- machines offline,
|
||||
- jobs en échec,
|
||||
- no-shows.
|
||||
|
||||
---
|
||||
|
||||
## 12. Tests
|
||||
|
||||
| Type | Outil | Portée |
|
||||
|------|-------|--------|
|
||||
| Unitaires | Pest | Wallet, Booking, Pricing, Machine provider |
|
||||
| Intégration | Pest + MySQL réel | Endpoints critiques |
|
||||
| Contrat | Payloads partenaires | Intégration machine / paiement |
|
||||
| Charge | k6 | Wallet, booking, start wash |
|
||||
|
||||
### Cas critiques obligatoires
|
||||
- double webhook paiement,
|
||||
- double réservation même créneau,
|
||||
- timeout commande machine,
|
||||
- événement machine reçu en doublon,
|
||||
- annulation et no-show simultanés,
|
||||
- scoping superviseur inter-organisation.
|
||||
|
||||
---
|
||||
|
||||
## 13. Livrables attendus
|
||||
|
||||
- code source Laravel,
|
||||
- migrations et seeders,
|
||||
- documentation OpenAPI,
|
||||
- `.env.example`,
|
||||
- Docker pour dev,
|
||||
- suite de tests critique,
|
||||
- mode simulation machine,
|
||||
- données de démonstration multi-laveries.
|
||||
Reference in New Issue
Block a user