feat: Release version 3.1.4 - Mode terrain et génération PDF
✨ Nouvelles fonctionnalités: - Ajout du mode terrain pour utilisation mobile hors connexion - Génération automatique de reçus PDF avec template personnalisé - Révision complète du système de cartes avec amélioration des performances 🔧 Améliorations techniques: - Refactoring du module chat avec architecture simplifiée - Optimisation du système de sécurité NIST SP 800-63B - Amélioration de la gestion des secteurs géographiques - Support UTF-8 étendu pour les noms d'utilisateurs 📱 Application mobile: - Nouveau mode terrain dans user_field_mode_page - Interface utilisateur adaptative pour conditions difficiles - Synchronisation offline améliorée 🗺️ Cartographie: - Optimisation des performances MapBox - Meilleure gestion des tuiles hors ligne - Amélioration de l'affichage des secteurs 📄 Documentation: - Ajout guide Android (ANDROID-GUIDE.md) - Documentation sécurité API (API-SECURITY.md) - Guide module chat (CHAT_MODULE.md) 🐛 Corrections: - Résolution des erreurs 400 lors de la création d'utilisateurs - Correction de la validation des noms d'utilisateurs - Fix des problèmes de synchronisation chat 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
419
api/docs/CHAT_MODULE.md
Normal file
419
api/docs/CHAT_MODULE.md
Normal file
@@ -0,0 +1,419 @@
|
||||
# Module Chat - Documentation API
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le module Chat permet aux utilisateurs de l'application GeoSector de communiquer entre eux via une messagerie intégrée. Il supporte les conversations privées, de groupe et les diffusions (broadcast).
|
||||
|
||||
## Architecture
|
||||
|
||||
### Tables de base de données
|
||||
|
||||
- `chat_rooms` : Salles de conversation
|
||||
- `chat_messages` : Messages échangés
|
||||
- `chat_participants` : Participants aux conversations
|
||||
- `chat_read_receipts` : Accusés de lecture
|
||||
|
||||
### Permissions par rôle
|
||||
|
||||
| Rôle | Permissions |
|
||||
|------|------------|
|
||||
| **1 - Utilisateur** | Conversations privées et groupes avec membres de son entité |
|
||||
| **2 - Admin entité** | Toutes conversations de son entité + création de diffusions |
|
||||
| **> 2 - Super admin** | Accès total à toutes les conversations |
|
||||
|
||||
## Flux d'utilisation du module Chat
|
||||
|
||||
### 📱 Vue d'ensemble du flux
|
||||
|
||||
Le module Chat fonctionne en mode **chargement dynamique** : les données sont récupérées à la demande, pas toutes en une fois au login.
|
||||
|
||||
### 1. Au login (`/api/login`)
|
||||
|
||||
La réponse du login contient un objet `chat` avec les informations de base :
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"user": {...},
|
||||
"amicale": {...},
|
||||
"chat": {
|
||||
"total_rooms": 5, // Nombre total de conversations
|
||||
"unread_messages": 12, // Total messages non lus
|
||||
"chat_enabled": true, // Module activé pour cet utilisateur
|
||||
"last_active_room": { // Dernière conversation active
|
||||
"id": "uuid-room-123",
|
||||
"title": "Discussion équipe",
|
||||
"type": "group",
|
||||
"last_message": "À demain !",
|
||||
"last_message_at": "2025-01-17 18:30:00"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
→ Permet d'afficher un **badge de notification** et de savoir si le chat est disponible
|
||||
|
||||
### 2. Ouverture de la page Chat
|
||||
|
||||
#### Étape 1 : Chargement initial
|
||||
```
|
||||
GET /api/chat/rooms
|
||||
```
|
||||
→ Récupère la liste des conversations avec aperçu du dernier message
|
||||
|
||||
#### Étape 2 : Sélection d'une conversation
|
||||
```
|
||||
GET /api/chat/rooms/{room_id}/messages?limit=50
|
||||
```
|
||||
→ Charge les 50 derniers messages (pagination disponible)
|
||||
|
||||
#### Étape 3 : Marquage comme lu
|
||||
```
|
||||
POST /api/chat/rooms/{room_id}/read
|
||||
```
|
||||
→ Met à jour les compteurs de messages non lus
|
||||
|
||||
### 3. Actions utilisateur
|
||||
|
||||
| Action | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| **Envoyer un message** | `POST /api/chat/rooms/{id}/messages` | Envoie et retourne le message créé |
|
||||
| **Créer une conversation** | `POST /api/chat/rooms` | Crée une nouvelle room |
|
||||
| **Obtenir les destinataires** | `GET /api/chat/recipients` | Liste des contacts disponibles |
|
||||
| **Charger plus de messages** | `GET /api/chat/rooms/{id}/messages?before={msg_id}` | Pagination |
|
||||
|
||||
### 4. Stratégies de rafraîchissement
|
||||
|
||||
#### Polling (recommandé pour débuter)
|
||||
- Rafraîchir `/api/chat/rooms` toutes les 30 secondes
|
||||
- Rafraîchir les messages de la conversation active toutes les 10 secondes
|
||||
|
||||
#### Pull to refresh
|
||||
- Permettre à l'utilisateur de rafraîchir manuellement
|
||||
|
||||
#### Lifecycle events
|
||||
- Recharger quand l'app revient au premier plan
|
||||
- Rafraîchir après envoi d'un message
|
||||
|
||||
### 5. Exemple d'implémentation Flutter
|
||||
|
||||
```dart
|
||||
class ChatService {
|
||||
Timer? _roomsTimer;
|
||||
Timer? _messagesTimer;
|
||||
|
||||
// 1. Au login, stocker les infos de base
|
||||
void initFromLogin(Map<String, dynamic> chatData) {
|
||||
_unreadCount = chatData['unread_messages'];
|
||||
_chatEnabled = chatData['chat_enabled'];
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 2. À l'ouverture du chat
|
||||
Future<void> openChatPage() async {
|
||||
// Charger les conversations
|
||||
final rooms = await api.get('/api/chat/rooms');
|
||||
_rooms = rooms['rooms'];
|
||||
|
||||
// Démarrer le polling
|
||||
_startPolling();
|
||||
}
|
||||
|
||||
// 3. Sélection d'une conversation
|
||||
Future<void> selectRoom(String roomId) async {
|
||||
// Charger les messages
|
||||
final response = await api.get('/api/chat/rooms/$roomId/messages');
|
||||
_currentMessages = response['messages'];
|
||||
|
||||
// Marquer comme lu
|
||||
await api.post('/api/chat/rooms/$roomId/read');
|
||||
|
||||
// Rafraîchir plus fréquemment cette conversation
|
||||
_startMessagePolling(roomId);
|
||||
}
|
||||
|
||||
// 4. Polling automatique
|
||||
void _startPolling() {
|
||||
_roomsTimer = Timer.periodic(Duration(seconds: 30), (_) {
|
||||
_refreshRooms();
|
||||
});
|
||||
}
|
||||
|
||||
// 5. Nettoyage
|
||||
void dispose() {
|
||||
_roomsTimer?.cancel();
|
||||
_messagesTimer?.cancel();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Endpoints API
|
||||
|
||||
### 1. GET /api/chat/rooms
|
||||
**Description** : Récupère la liste des conversations de l'utilisateur
|
||||
|
||||
**Réponse** :
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"rooms": [
|
||||
{
|
||||
"id": "uuid-room-1",
|
||||
"title": "Discussion équipe",
|
||||
"type": "group",
|
||||
"created_at": "2025-01-17 10:00:00",
|
||||
"created_by": 123,
|
||||
"updated_at": "2025-01-17 14:30:00",
|
||||
"last_message": "Bonjour tout le monde",
|
||||
"last_message_at": "2025-01-17 14:30:00",
|
||||
"unread_count": 3,
|
||||
"participant_count": 5,
|
||||
"participants": [
|
||||
{
|
||||
"user_id": 123,
|
||||
"name": "Jean Dupont",
|
||||
"first_name": "Jean",
|
||||
"is_admin": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. POST /api/chat/rooms
|
||||
**Description** : Crée une nouvelle conversation
|
||||
|
||||
**Body** :
|
||||
```json
|
||||
{
|
||||
"type": "private|group|broadcast",
|
||||
"title": "Titre optionnel (requis pour group/broadcast)",
|
||||
"participants": [456, 789], // IDs des participants
|
||||
"initial_message": "Message initial optionnel"
|
||||
}
|
||||
```
|
||||
|
||||
**Règles** :
|
||||
- `private` : Maximum 2 participants (incluant le créateur)
|
||||
- `group` : Plusieurs participants possibles
|
||||
- `broadcast` : Réservé aux admins (rôle >= 2)
|
||||
|
||||
**Réponse** :
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"room": {
|
||||
"id": "uuid-new-room",
|
||||
"title": "Nouvelle conversation",
|
||||
"type": "group",
|
||||
"created_at": "2025-01-17 15:00:00",
|
||||
"participants": [...]
|
||||
},
|
||||
"existing": false // true si conversation privée existante trouvée
|
||||
}
|
||||
```
|
||||
|
||||
### 3. GET /api/chat/rooms/{id}/messages
|
||||
**Description** : Récupère les messages d'une conversation
|
||||
|
||||
**Paramètres** :
|
||||
- `limit` : Nombre de messages (défaut: 50, max: 100)
|
||||
- `before` : ID du message pour pagination
|
||||
|
||||
**Réponse** :
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"messages": [
|
||||
{
|
||||
"id": "uuid-message-1",
|
||||
"content": "Bonjour !",
|
||||
"sender_id": 123,
|
||||
"sender_name": "Jean Dupont",
|
||||
"sender_first_name": "Jean",
|
||||
"sent_at": "2025-01-17 14:00:00",
|
||||
"edited_at": null,
|
||||
"is_deleted": false,
|
||||
"is_read": true,
|
||||
"is_mine": false,
|
||||
"read_count": 3
|
||||
}
|
||||
],
|
||||
"has_more": true
|
||||
}
|
||||
```
|
||||
|
||||
### 4. POST /api/chat/rooms/{id}/messages
|
||||
**Description** : Envoie un message dans une conversation
|
||||
|
||||
**Body** :
|
||||
```json
|
||||
{
|
||||
"content": "Contenu du message (max 5000 caractères)"
|
||||
}
|
||||
```
|
||||
|
||||
**Réponse** :
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": {
|
||||
"id": "uuid-new-message",
|
||||
"content": "Message envoyé",
|
||||
"sender_id": 123,
|
||||
"sender_name": "Jean Dupont",
|
||||
"sent_at": "2025-01-17 15:30:00",
|
||||
"is_mine": true,
|
||||
"is_read": false,
|
||||
"read_count": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. POST /api/chat/rooms/{id}/read
|
||||
**Description** : Marque les messages comme lus
|
||||
|
||||
**Body (optionnel)** :
|
||||
```json
|
||||
{
|
||||
"message_ids": ["uuid-1", "uuid-2"] // Si omis, marque tous les messages
|
||||
}
|
||||
```
|
||||
|
||||
**Réponse** :
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"unread_count": 0 // Nombre de messages non lus restants
|
||||
}
|
||||
```
|
||||
|
||||
### 6. GET /api/chat/recipients
|
||||
**Description** : Liste des destinataires possibles pour créer une conversation
|
||||
|
||||
**Réponse** :
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"recipients": [
|
||||
{
|
||||
"id": 456,
|
||||
"name": "Marie Martin",
|
||||
"first_name": "Marie",
|
||||
"role": 1,
|
||||
"entite_id": 5
|
||||
}
|
||||
],
|
||||
"recipients_by_entity": {
|
||||
"Amicale de Grenoble": [
|
||||
{...}
|
||||
],
|
||||
"Amicale de Lyon": [
|
||||
{...}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Fonctionnalités clés
|
||||
|
||||
### 1. Types de conversations
|
||||
|
||||
#### Private (Conversation privée)
|
||||
- Entre 2 utilisateurs uniquement
|
||||
- Détection automatique de conversation existante
|
||||
- Pas de titre requis
|
||||
|
||||
#### Group (Groupe)
|
||||
- Plusieurs participants
|
||||
- Titre optionnel mais recommandé
|
||||
- Admin de groupe (créateur)
|
||||
|
||||
#### Broadcast (Diffusion)
|
||||
- Réservé aux admins (rôle >= 2)
|
||||
- Communication unidirectionnelle possible
|
||||
- Pour annonces importantes
|
||||
|
||||
### 2. Gestion des permissions
|
||||
|
||||
Le système vérifie automatiquement :
|
||||
- L'appartenance à une conversation avant lecture/écriture
|
||||
- Les droits de création selon le type de conversation
|
||||
- La visibilité des destinataires selon le rôle
|
||||
|
||||
### 3. Statuts de lecture
|
||||
|
||||
- **Accusés de lecture individuels** : Chaque message peut être marqué comme lu
|
||||
- **Compteur de non-lus** : Par conversation et global
|
||||
- **Last read** : Timestamp de dernière lecture par participant
|
||||
|
||||
### 4. Optimisations
|
||||
|
||||
- **Pagination** : Chargement progressif des messages
|
||||
- **Index optimisés** : Pour les requêtes fréquentes
|
||||
- **Vue SQL** : Pour récupération rapide du dernier message
|
||||
|
||||
## Sécurité
|
||||
|
||||
### Chiffrement
|
||||
- Les noms d'utilisateurs sont stockés chiffrés (AES-256)
|
||||
- Déchiffrement à la volée lors de la lecture
|
||||
|
||||
### Validation
|
||||
- Longueur maximale des messages : 5000 caractères
|
||||
- Trim automatique du contenu
|
||||
- Vérification des permissions à chaque action
|
||||
|
||||
### Isolation
|
||||
- Les utilisateurs ne voient que leurs conversations autorisées
|
||||
- Filtrage par entité selon le rôle
|
||||
- Soft delete pour conservation de l'historique
|
||||
|
||||
## Migration
|
||||
|
||||
Exécuter le script SQL :
|
||||
```bash
|
||||
mysql -u root -p geo_app < scripts/sql/create_chat_tables.sql
|
||||
```
|
||||
|
||||
## Évolutions futures possibles
|
||||
|
||||
1. **Notifications push** : Intégration avec Firebase/WebSocket
|
||||
2. **Fichiers joints** : Support d'images et documents
|
||||
3. **Réactions** : Emojis sur les messages
|
||||
4. **Mentions** : @username pour notifier
|
||||
5. **Recherche** : Dans l'historique des messages
|
||||
6. **Chiffrement E2E** : Pour conversations sensibles
|
||||
7. **Statuts de présence** : En ligne/Hors ligne
|
||||
8. **Indicateur de frappe** : "X est en train d'écrire..."
|
||||
|
||||
## Tests
|
||||
|
||||
### Cas de test recommandés
|
||||
|
||||
1. **Création de conversation privée**
|
||||
- Vérifier la détection de conversation existante
|
||||
- Tester avec utilisateurs de différentes entités
|
||||
|
||||
2. **Envoi de messages**
|
||||
- Messages avec caractères UTF-8 (émojis, accents)
|
||||
- Messages très longs (limite 5000)
|
||||
- Messages vides (doivent être rejetés)
|
||||
|
||||
3. **Marquage comme lu**
|
||||
- Marquer messages spécifiques
|
||||
- Marquer tous les messages d'une room
|
||||
- Vérifier les compteurs
|
||||
|
||||
4. **Permissions**
|
||||
- Utilisateur simple ne peut pas créer de broadcast
|
||||
- Accès refusé aux conversations non autorisées
|
||||
- Filtrage correct des destinataires
|
||||
|
||||
## Support
|
||||
|
||||
Pour toute question ou problème :
|
||||
- Vérifier les logs dans `/logs/`
|
||||
- Consulter les tables `chat_*` en base de données
|
||||
- Tester avec les scripts de test fournis
|
||||
Reference in New Issue
Block a user