Clean project: remove test files, debug logs, and add documentation
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
/// Service pour la gestion des organisations
|
||||
/// Couche de logique métier entre le repository et l'interface utilisateur
|
||||
library organisation_service;
|
||||
|
||||
import '../models/organisation_model.dart';
|
||||
import '../repositories/organisation_repository.dart';
|
||||
|
||||
/// Service de gestion des organisations
|
||||
class OrganisationService {
|
||||
final OrganisationRepository _repository;
|
||||
|
||||
OrganisationService(this._repository);
|
||||
|
||||
/// Récupère la liste des organisations avec pagination et recherche
|
||||
Future<List<OrganisationModel>> getOrganisations({
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
String? recherche,
|
||||
}) async {
|
||||
try {
|
||||
return await _repository.getOrganisations(
|
||||
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<OrganisationModel?> getOrganisationById(String id) async {
|
||||
if (id.isEmpty) {
|
||||
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
|
||||
}
|
||||
|
||||
try {
|
||||
return await _repository.getOrganisationById(id);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Crée une nouvelle organisation avec validation
|
||||
Future<OrganisationModel> createOrganisation(OrganisationModel organisation) async {
|
||||
// Validation des données obligatoires
|
||||
_validateOrganisation(organisation);
|
||||
|
||||
try {
|
||||
return await _repository.createOrganisation(organisation);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la création de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour une organisation avec validation
|
||||
Future<OrganisationModel> updateOrganisation(String id, OrganisationModel organisation) async {
|
||||
if (id.isEmpty) {
|
||||
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
|
||||
}
|
||||
|
||||
// Validation des données obligatoires
|
||||
_validateOrganisation(organisation);
|
||||
|
||||
try {
|
||||
return await _repository.updateOrganisation(id, organisation);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime une organisation
|
||||
Future<void> deleteOrganisation(String id) async {
|
||||
if (id.isEmpty) {
|
||||
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
|
||||
}
|
||||
|
||||
try {
|
||||
await _repository.deleteOrganisation(id);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la suppression de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Active une organisation
|
||||
Future<OrganisationModel> activateOrganisation(String id) async {
|
||||
if (id.isEmpty) {
|
||||
throw ArgumentError('L\'ID de l\'organisation ne peut pas être vide');
|
||||
}
|
||||
|
||||
try {
|
||||
return await _repository.activateOrganisation(id);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de l\'activation de l\'organisation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Recherche avancée d'organisations
|
||||
Future<List<OrganisationModel>> searchOrganisations({
|
||||
String? nom,
|
||||
TypeOrganisation? type,
|
||||
StatutOrganisation? statut,
|
||||
String? ville,
|
||||
String? region,
|
||||
String? pays,
|
||||
int page = 0,
|
||||
int size = 20,
|
||||
}) async {
|
||||
try {
|
||||
return await _repository.searchOrganisations(
|
||||
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>> getOrganisationsStats() async {
|
||||
try {
|
||||
return await _repository.getOrganisationsStats();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des statistiques: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Filtre les organisations par statut
|
||||
List<OrganisationModel> filterByStatus(
|
||||
List<OrganisationModel> organisations,
|
||||
StatutOrganisation statut,
|
||||
) {
|
||||
return organisations.where((org) => org.statut == statut).toList();
|
||||
}
|
||||
|
||||
/// Filtre les organisations par type
|
||||
List<OrganisationModel> filterByType(
|
||||
List<OrganisationModel> organisations,
|
||||
TypeOrganisation type,
|
||||
) {
|
||||
return organisations.where((org) => org.typeOrganisation == type).toList();
|
||||
}
|
||||
|
||||
/// Trie les organisations par nom
|
||||
List<OrganisationModel> sortByName(
|
||||
List<OrganisationModel> organisations, {
|
||||
bool ascending = true,
|
||||
}) {
|
||||
final sorted = List<OrganisationModel>.from(organisations);
|
||||
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<OrganisationModel> sortByCreationDate(
|
||||
List<OrganisationModel> organisations, {
|
||||
bool ascending = true,
|
||||
}) {
|
||||
final sorted = List<OrganisationModel>.from(organisations);
|
||||
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<OrganisationModel> sortByMemberCount(
|
||||
List<OrganisationModel> organisations, {
|
||||
bool ascending = true,
|
||||
}) {
|
||||
final sorted = List<OrganisationModel>.from(organisations);
|
||||
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<OrganisationModel> searchLocal(
|
||||
List<OrganisationModel> organisations,
|
||||
String query,
|
||||
) {
|
||||
if (query.isEmpty) return organisations;
|
||||
|
||||
final lowerQuery = query.toLowerCase();
|
||||
return organisations.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<OrganisationModel> organisations) {
|
||||
if (organisations.isEmpty) {
|
||||
return {
|
||||
'total': 0,
|
||||
'actives': 0,
|
||||
'inactives': 0,
|
||||
'totalMembres': 0,
|
||||
'moyenneMembres': 0.0,
|
||||
'parType': <String, int>{},
|
||||
'parStatut': <String, int>{},
|
||||
};
|
||||
}
|
||||
|
||||
final actives = organisations.where((org) => org.statut == StatutOrganisation.active).length;
|
||||
final inactives = organisations.length - actives;
|
||||
final totalMembres = organisations.fold<int>(0, (sum, org) => sum + org.nombreMembres);
|
||||
final moyenneMembres = totalMembres / organisations.length;
|
||||
|
||||
// Statistiques par type
|
||||
final parType = <String, int>{};
|
||||
for (final org in organisations) {
|
||||
final type = org.typeOrganisation.displayName;
|
||||
parType[type] = (parType[type] ?? 0) + 1;
|
||||
}
|
||||
|
||||
// Statistiques par statut
|
||||
final parStatut = <String, int>{};
|
||||
for (final org in organisations) {
|
||||
final statut = org.statut.displayName;
|
||||
parStatut[statut] = (parStatut[statut] ?? 0) + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
'total': organisations.length,
|
||||
'actives': actives,
|
||||
'inactives': inactives,
|
||||
'totalMembres': totalMembres,
|
||||
'moyenneMembres': moyenneMembres,
|
||||
'parType': parType,
|
||||
'parStatut': parStatut,
|
||||
};
|
||||
}
|
||||
|
||||
/// Validation des données d'organisation
|
||||
void _validateOrganisation(OrganisationModel organisation) {
|
||||
if (organisation.nom.trim().isEmpty) {
|
||||
throw ArgumentError('Le nom de l\'organisation est obligatoire');
|
||||
}
|
||||
|
||||
if (organisation.nom.trim().length < 2) {
|
||||
throw ArgumentError('Le nom de l\'organisation doit contenir au moins 2 caractères');
|
||||
}
|
||||
|
||||
if (organisation.nom.trim().length > 200) {
|
||||
throw ArgumentError('Le nom de l\'organisation ne peut pas dépasser 200 caractères');
|
||||
}
|
||||
|
||||
if (organisation.nomCourt != null && organisation.nomCourt!.length > 50) {
|
||||
throw ArgumentError('Le nom court ne peut pas dépasser 50 caractères');
|
||||
}
|
||||
|
||||
if (organisation.email != null && organisation.email!.isNotEmpty) {
|
||||
if (!_isValidEmail(organisation.email!)) {
|
||||
throw ArgumentError('L\'adresse email n\'est pas valide');
|
||||
}
|
||||
}
|
||||
|
||||
if (organisation.telephone != null && organisation.telephone!.isNotEmpty) {
|
||||
if (!_isValidPhone(organisation.telephone!)) {
|
||||
throw ArgumentError('Le numéro de téléphone n\'est pas valide');
|
||||
}
|
||||
}
|
||||
|
||||
if (organisation.siteWeb != null && organisation.siteWeb!.isNotEmpty) {
|
||||
if (!_isValidUrl(organisation.siteWeb!)) {
|
||||
throw ArgumentError('L\'URL du site web n\'est pas valide');
|
||||
}
|
||||
}
|
||||
|
||||
if (organisation.budgetAnnuel != null && organisation.budgetAnnuel! < 0) {
|
||||
throw ArgumentError('Le budget annuel doit être positif');
|
||||
}
|
||||
|
||||
if (organisation.montantCotisationAnnuelle != null && organisation.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user