Files
unionflow-server-api/unionflow-mobile-apps/lib/core/services/api_service.dart
2025-09-13 19:05:06 +00:00

401 lines
14 KiB
Dart

import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import '../models/membre_model.dart';
import '../models/cotisation_model.dart';
import '../models/wave_checkout_session_model.dart';
import '../network/dio_client.dart';
/// Service API principal pour communiquer avec le serveur UnionFlow
@singleton
class ApiService {
final DioClient _dioClient;
ApiService(this._dioClient);
Dio get _dio => _dioClient.dio;
// ========================================
// MEMBRES
// ========================================
/// Récupère la liste de tous les membres actifs
Future<List<MembreModel>> getMembres() async {
try {
final response = await _dio.get('/api/membres');
if (response.data is List) {
return (response.data as List)
.map((json) => MembreModel.fromJson(json as Map<String, dynamic>))
.toList();
}
throw Exception('Format de réponse invalide pour la liste des membres');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération des membres');
}
}
/// Récupère un membre par son ID
Future<MembreModel> getMembreById(String id) async {
try {
final response = await _dio.get('/api/membres/$id');
return MembreModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération du membre');
}
}
/// Crée un nouveau membre
Future<MembreModel> createMembre(MembreModel membre) async {
try {
final response = await _dio.post(
'/api/membres',
data: membre.toJson(),
);
return MembreModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la création du membre');
}
}
/// Met à jour un membre existant
Future<MembreModel> updateMembre(String id, MembreModel membre) async {
try {
final response = await _dio.put(
'/api/membres/$id',
data: membre.toJson(),
);
return MembreModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la mise à jour du membre');
}
}
/// Désactive un membre
Future<void> deleteMembre(String id) async {
try {
await _dio.delete('/api/membres/$id');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la suppression du membre');
}
}
/// Recherche des membres par nom ou prénom
Future<List<MembreModel>> searchMembres(String query) async {
try {
final response = await _dio.get(
'/api/membres/recherche',
queryParameters: {'q': query},
);
if (response.data is List) {
return (response.data as List)
.map((json) => MembreModel.fromJson(json as Map<String, dynamic>))
.toList();
}
throw Exception('Format de réponse invalide pour la recherche');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la recherche de membres');
}
}
/// Récupère les statistiques des membres
Future<Map<String, dynamic>> getMembresStats() async {
try {
final response = await _dio.get('/api/membres/stats');
return response.data as Map<String, dynamic>;
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération des statistiques');
}
}
// ========================================
// PAIEMENTS WAVE
// ========================================
/// Crée une session de paiement Wave
Future<WaveCheckoutSessionModel> createWaveSession({
required double montant,
required String devise,
required String successUrl,
required String errorUrl,
String? organisationId,
String? membreId,
String? typePaiement,
String? description,
}) async {
try {
final response = await _dio.post(
'/api/paiements/wave/sessions',
data: {
'montant': montant,
'devise': devise,
'successUrl': successUrl,
'errorUrl': errorUrl,
if (organisationId != null) 'organisationId': organisationId,
if (membreId != null) 'membreId': membreId,
if (typePaiement != null) 'typePaiement': typePaiement,
if (description != null) 'description': description,
},
);
return WaveCheckoutSessionModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la création de la session Wave');
}
}
/// Récupère une session de paiement Wave par son ID
Future<WaveCheckoutSessionModel> getWaveSession(String sessionId) async {
try {
final response = await _dio.get('/api/paiements/wave/sessions/$sessionId');
return WaveCheckoutSessionModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération de la session Wave');
}
}
/// Vérifie le statut d'une session de paiement Wave
Future<String> checkWaveSessionStatus(String sessionId) async {
try {
final response = await _dio.get('/api/paiements/wave/sessions/$sessionId/status');
return response.data['statut'] as String;
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la vérification du statut Wave');
}
}
// ========================================
// COTISATIONS
// ========================================
/// Récupère la liste de toutes les cotisations avec pagination
Future<List<CotisationModel>> getCotisations({int page = 0, int size = 20}) async {
try {
final response = await _dio.get('/api/cotisations', queryParameters: {
'page': page,
'size': size,
});
if (response.data is List) {
return (response.data as List)
.map((json) => CotisationModel.fromJson(json as Map<String, dynamic>))
.toList();
}
throw Exception('Format de réponse invalide pour la liste des cotisations');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération des cotisations');
}
}
/// Récupère une cotisation par son ID
Future<CotisationModel> getCotisationById(String id) async {
try {
final response = await _dio.get('/api/cotisations/$id');
return CotisationModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération de la cotisation');
}
}
/// Récupère une cotisation par son numéro de référence
Future<CotisationModel> getCotisationByReference(String numeroReference) async {
try {
final response = await _dio.get('/api/cotisations/reference/$numeroReference');
return CotisationModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération de la cotisation');
}
}
/// Crée une nouvelle cotisation
Future<CotisationModel> createCotisation(CotisationModel cotisation) async {
try {
final response = await _dio.post(
'/api/cotisations',
data: cotisation.toJson(),
);
return CotisationModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la création de la cotisation');
}
}
/// Met à jour une cotisation existante
Future<CotisationModel> updateCotisation(String id, CotisationModel cotisation) async {
try {
final response = await _dio.put(
'/api/cotisations/$id',
data: cotisation.toJson(),
);
return CotisationModel.fromJson(response.data as Map<String, dynamic>);
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la mise à jour de la cotisation');
}
}
/// Supprime une cotisation
Future<void> deleteCotisation(String id) async {
try {
await _dio.delete('/api/cotisations/$id');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la suppression de la cotisation');
}
}
/// Récupère les cotisations d'un membre
Future<List<CotisationModel>> getCotisationsByMembre(String membreId, {int page = 0, int size = 20}) async {
try {
final response = await _dio.get('/api/cotisations/membre/$membreId', queryParameters: {
'page': page,
'size': size,
});
if (response.data is List) {
return (response.data as List)
.map((json) => CotisationModel.fromJson(json as Map<String, dynamic>))
.toList();
}
throw Exception('Format de réponse invalide pour les cotisations du membre');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération des cotisations du membre');
}
}
/// Récupère les cotisations par statut
Future<List<CotisationModel>> getCotisationsByStatut(String statut, {int page = 0, int size = 20}) async {
try {
final response = await _dio.get('/api/cotisations/statut/$statut', queryParameters: {
'page': page,
'size': size,
});
if (response.data is List) {
return (response.data as List)
.map((json) => CotisationModel.fromJson(json as Map<String, dynamic>))
.toList();
}
throw Exception('Format de réponse invalide pour les cotisations par statut');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération des cotisations par statut');
}
}
/// Récupère les cotisations en retard
Future<List<CotisationModel>> getCotisationsEnRetard({int page = 0, int size = 20}) async {
try {
final response = await _dio.get('/api/cotisations/en-retard', queryParameters: {
'page': page,
'size': size,
});
if (response.data is List) {
return (response.data as List)
.map((json) => CotisationModel.fromJson(json as Map<String, dynamic>))
.toList();
}
throw Exception('Format de réponse invalide pour les cotisations en retard');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération des cotisations en retard');
}
}
/// Recherche avancée de cotisations
Future<List<CotisationModel>> rechercherCotisations({
String? membreId,
String? statut,
String? typeCotisation,
int? annee,
int? mois,
int page = 0,
int size = 20,
}) async {
try {
final queryParams = <String, dynamic>{
'page': page,
'size': size,
};
if (membreId != null) queryParams['membreId'] = membreId;
if (statut != null) queryParams['statut'] = statut;
if (typeCotisation != null) queryParams['typeCotisation'] = typeCotisation;
if (annee != null) queryParams['annee'] = annee;
if (mois != null) queryParams['mois'] = mois;
final response = await _dio.get('/api/cotisations/recherche', queryParameters: queryParams);
if (response.data is List) {
return (response.data as List)
.map((json) => CotisationModel.fromJson(json as Map<String, dynamic>))
.toList();
}
throw Exception('Format de réponse invalide pour la recherche de cotisations');
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la recherche de cotisations');
}
}
/// Récupère les statistiques des cotisations
Future<Map<String, dynamic>> getCotisationsStats() async {
try {
final response = await _dio.get('/api/cotisations/stats');
return response.data as Map<String, dynamic>;
} on DioException catch (e) {
throw _handleDioException(e, 'Erreur lors de la récupération des statistiques des cotisations');
}
}
// ========================================
// GESTION DES ERREURS
// ========================================
/// Gère les exceptions Dio et les convertit en messages d'erreur appropriés
Exception _handleDioException(DioException e, String defaultMessage) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
case DioExceptionType.sendTimeout:
case DioExceptionType.receiveTimeout:
return Exception('Délai d\'attente dépassé. Vérifiez votre connexion internet.');
case DioExceptionType.badResponse:
final statusCode = e.response?.statusCode;
final responseData = e.response?.data;
if (statusCode == 400) {
if (responseData is Map && responseData.containsKey('message')) {
return Exception(responseData['message']);
}
return Exception('Données invalides');
} else if (statusCode == 401) {
return Exception('Non autorisé. Veuillez vous reconnecter.');
} else if (statusCode == 403) {
return Exception('Accès interdit');
} else if (statusCode == 404) {
return Exception('Ressource non trouvée');
} else if (statusCode == 500) {
return Exception('Erreur serveur. Veuillez réessayer plus tard.');
}
return Exception('$defaultMessage (Code: $statusCode)');
case DioExceptionType.cancel:
return Exception('Requête annulée');
case DioExceptionType.connectionError:
return Exception('Erreur de connexion. Vérifiez votre connexion internet.');
case DioExceptionType.badCertificate:
return Exception('Certificat SSL invalide');
case DioExceptionType.unknown:
default:
return Exception(defaultMessage);
}
}
}