Refactoring - Version OK

This commit is contained in:
dahoud
2025-11-17 16:02:04 +00:00
parent 3f00a26308
commit 3b9ffac8cd
198 changed files with 18010 additions and 11383 deletions

View File

@@ -0,0 +1,316 @@
/// Service pour la gestion des organisations
/// Couche de logique métier entre le repository et l'interface utilisateur
library organization_service;
import '../models/organization_model.dart';
import '../repositories/organization_repository.dart';
/// Service de gestion des organisations
class OrganizationService {
final OrganizationRepository _repository;
OrganizationService(this._repository);
/// Récupère la liste des organisations avec pagination et recherche
Future<List<OrganizationModel>> getOrganizations({
int page = 0,
int size = 20,
String? recherche,
}) async {
try {
return await _repository.getOrganizations(
page: page,
size: size,
recherche: recherche,
);
} catch (e) {
throw Exception('Erreur lors de la récupération des organisations: $e');
}
}
/// Récupère une organisation par son ID
Future<OrganizationModel?> getOrganizationById(String id) async {
if (id.isEmpty) {
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
}
try {
return await _repository.getOrganizationById(id);
} catch (e) {
throw Exception('Erreur lors de la récupération de l\'organisation: $e');
}
}
/// Crée une nouvelle organisation avec validation
Future<OrganizationModel> createOrganization(OrganizationModel organization) async {
// Validation des données obligatoires
_validateOrganization(organization);
try {
return await _repository.createOrganization(organization);
} catch (e) {
throw Exception('Erreur lors de la création de l\'organisation: $e');
}
}
/// Met à jour une organisation avec validation
Future<OrganizationModel> updateOrganization(String id, OrganizationModel organization) async {
if (id.isEmpty) {
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
}
// Validation des données obligatoires
_validateOrganization(organization);
try {
return await _repository.updateOrganization(id, organization);
} catch (e) {
throw Exception('Erreur lors de la mise à jour de l\'organisation: $e');
}
}
/// Supprime une organisation
Future<void> deleteOrganization(String id) async {
if (id.isEmpty) {
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
}
try {
await _repository.deleteOrganization(id);
} catch (e) {
throw Exception('Erreur lors de la suppression de l\'organisation: $e');
}
}
/// Active une organisation
Future<OrganizationModel> activateOrganization(String id) async {
if (id.isEmpty) {
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
}
try {
return await _repository.activateOrganization(id);
} catch (e) {
throw Exception('Erreur lors de l\'activation de l\'organisation: $e');
}
}
/// Recherche avancée d'organisations
Future<List<OrganizationModel>> searchOrganizations({
String? nom,
TypeOrganization? type,
StatutOrganization? statut,
String? ville,
String? region,
String? pays,
int page = 0,
int size = 20,
}) async {
try {
return await _repository.searchOrganizations(
nom: nom,
type: type,
statut: statut,
ville: ville,
region: region,
pays: pays,
page: page,
size: size,
);
} catch (e) {
throw Exception('Erreur lors de la recherche d\'organisations: $e');
}
}
/// Récupère les statistiques des organisations
Future<Map<String, dynamic>> getOrganizationsStats() async {
try {
return await _repository.getOrganizationsStats();
} catch (e) {
throw Exception('Erreur lors de la récupération des statistiques: $e');
}
}
/// Filtre les organisations par statut
List<OrganizationModel> filterByStatus(
List<OrganizationModel> organizations,
StatutOrganization statut,
) {
return organizations.where((org) => org.statut == statut).toList();
}
/// Filtre les organisations par type
List<OrganizationModel> filterByType(
List<OrganizationModel> organizations,
TypeOrganization type,
) {
return organizations.where((org) => org.typeOrganisation == type).toList();
}
/// Trie les organisations par nom
List<OrganizationModel> sortByName(
List<OrganizationModel> organizations, {
bool ascending = true,
}) {
final sorted = List<OrganizationModel>.from(organizations);
sorted.sort((a, b) {
final comparison = a.nom.toLowerCase().compareTo(b.nom.toLowerCase());
return ascending ? comparison : -comparison;
});
return sorted;
}
/// Trie les organisations par date de création
List<OrganizationModel> sortByCreationDate(
List<OrganizationModel> organizations, {
bool ascending = true,
}) {
final sorted = List<OrganizationModel>.from(organizations);
sorted.sort((a, b) {
final dateA = a.dateCreation ?? DateTime.fromMillisecondsSinceEpoch(0);
final dateB = b.dateCreation ?? DateTime.fromMillisecondsSinceEpoch(0);
final comparison = dateA.compareTo(dateB);
return ascending ? comparison : -comparison;
});
return sorted;
}
/// Trie les organisations par nombre de membres
List<OrganizationModel> sortByMemberCount(
List<OrganizationModel> organizations, {
bool ascending = true,
}) {
final sorted = List<OrganizationModel>.from(organizations);
sorted.sort((a, b) {
final comparison = a.nombreMembres.compareTo(b.nombreMembres);
return ascending ? comparison : -comparison;
});
return sorted;
}
/// Recherche locale dans une liste d'organisations
List<OrganizationModel> searchLocal(
List<OrganizationModel> organizations,
String query,
) {
if (query.isEmpty) return organizations;
final lowerQuery = query.toLowerCase();
return organizations.where((org) {
return org.nom.toLowerCase().contains(lowerQuery) ||
(org.nomCourt?.toLowerCase().contains(lowerQuery) ?? false) ||
(org.description?.toLowerCase().contains(lowerQuery) ?? false) ||
(org.ville?.toLowerCase().contains(lowerQuery) ?? false) ||
(org.region?.toLowerCase().contains(lowerQuery) ?? false);
}).toList();
}
/// Calcule les statistiques locales d'une liste d'organisations
Map<String, dynamic> calculateLocalStats(List<OrganizationModel> organizations) {
if (organizations.isEmpty) {
return {
'total': 0,
'actives': 0,
'inactives': 0,
'totalMembres': 0,
'moyenneMembres': 0.0,
'parType': <String, int>{},
'parStatut': <String, int>{},
};
}
final actives = organizations.where((org) => org.statut == StatutOrganization.active).length;
final inactives = organizations.length - actives;
final totalMembres = organizations.fold<int>(0, (sum, org) => sum + org.nombreMembres);
final moyenneMembres = totalMembres / organizations.length;
// Statistiques par type
final parType = <String, int>{};
for (final org in organizations) {
final type = org.typeOrganisation.displayName;
parType[type] = (parType[type] ?? 0) + 1;
}
// Statistiques par statut
final parStatut = <String, int>{};
for (final org in organizations) {
final statut = org.statut.displayName;
parStatut[statut] = (parStatut[statut] ?? 0) + 1;
}
return {
'total': organizations.length,
'actives': actives,
'inactives': inactives,
'totalMembres': totalMembres,
'moyenneMembres': moyenneMembres,
'parType': parType,
'parStatut': parStatut,
};
}
/// Validation des données d'organisation
void _validateOrganization(OrganizationModel organization) {
if (organization.nom.trim().isEmpty) {
throw ArgumentError('Le nom de l\'organisation est obligatoire');
}
if (organization.nom.trim().length < 2) {
throw ArgumentError('Le nom de l\'organisation doit contenir au moins 2 caractères');
}
if (organization.nom.trim().length > 200) {
throw ArgumentError('Le nom de l\'organisation ne peut pas dépasser 200 caractères');
}
if (organization.nomCourt != null && organization.nomCourt!.length > 50) {
throw ArgumentError('Le nom court ne peut pas dépasser 50 caractères');
}
if (organization.email != null && organization.email!.isNotEmpty) {
if (!_isValidEmail(organization.email!)) {
throw ArgumentError('L\'adresse email n\'est pas valide');
}
}
if (organization.telephone != null && organization.telephone!.isNotEmpty) {
if (!_isValidPhone(organization.telephone!)) {
throw ArgumentError('Le numéro de téléphone n\'est pas valide');
}
}
if (organization.siteWeb != null && organization.siteWeb!.isNotEmpty) {
if (!_isValidUrl(organization.siteWeb!)) {
throw ArgumentError('L\'URL du site web n\'est pas valide');
}
}
if (organization.budgetAnnuel != null && organization.budgetAnnuel! < 0) {
throw ArgumentError('Le budget annuel doit être positif');
}
if (organization.montantCotisationAnnuelle != null && organization.montantCotisationAnnuelle! < 0) {
throw ArgumentError('Le montant de cotisation doit être positif');
}
}
/// Validation d'email
bool _isValidEmail(String email) {
return RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(email);
}
/// Validation de téléphone
bool _isValidPhone(String phone) {
return RegExp(r'^\+?[0-9\s\-\(\)]{8,15}$').hasMatch(phone);
}
/// Validation d'URL
bool _isValidUrl(String url) {
try {
final uri = Uri.parse(url);
return uri.hasScheme && (uri.scheme == 'http' || uri.scheme == 'https');
} catch (e) {
return false;
}
}
}