feat: WebSocket temps réel + Finance Workflow + corrections
- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics) * Backend: KafkaEventProducer, KafkaEventConsumer * Mobile: WebSocketService (reconnection, heartbeat, typed events) * DashboardBloc: Auto-refresh depuis WebSocket events - Finance Workflow: approbations + budgets (backend + mobile) * Backend: entities, services, resources, migrations Flyway V6 * Mobile: features finance_workflow complète avec BLoC - Corrections DI: interfaces IRepository partout * IProfileRepository, IOrganizationRepository, IMembreRepository * GetIt configuré avec @injectable - Spec-Kit: constitution + templates mis à jour * .specify/memory/constitution.md enrichie * Templates agent, plan, spec, tasks, checklist - Nettoyage: fichiers temporaires supprimés Signed-off-by: lions dev Team
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
/// Repository pour la gestion des événements
|
||||
/// Implémentation du repository pour la gestion des événements
|
||||
/// Interface avec l'API backend EvenementResource
|
||||
library evenement_repository;
|
||||
library evenement_repository_impl;
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:unionflow_mobile_apps/core/network/api_client.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../domain/repositories/evenement_repository.dart';
|
||||
import '../models/evenement_model.dart';
|
||||
|
||||
/// Résultat de recherche paginé
|
||||
@@ -49,55 +52,30 @@ class EvenementSearchResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface du repository des événements
|
||||
abstract class EvenementRepository {
|
||||
/// Récupère la liste des événements avec pagination
|
||||
Future<EvenementSearchResult> getEvenements({
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? recherche,
|
||||
});
|
||||
|
||||
/// Récupère un événement par son ID
|
||||
Future<EvenementModel?> getEvenementById(String id);
|
||||
|
||||
/// Crée un nouvel événement
|
||||
Future<EvenementModel> createEvenement(EvenementModel evenement);
|
||||
|
||||
/// Met à jour un événement
|
||||
Future<EvenementModel> updateEvenement(String id, EvenementModel evenement);
|
||||
|
||||
/// Supprime un événement
|
||||
Future<void> deleteEvenement(String id);
|
||||
|
||||
/// Récupère les événements à venir
|
||||
Future<EvenementSearchResult> getEvenementsAVenir({int page = 0, int size = 20});
|
||||
|
||||
/// Récupère les événements en cours
|
||||
Future<EvenementSearchResult> getEvenementsEnCours({int page = 0, int size = 20});
|
||||
|
||||
/// Récupère les événements passés
|
||||
Future<EvenementSearchResult> getEvenementsPasses({int page = 0, int size = 20});
|
||||
|
||||
/// S'inscrire à un événement
|
||||
Future<void> inscrireEvenement(String evenementId);
|
||||
|
||||
/// Se désinscrire d'un événement
|
||||
Future<void> desinscrireEvenement(String evenementId);
|
||||
|
||||
/// Récupère les participants d'un événement
|
||||
Future<List<Map<String, dynamic>>> getParticipants(String evenementId);
|
||||
|
||||
/// Récupère les statistiques des événements
|
||||
Future<Map<String, dynamic>> getEvenementsStats();
|
||||
}
|
||||
|
||||
/// Implémentation du repository des événements
|
||||
class EvenementRepositoryImpl implements EvenementRepository {
|
||||
final Dio _dio;
|
||||
@LazySingleton(as: IEvenementRepository)
|
||||
class EvenementRepositoryImpl implements IEvenementRepository {
|
||||
final ApiClient _apiClient;
|
||||
static const String _baseUrl = '/api/evenements';
|
||||
|
||||
EvenementRepositoryImpl(this._dio);
|
||||
EvenementRepositoryImpl(this._apiClient);
|
||||
|
||||
/// Parse une réponse API : liste directe ou objet paginé (content / data).
|
||||
EvenementSearchResult _parseSearchResponse(dynamic data, int page, int size) {
|
||||
if (data is List) {
|
||||
final evenements = (data as List<dynamic>)
|
||||
.map((e) => EvenementModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
return EvenementSearchResult(
|
||||
evenements: evenements,
|
||||
total: evenements.length,
|
||||
page: page,
|
||||
size: size,
|
||||
totalPages: 1,
|
||||
);
|
||||
}
|
||||
return EvenementSearchResult.fromJson(data as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<EvenementSearchResult> getEvenements({
|
||||
@@ -115,7 +93,7 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
queryParams['recherche'] = recherche;
|
||||
}
|
||||
|
||||
final response = await _dio.get(
|
||||
final response = await _apiClient.get(
|
||||
_baseUrl,
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
@@ -155,7 +133,7 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<EvenementModel?> getEvenementById(String id) async {
|
||||
try {
|
||||
final response = await _dio.get('$_baseUrl/$id');
|
||||
final response = await _apiClient.get('$_baseUrl/$id');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return EvenementModel.fromJson(response.data as Map<String, dynamic>);
|
||||
@@ -177,7 +155,7 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<EvenementModel> createEvenement(EvenementModel evenement) async {
|
||||
try {
|
||||
final response = await _dio.post(
|
||||
final response = await _apiClient.post(
|
||||
_baseUrl,
|
||||
data: evenement.toJson(),
|
||||
);
|
||||
@@ -197,7 +175,7 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<EvenementModel> updateEvenement(String id, EvenementModel evenement) async {
|
||||
try {
|
||||
final response = await _dio.put(
|
||||
final response = await _apiClient.put(
|
||||
'$_baseUrl/$id',
|
||||
data: evenement.toJson(),
|
||||
);
|
||||
@@ -217,7 +195,7 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<void> deleteEvenement(String id) async {
|
||||
try {
|
||||
final response = await _dio.delete('$_baseUrl/$id');
|
||||
final response = await _apiClient.delete('$_baseUrl/$id');
|
||||
|
||||
if (response.statusCode != 204 && response.statusCode != 200) {
|
||||
throw Exception('Erreur lors de la suppression de l\'événement: ${response.statusCode}');
|
||||
@@ -232,13 +210,13 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<EvenementSearchResult> getEvenementsAVenir({int page = 0, int size = 20}) async {
|
||||
try {
|
||||
final response = await _dio.get(
|
||||
final response = await _apiClient.get(
|
||||
'$_baseUrl/a-venir',
|
||||
queryParameters: {'page': page, 'size': size},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return EvenementSearchResult.fromJson(response.data as Map<String, dynamic>);
|
||||
return _parseSearchResponse(response.data, page, size);
|
||||
} else {
|
||||
throw Exception('Erreur lors de la récupération des événements à venir: ${response.statusCode}');
|
||||
}
|
||||
@@ -252,13 +230,13 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<EvenementSearchResult> getEvenementsEnCours({int page = 0, int size = 20}) async {
|
||||
try {
|
||||
final response = await _dio.get(
|
||||
final response = await _apiClient.get(
|
||||
'$_baseUrl/en-cours',
|
||||
queryParameters: {'page': page, 'size': size},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return EvenementSearchResult.fromJson(response.data as Map<String, dynamic>);
|
||||
return _parseSearchResponse(response.data, page, size);
|
||||
} else {
|
||||
throw Exception('Erreur lors de la récupération des événements en cours: ${response.statusCode}');
|
||||
}
|
||||
@@ -272,13 +250,13 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<EvenementSearchResult> getEvenementsPasses({int page = 0, int size = 20}) async {
|
||||
try {
|
||||
final response = await _dio.get(
|
||||
final response = await _apiClient.get(
|
||||
'$_baseUrl/passes',
|
||||
queryParameters: {'page': page, 'size': size},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return EvenementSearchResult.fromJson(response.data as Map<String, dynamic>);
|
||||
return _parseSearchResponse(response.data, page, size);
|
||||
} else {
|
||||
throw Exception('Erreur lors de la récupération des événements passés: ${response.statusCode}');
|
||||
}
|
||||
@@ -292,7 +270,7 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<void> inscrireEvenement(String evenementId) async {
|
||||
try {
|
||||
final response = await _dio.post('$_baseUrl/$evenementId/inscrire');
|
||||
final response = await _apiClient.post('$_baseUrl/$evenementId/inscrire');
|
||||
|
||||
if (response.statusCode != 200 && response.statusCode != 201) {
|
||||
throw Exception('Erreur lors de l\'inscription à l\'événement: ${response.statusCode}');
|
||||
@@ -307,7 +285,7 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<void> desinscrireEvenement(String evenementId) async {
|
||||
try {
|
||||
final response = await _dio.delete('$_baseUrl/$evenementId/desinscrire');
|
||||
final response = await _apiClient.delete('$_baseUrl/$evenementId/desinscrire');
|
||||
|
||||
if (response.statusCode != 200 && response.statusCode != 204) {
|
||||
throw Exception('Erreur lors de la désinscription de l\'événement: ${response.statusCode}');
|
||||
@@ -322,7 +300,7 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
@override
|
||||
Future<List<Map<String, dynamic>>> getParticipants(String evenementId) async {
|
||||
try {
|
||||
final response = await _dio.get('$_baseUrl/$evenementId/participants');
|
||||
final response = await _apiClient.get('$_baseUrl/$evenementId/participants');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return (response.data as List<dynamic>)
|
||||
@@ -338,10 +316,26 @@ class EvenementRepositoryImpl implements EvenementRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> getInscriptionStatus(String evenementId) async {
|
||||
try {
|
||||
final response = await _apiClient.get('$_baseUrl/$evenementId/me/inscrit');
|
||||
if (response.statusCode == 200 && response.data is Map) {
|
||||
final data = response.data as Map<String, dynamic>;
|
||||
return data['inscrit'] == true;
|
||||
}
|
||||
return false;
|
||||
} on DioException catch (_) {
|
||||
return false;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> getEvenementsStats() async {
|
||||
try {
|
||||
final response = await _dio.get('$_baseUrl/statistiques');
|
||||
final response = await _apiClient.get('$_baseUrl/statistiques');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response.data as Map<String, dynamic>;
|
||||
|
||||
Reference in New Issue
Block a user