Refactoring

This commit is contained in:
DahoudG
2025-09-17 17:54:06 +00:00
parent 12d514d866
commit 63fe107f98
165 changed files with 54220 additions and 276 deletions

View File

@@ -0,0 +1,435 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../../core/error/exceptions.dart';
import '../models/demande_aide_model.dart';
import '../models/proposition_aide_model.dart';
import '../models/evaluation_aide_model.dart';
/// Source de données locale pour le module solidarité
///
/// Cette classe gère le cache local des données de solidarité
/// pour permettre un fonctionnement hors ligne et améliorer les performances.
abstract class SolidariteLocalDataSource {
// Cache des demandes d'aide
Future<void> cacherDemandeAide(DemandeAideModel demande);
Future<DemandeAideModel?> obtenirDemandeAideCachee(String id);
Future<List<DemandeAideModel>> obtenirDemandesAideCachees();
Future<void> supprimerDemandeAideCachee(String id);
Future<void> viderCacheDemandesAide();
// Cache des propositions d'aide
Future<void> cacherPropositionAide(PropositionAideModel proposition);
Future<PropositionAideModel?> obtenirPropositionAideCachee(String id);
Future<List<PropositionAideModel>> obtenirPropositionsAideCachees();
Future<void> supprimerPropositionAideCachee(String id);
Future<void> viderCachePropositionsAide();
// Cache des évaluations
Future<void> cacherEvaluation(EvaluationAideModel evaluation);
Future<EvaluationAideModel?> obtenirEvaluationCachee(String id);
Future<List<EvaluationAideModel>> obtenirEvaluationsCachees();
Future<void> supprimerEvaluationCachee(String id);
Future<void> viderCacheEvaluations();
// Cache des statistiques
Future<void> cacherStatistiques(String organisationId, Map<String, dynamic> statistiques);
Future<Map<String, dynamic>?> obtenirStatistiquesCachees(String organisationId);
Future<void> supprimerStatistiquesCachees(String organisationId);
// Gestion du cache
Future<DateTime?> obtenirDateDerniereMiseAJour(String cacheKey);
Future<void> marquerMiseAJour(String cacheKey);
Future<bool> estCacheExpire(String cacheKey, Duration dureeValidite);
Future<void> viderToutCache();
}
/// Implémentation de la source de données locale
class SolidariteLocalDataSourceImpl implements SolidariteLocalDataSource {
final SharedPreferences sharedPreferences;
// Clés de cache
static const String _demandesAideKey = 'CACHED_DEMANDES_AIDE';
static const String _propositionsAideKey = 'CACHED_PROPOSITIONS_AIDE';
static const String _evaluationsKey = 'CACHED_EVALUATIONS';
static const String _statistiquesKey = 'CACHED_STATISTIQUES';
static const String _lastUpdatePrefix = 'LAST_UPDATE_';
// Durées de validité du cache
static const Duration _dureeValiditeDefaut = Duration(minutes: 15);
static const Duration _dureeValiditeStatistiques = Duration(hours: 1);
SolidariteLocalDataSourceImpl({required this.sharedPreferences});
// Cache des demandes d'aide
@override
Future<void> cacherDemandeAide(DemandeAideModel demande) async {
try {
final demandes = await obtenirDemandesAideCachees();
// Supprimer l'ancienne version si elle existe
demandes.removeWhere((d) => d.id == demande.id);
// Ajouter la nouvelle version
demandes.add(demande);
// Limiter le cache à 100 demandes maximum
if (demandes.length > 100) {
demandes.sort((a, b) => b.dateModification.compareTo(a.dateModification));
demandes.removeRange(100, demandes.length);
}
final jsonList = demandes.map((d) => d.toJson()).toList();
await sharedPreferences.setString(_demandesAideKey, jsonEncode(jsonList));
await marquerMiseAJour(_demandesAideKey);
} catch (e) {
throw CacheException(message: 'Erreur lors de la mise en cache de la demande: ${e.toString()}');
}
}
@override
Future<DemandeAideModel?> obtenirDemandeAideCachee(String id) async {
try {
final demandes = await obtenirDemandesAideCachees();
return demandes.cast<DemandeAideModel?>().firstWhere(
(d) => d?.id == id,
orElse: () => null,
);
} catch (e) {
return null;
}
}
@override
Future<List<DemandeAideModel>> obtenirDemandesAideCachees() async {
try {
final jsonString = sharedPreferences.getString(_demandesAideKey);
if (jsonString == null) return [];
final List<dynamic> jsonList = jsonDecode(jsonString);
return jsonList.map((json) => DemandeAideModel.fromJson(json)).toList();
} catch (e) {
return [];
}
}
@override
Future<void> supprimerDemandeAideCachee(String id) async {
try {
final demandes = await obtenirDemandesAideCachees();
demandes.removeWhere((d) => d.id == id);
final jsonList = demandes.map((d) => d.toJson()).toList();
await sharedPreferences.setString(_demandesAideKey, jsonEncode(jsonList));
} catch (e) {
throw CacheException(message: 'Erreur lors de la suppression de la demande du cache: ${e.toString()}');
}
}
@override
Future<void> viderCacheDemandesAide() async {
try {
await sharedPreferences.remove(_demandesAideKey);
await sharedPreferences.remove('$_lastUpdatePrefix$_demandesAideKey');
} catch (e) {
throw CacheException(message: 'Erreur lors de la suppression du cache des demandes: ${e.toString()}');
}
}
// Cache des propositions d'aide
@override
Future<void> cacherPropositionAide(PropositionAideModel proposition) async {
try {
final propositions = await obtenirPropositionsAideCachees();
// Supprimer l'ancienne version si elle existe
propositions.removeWhere((p) => p.id == proposition.id);
// Ajouter la nouvelle version
propositions.add(proposition);
// Limiter le cache à 100 propositions maximum
if (propositions.length > 100) {
propositions.sort((a, b) => b.dateModification.compareTo(a.dateModification));
propositions.removeRange(100, propositions.length);
}
final jsonList = propositions.map((p) => p.toJson()).toList();
await sharedPreferences.setString(_propositionsAideKey, jsonEncode(jsonList));
await marquerMiseAJour(_propositionsAideKey);
} catch (e) {
throw CacheException(message: 'Erreur lors de la mise en cache de la proposition: ${e.toString()}');
}
}
@override
Future<PropositionAideModel?> obtenirPropositionAideCachee(String id) async {
try {
final propositions = await obtenirPropositionsAideCachees();
return propositions.cast<PropositionAideModel?>().firstWhere(
(p) => p?.id == id,
orElse: () => null,
);
} catch (e) {
return null;
}
}
@override
Future<List<PropositionAideModel>> obtenirPropositionsAideCachees() async {
try {
final jsonString = sharedPreferences.getString(_propositionsAideKey);
if (jsonString == null) return [];
final List<dynamic> jsonList = jsonDecode(jsonString);
return jsonList.map((json) => PropositionAideModel.fromJson(json)).toList();
} catch (e) {
return [];
}
}
@override
Future<void> supprimerPropositionAideCachee(String id) async {
try {
final propositions = await obtenirPropositionsAideCachees();
propositions.removeWhere((p) => p.id == id);
final jsonList = propositions.map((p) => p.toJson()).toList();
await sharedPreferences.setString(_propositionsAideKey, jsonEncode(jsonList));
} catch (e) {
throw CacheException(message: 'Erreur lors de la suppression de la proposition du cache: ${e.toString()}');
}
}
@override
Future<void> viderCachePropositionsAide() async {
try {
await sharedPreferences.remove(_propositionsAideKey);
await sharedPreferences.remove('$_lastUpdatePrefix$_propositionsAideKey');
} catch (e) {
throw CacheException(message: 'Erreur lors de la suppression du cache des propositions: ${e.toString()}');
}
}
// Cache des évaluations
@override
Future<void> cacherEvaluation(EvaluationAideModel evaluation) async {
try {
final evaluations = await obtenirEvaluationsCachees();
// Supprimer l'ancienne version si elle existe
evaluations.removeWhere((e) => e.id == evaluation.id);
// Ajouter la nouvelle version
evaluations.add(evaluation);
// Limiter le cache à 200 évaluations maximum
if (evaluations.length > 200) {
evaluations.sort((a, b) => b.dateModification.compareTo(a.dateModification));
evaluations.removeRange(200, evaluations.length);
}
final jsonList = evaluations.map((e) => e.toJson()).toList();
await sharedPreferences.setString(_evaluationsKey, jsonEncode(jsonList));
await marquerMiseAJour(_evaluationsKey);
} catch (e) {
throw CacheException(message: 'Erreur lors de la mise en cache de l\'évaluation: ${e.toString()}');
}
}
@override
Future<EvaluationAideModel?> obtenirEvaluationCachee(String id) async {
try {
final evaluations = await obtenirEvaluationsCachees();
return evaluations.cast<EvaluationAideModel?>().firstWhere(
(e) => e?.id == id,
orElse: () => null,
);
} catch (e) {
return null;
}
}
@override
Future<List<EvaluationAideModel>> obtenirEvaluationsCachees() async {
try {
final jsonString = sharedPreferences.getString(_evaluationsKey);
if (jsonString == null) return [];
final List<dynamic> jsonList = jsonDecode(jsonString);
return jsonList.map((json) => EvaluationAideModel.fromJson(json)).toList();
} catch (e) {
return [];
}
}
@override
Future<void> supprimerEvaluationCachee(String id) async {
try {
final evaluations = await obtenirEvaluationsCachees();
evaluations.removeWhere((e) => e.id == id);
final jsonList = evaluations.map((e) => e.toJson()).toList();
await sharedPreferences.setString(_evaluationsKey, jsonEncode(jsonList));
} catch (e) {
throw CacheException(message: 'Erreur lors de la suppression de l\'évaluation du cache: ${e.toString()}');
}
}
@override
Future<void> viderCacheEvaluations() async {
try {
await sharedPreferences.remove(_evaluationsKey);
await sharedPreferences.remove('$_lastUpdatePrefix$_evaluationsKey');
} catch (e) {
throw CacheException(message: 'Erreur lors de la suppression du cache des évaluations: ${e.toString()}');
}
}
// Cache des statistiques
@override
Future<void> cacherStatistiques(String organisationId, Map<String, dynamic> statistiques) async {
try {
final key = '$_statistiquesKey$organisationId';
await sharedPreferences.setString(key, jsonEncode(statistiques));
await marquerMiseAJour(key);
} catch (e) {
throw CacheException(message: 'Erreur lors de la mise en cache des statistiques: ${e.toString()}');
}
}
@override
Future<Map<String, dynamic>?> obtenirStatistiquesCachees(String organisationId) async {
try {
final key = '$_statistiquesKey$organisationId';
final jsonString = sharedPreferences.getString(key);
if (jsonString == null) return null;
return Map<String, dynamic>.from(jsonDecode(jsonString));
} catch (e) {
return null;
}
}
@override
Future<void> supprimerStatistiquesCachees(String organisationId) async {
try {
final key = '$_statistiquesKey$organisationId';
await sharedPreferences.remove(key);
await sharedPreferences.remove('$_lastUpdatePrefix$key');
} catch (e) {
throw CacheException(message: 'Erreur lors de la suppression des statistiques du cache: ${e.toString()}');
}
}
// Gestion du cache
@override
Future<DateTime?> obtenirDateDerniereMiseAJour(String cacheKey) async {
try {
final timestamp = sharedPreferences.getInt('$_lastUpdatePrefix$cacheKey');
if (timestamp == null) return null;
return DateTime.fromMillisecondsSinceEpoch(timestamp);
} catch (e) {
return null;
}
}
@override
Future<void> marquerMiseAJour(String cacheKey) async {
try {
final timestamp = DateTime.now().millisecondsSinceEpoch;
await sharedPreferences.setInt('$_lastUpdatePrefix$cacheKey', timestamp);
} catch (e) {
throw CacheException(message: 'Erreur lors de la mise à jour du timestamp: ${e.toString()}');
}
}
@override
Future<bool> estCacheExpire(String cacheKey, Duration dureeValidite) async {
try {
final dateDerniereMiseAJour = await obtenirDateDerniereMiseAJour(cacheKey);
if (dateDerniereMiseAJour == null) return true;
final maintenant = DateTime.now();
final dureeEcoulee = maintenant.difference(dateDerniereMiseAJour);
return dureeEcoulee > dureeValidite;
} catch (e) {
return true; // En cas d'erreur, considérer le cache comme expiré
}
}
@override
Future<void> viderToutCache() async {
try {
await Future.wait([
viderCacheDemandesAide(),
viderCachePropositionsAide(),
viderCacheEvaluations(),
]);
// Supprimer toutes les statistiques cachées
final keys = sharedPreferences.getKeys();
final statistiquesKeys = keys.where((key) => key.startsWith(_statistiquesKey));
for (final key in statistiquesKeys) {
await sharedPreferences.remove(key);
await sharedPreferences.remove('$_lastUpdatePrefix$key');
}
} catch (e) {
throw CacheException(message: 'Erreur lors de la suppression complète du cache: ${e.toString()}');
}
}
/// Méthodes utilitaires pour la gestion du cache
/// Vérifie si le cache des demandes est valide
Future<bool> estCacheDemandesValide() async {
return !(await estCacheExpire(_demandesAideKey, _dureeValiditeDefaut));
}
/// Vérifie si le cache des propositions est valide
Future<bool> estCachePropositionsValide() async {
return !(await estCacheExpire(_propositionsAideKey, _dureeValiditeDefaut));
}
/// Vérifie si le cache des évaluations est valide
Future<bool> estCacheEvaluationsValide() async {
return !(await estCacheExpire(_evaluationsKey, _dureeValiditeDefaut));
}
/// Vérifie si le cache des statistiques est valide
Future<bool> estCacheStatistiquesValide(String organisationId) async {
final key = '$_statistiquesKey$organisationId';
return !(await estCacheExpire(key, _dureeValiditeStatistiques));
}
/// Obtient la taille approximative du cache en octets
Future<int> obtenirTailleCache() async {
try {
int taille = 0;
final demandes = sharedPreferences.getString(_demandesAideKey);
if (demandes != null) taille += demandes.length;
final propositions = sharedPreferences.getString(_propositionsAideKey);
if (propositions != null) taille += propositions.length;
final evaluations = sharedPreferences.getString(_evaluationsKey);
if (evaluations != null) taille += evaluations.length;
// Ajouter les statistiques
final keys = sharedPreferences.getKeys();
final statistiquesKeys = keys.where((key) => key.startsWith(_statistiquesKey));
for (final key in statistiquesKeys) {
final value = sharedPreferences.getString(key);
if (value != null) taille += value.length;
}
return taille;
} catch (e) {
return 0;
}
}
}

View File

@@ -0,0 +1,817 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../../../../core/error/exceptions.dart';
import '../../../../core/network/api_client.dart';
import '../models/demande_aide_model.dart';
import '../models/proposition_aide_model.dart';
import '../models/evaluation_aide_model.dart';
/// Source de données distante pour le module solidarité
///
/// Cette classe gère toutes les communications avec l'API REST
/// du backend UnionFlow pour les fonctionnalités de solidarité.
abstract class SolidariteRemoteDataSource {
// Demandes d'aide
Future<DemandeAideModel> creerDemandeAide(DemandeAideModel demande);
Future<DemandeAideModel> mettreAJourDemandeAide(DemandeAideModel demande);
Future<DemandeAideModel> obtenirDemandeAide(String id);
Future<DemandeAideModel> soumettreDemande(String demandeId);
Future<DemandeAideModel> evaluerDemande({
required String demandeId,
required String evaluateurId,
required String decision,
String? commentaire,
double? montantApprouve,
});
Future<List<DemandeAideModel>> rechercherDemandes({
String? organisationId,
String? typeAide,
String? statut,
String? demandeurId,
bool? urgente,
int page = 0,
int taille = 20,
});
Future<List<DemandeAideModel>> obtenirDemandesUrgentes(String organisationId);
Future<List<DemandeAideModel>> obtenirMesdemandes(String utilisateurId);
// Propositions d'aide
Future<PropositionAideModel> creerPropositionAide(PropositionAideModel proposition);
Future<PropositionAideModel> mettreAJourPropositionAide(PropositionAideModel proposition);
Future<PropositionAideModel> obtenirPropositionAide(String id);
Future<PropositionAideModel> changerStatutProposition({
required String propositionId,
required bool activer,
});
Future<List<PropositionAideModel>> rechercherPropositions({
String? organisationId,
String? typeAide,
String? proposantId,
bool? actives,
int page = 0,
int taille = 20,
});
Future<List<PropositionAideModel>> obtenirPropositionsActives(String typeAide);
Future<List<PropositionAideModel>> obtenirMeilleuresPropositions(int limite);
Future<List<PropositionAideModel>> obtenirMesPropositions(String utilisateurId);
// Matching
Future<List<PropositionAideModel>> trouverPropositionsCompatibles(String demandeId);
Future<List<DemandeAideModel>> trouverDemandesCompatibles(String propositionId);
Future<List<PropositionAideModel>> rechercherProposantsFinanciers(String demandeId);
// Évaluations
Future<EvaluationAideModel> creerEvaluation(EvaluationAideModel evaluation);
Future<EvaluationAideModel> mettreAJourEvaluation(EvaluationAideModel evaluation);
Future<EvaluationAideModel> obtenirEvaluation(String id);
Future<List<EvaluationAideModel>> obtenirEvaluationsDemande(String demandeId);
Future<List<EvaluationAideModel>> obtenirEvaluationsProposition(String propositionId);
Future<EvaluationAideModel> signalerEvaluation({
required String evaluationId,
required String motif,
});
Future<StatistiquesEvaluationModel> calculerMoyenneDemande(String demandeId);
Future<StatistiquesEvaluationModel> calculerMoyenneProposition(String propositionId);
// Statistiques
Future<Map<String, dynamic>> obtenirStatistiquesSolidarite(String organisationId);
}
/// Implémentation de la source de données distante
class SolidariteRemoteDataSourceImpl implements SolidariteRemoteDataSource {
final ApiClient apiClient;
static const String baseEndpoint = '/api/solidarite';
SolidariteRemoteDataSourceImpl({required this.apiClient});
// Demandes d'aide
@override
Future<DemandeAideModel> creerDemandeAide(DemandeAideModel demande) async {
try {
final response = await apiClient.post(
'$baseEndpoint/demandes',
data: demande.toJson(),
);
if (response.statusCode == 201) {
return DemandeAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors de la création de la demande d\'aide',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<DemandeAideModel> mettreAJourDemandeAide(DemandeAideModel demande) async {
try {
final response = await apiClient.put(
'$baseEndpoint/demandes/${demande.id}',
data: demande.toJson(),
);
if (response.statusCode == 200) {
return DemandeAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors de la mise à jour de la demande d\'aide',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<DemandeAideModel> obtenirDemandeAide(String id) async {
try {
final response = await apiClient.get('$baseEndpoint/demandes/$id');
if (response.statusCode == 200) {
return DemandeAideModel.fromJson(response.data);
} else if (response.statusCode == 404) {
throw NotFoundException(message: 'Demande d\'aide non trouvée');
} else {
throw ServerException(
message: 'Erreur lors de la récupération de la demande d\'aide',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException || e is NotFoundException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<DemandeAideModel> soumettreDemande(String demandeId) async {
try {
final response = await apiClient.post(
'$baseEndpoint/demandes/$demandeId/soumettre',
);
if (response.statusCode == 200) {
return DemandeAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors de la soumission de la demande',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<DemandeAideModel> evaluerDemande({
required String demandeId,
required String evaluateurId,
required String decision,
String? commentaire,
double? montantApprouve,
}) async {
try {
final data = {
'evaluateurId': evaluateurId,
'decision': decision,
if (commentaire != null) 'commentaire': commentaire,
if (montantApprouve != null) 'montantApprouve': montantApprouve,
};
final response = await apiClient.post(
'$baseEndpoint/demandes/$demandeId/evaluer',
data: data,
);
if (response.statusCode == 200) {
return DemandeAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors de l\'évaluation de la demande',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<DemandeAideModel>> rechercherDemandes({
String? organisationId,
String? typeAide,
String? statut,
String? demandeurId,
bool? urgente,
int page = 0,
int taille = 20,
}) async {
try {
final queryParams = <String, dynamic>{
'page': page,
'size': taille,
if (organisationId != null) 'organisationId': organisationId,
if (typeAide != null) 'typeAide': typeAide,
if (statut != null) 'statut': statut,
if (demandeurId != null) 'demandeurId': demandeurId,
if (urgente != null) 'urgente': urgente,
};
final response = await apiClient.get(
'$baseEndpoint/demandes/rechercher',
queryParameters: queryParams,
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data['content'];
return data.map((json) => DemandeAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la recherche des demandes',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<DemandeAideModel>> obtenirDemandesUrgentes(String organisationId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/demandes/urgentes',
queryParameters: {'organisationId': organisationId},
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => DemandeAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la récupération des demandes urgentes',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<DemandeAideModel>> obtenirMesdemandes(String utilisateurId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/demandes/mes-demandes',
queryParameters: {'utilisateurId': utilisateurId},
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => DemandeAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la récupération de vos demandes',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
// Propositions d'aide
@override
Future<PropositionAideModel> creerPropositionAide(PropositionAideModel proposition) async {
try {
final response = await apiClient.post(
'$baseEndpoint/propositions',
data: proposition.toJson(),
);
if (response.statusCode == 201) {
return PropositionAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors de la création de la proposition d\'aide',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<PropositionAideModel> mettreAJourPropositionAide(PropositionAideModel proposition) async {
try {
final response = await apiClient.put(
'$baseEndpoint/propositions/${proposition.id}',
data: proposition.toJson(),
);
if (response.statusCode == 200) {
return PropositionAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors de la mise à jour de la proposition d\'aide',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<PropositionAideModel> obtenirPropositionAide(String id) async {
try {
final response = await apiClient.get('$baseEndpoint/propositions/$id');
if (response.statusCode == 200) {
return PropositionAideModel.fromJson(response.data);
} else if (response.statusCode == 404) {
throw NotFoundException(message: 'Proposition d\'aide non trouvée');
} else {
throw ServerException(
message: 'Erreur lors de la récupération de la proposition d\'aide',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException || e is NotFoundException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<PropositionAideModel> changerStatutProposition({
required String propositionId,
required bool activer,
}) async {
try {
final endpoint = activer ? 'activer' : 'desactiver';
final response = await apiClient.post(
'$baseEndpoint/propositions/$propositionId/$endpoint',
);
if (response.statusCode == 200) {
return PropositionAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors du changement de statut de la proposition',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<PropositionAideModel>> rechercherPropositions({
String? organisationId,
String? typeAide,
String? proposantId,
bool? actives,
int page = 0,
int taille = 20,
}) async {
try {
final queryParams = <String, dynamic>{
'page': page,
'size': taille,
if (organisationId != null) 'organisationId': organisationId,
if (typeAide != null) 'typeAide': typeAide,
if (proposantId != null) 'proposantId': proposantId,
if (actives != null) 'actives': actives,
};
final response = await apiClient.get(
'$baseEndpoint/propositions/rechercher',
queryParameters: queryParams,
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data['content'];
return data.map((json) => PropositionAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la recherche des propositions',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<PropositionAideModel>> obtenirPropositionsActives(String typeAide) async {
try {
final response = await apiClient.get(
'$baseEndpoint/propositions/actives',
queryParameters: {'typeAide': typeAide},
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => PropositionAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la récupération des propositions actives',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<PropositionAideModel>> obtenirMeilleuresPropositions(int limite) async {
try {
final response = await apiClient.get(
'$baseEndpoint/propositions/meilleures',
queryParameters: {'limite': limite},
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => PropositionAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la récupération des meilleures propositions',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<PropositionAideModel>> obtenirMesPropositions(String utilisateurId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/propositions/mes-propositions',
queryParameters: {'utilisateurId': utilisateurId},
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => PropositionAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la récupération de vos propositions',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
// Matching
@override
Future<List<PropositionAideModel>> trouverPropositionsCompatibles(String demandeId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/matching/propositions-compatibles/$demandeId',
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => PropositionAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la recherche de propositions compatibles',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<DemandeAideModel>> trouverDemandesCompatibles(String propositionId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/matching/demandes-compatibles/$propositionId',
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => DemandeAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la recherche de demandes compatibles',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<PropositionAideModel>> rechercherProposantsFinanciers(String demandeId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/matching/proposants-financiers/$demandeId',
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => PropositionAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la recherche de proposants financiers',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
// Évaluations
@override
Future<EvaluationAideModel> creerEvaluation(EvaluationAideModel evaluation) async {
try {
final response = await apiClient.post(
'$baseEndpoint/evaluations',
data: evaluation.toJson(),
);
if (response.statusCode == 201) {
return EvaluationAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors de la création de l\'évaluation',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<EvaluationAideModel> mettreAJourEvaluation(EvaluationAideModel evaluation) async {
try {
final response = await apiClient.put(
'$baseEndpoint/evaluations/${evaluation.id}',
data: evaluation.toJson(),
);
if (response.statusCode == 200) {
return EvaluationAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors de la mise à jour de l\'évaluation',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<EvaluationAideModel> obtenirEvaluation(String id) async {
try {
final response = await apiClient.get('$baseEndpoint/evaluations/$id');
if (response.statusCode == 200) {
return EvaluationAideModel.fromJson(response.data);
} else if (response.statusCode == 404) {
throw NotFoundException(message: 'Évaluation non trouvée');
} else {
throw ServerException(
message: 'Erreur lors de la récupération de l\'évaluation',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException || e is NotFoundException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<EvaluationAideModel>> obtenirEvaluationsDemande(String demandeId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/evaluations/demande/$demandeId',
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => EvaluationAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la récupération des évaluations de la demande',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<List<EvaluationAideModel>> obtenirEvaluationsProposition(String propositionId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/evaluations/proposition/$propositionId',
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
return data.map((json) => EvaluationAideModel.fromJson(json)).toList();
} else {
throw ServerException(
message: 'Erreur lors de la récupération des évaluations de la proposition',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<EvaluationAideModel> signalerEvaluation({
required String evaluationId,
required String motif,
}) async {
try {
final response = await apiClient.post(
'$baseEndpoint/evaluations/$evaluationId/signaler',
data: {'motif': motif},
);
if (response.statusCode == 200) {
return EvaluationAideModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors du signalement de l\'évaluation',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<StatistiquesEvaluationModel> calculerMoyenneDemande(String demandeId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/evaluations/moyenne/demande/$demandeId',
);
if (response.statusCode == 200) {
return StatistiquesEvaluationModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors du calcul de la moyenne de la demande',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
@override
Future<StatistiquesEvaluationModel> calculerMoyenneProposition(String propositionId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/evaluations/moyenne/proposition/$propositionId',
);
if (response.statusCode == 200) {
return StatistiquesEvaluationModel.fromJson(response.data);
} else {
throw ServerException(
message: 'Erreur lors du calcul de la moyenne de la proposition',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
// Statistiques
@override
Future<Map<String, dynamic>> obtenirStatistiquesSolidarite(String organisationId) async {
try {
final response = await apiClient.get(
'$baseEndpoint/statistiques',
queryParameters: {'organisationId': organisationId},
);
if (response.statusCode == 200) {
return Map<String, dynamic>.from(response.data);
} else {
throw ServerException(
message: 'Erreur lors de la récupération des statistiques',
statusCode: response.statusCode,
);
}
} catch (e) {
if (e is ServerException) rethrow;
throw ServerException(
message: 'Erreur de communication avec le serveur: ${e.toString()}',
);
}
}
}

View File

@@ -0,0 +1,332 @@
import 'package:get_it/get_it.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../core/network/api_client.dart';
import '../../../core/network/network_info.dart';
// Domain
import '../domain/repositories/solidarite_repository.dart';
import '../domain/usecases/gerer_demandes_aide_usecase.dart';
import '../domain/usecases/gerer_propositions_aide_usecase.dart';
import '../domain/usecases/gerer_matching_usecase.dart';
import '../domain/usecases/gerer_evaluations_usecase.dart';
import '../domain/usecases/obtenir_statistiques_usecase.dart';
// Data
import 'datasources/solidarite_remote_data_source.dart';
import 'datasources/solidarite_local_data_source.dart';
import 'repositories/solidarite_repository_impl.dart';
/// Configuration de l'injection de dépendances pour le module solidarité
///
/// Cette classe configure tous les services, repositories, use cases
/// et data sources nécessaires au fonctionnement du module solidarité.
class SolidariteInjectionContainer {
static final GetIt _sl = GetIt.instance;
/// Initialise toutes les dépendances du module solidarité
static Future<void> init() async {
// ============================================================================
// Features - Solidarité
// ============================================================================
// Use Cases - Demandes d'aide
_sl.registerLazySingleton(() => CreerDemandeAideUseCase(_sl()));
_sl.registerLazySingleton(() => MettreAJourDemandeAideUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirDemandeAideUseCase(_sl()));
_sl.registerLazySingleton(() => SoumettreDemandeAideUseCase(_sl()));
_sl.registerLazySingleton(() => EvaluerDemandeAideUseCase(_sl()));
_sl.registerLazySingleton(() => RechercherDemandesAideUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirDemandesUrgentesUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirMesDemandesUseCase(_sl()));
_sl.registerLazySingleton(() => ValiderDemandeAideUseCase());
_sl.registerLazySingleton(() => CalculerPrioriteDemandeUseCase());
// Use Cases - Propositions d'aide
_sl.registerLazySingleton(() => CreerPropositionAideUseCase(_sl()));
_sl.registerLazySingleton(() => MettreAJourPropositionAideUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirPropositionAideUseCase(_sl()));
_sl.registerLazySingleton(() => ChangerStatutPropositionUseCase(_sl()));
_sl.registerLazySingleton(() => RechercherPropositionsAideUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirPropositionsActivesUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirMeilleuresPropositionsUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirMesPropositionsUseCase(_sl()));
_sl.registerLazySingleton(() => ValiderPropositionAideUseCase());
_sl.registerLazySingleton(() => CalculerScorePropositionUseCase());
// Use Cases - Matching
_sl.registerLazySingleton(() => TrouverPropositionsCompatiblesUseCase(_sl()));
_sl.registerLazySingleton(() => TrouverDemandesCompatiblesUseCase(_sl()));
_sl.registerLazySingleton(() => RechercherProposantsFinanciersUseCase(_sl()));
_sl.registerLazySingleton(() => CalculerScoreCompatibiliteUseCase());
_sl.registerLazySingleton(() => EffectuerMatchingIntelligentUseCase(
trouverPropositionsCompatibles: _sl(),
calculerScoreCompatibilite: _sl(),
));
_sl.registerLazySingleton(() => AnalyserTendancesMatchingUseCase());
// Use Cases - Évaluations
_sl.registerLazySingleton(() => CreerEvaluationUseCase(_sl()));
_sl.registerLazySingleton(() => MettreAJourEvaluationUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirEvaluationUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirEvaluationsDemandeUseCase(_sl()));
_sl.registerLazySingleton(() => ObtenirEvaluationsPropositionUseCase(_sl()));
_sl.registerLazySingleton(() => SignalerEvaluationUseCase(_sl()));
_sl.registerLazySingleton(() => CalculerMoyenneDemandeUseCase(_sl()));
_sl.registerLazySingleton(() => CalculerMoyennePropositionUseCase(_sl()));
_sl.registerLazySingleton(() => ValiderEvaluationUseCase());
_sl.registerLazySingleton(() => CalculerScoreQualiteEvaluationUseCase());
_sl.registerLazySingleton(() => AnalyserTendancesEvaluationUseCase());
// Use Cases - Statistiques
_sl.registerLazySingleton(() => ObtenirStatistiquesSolidariteUseCase(_sl()));
_sl.registerLazySingleton(() => CalculerKPIsPerformanceUseCase());
_sl.registerLazySingleton(() => GenererRapportActiviteUseCase());
// Repository
_sl.registerLazySingleton<SolidariteRepository>(
() => SolidariteRepositoryImpl(
remoteDataSource: _sl(),
localDataSource: _sl(),
networkInfo: _sl(),
),
);
// Data Sources
_sl.registerLazySingleton<SolidariteRemoteDataSource>(
() => SolidariteRemoteDataSourceImpl(apiClient: _sl()),
);
_sl.registerLazySingleton<SolidariteLocalDataSource>(
() => SolidariteLocalDataSourceImpl(sharedPreferences: _sl()),
);
// ============================================================================
// Core (si pas déjà enregistrés)
// ============================================================================
// Ces services sont normalement enregistrés dans le core injection container
// Nous les enregistrons ici seulement s'ils ne sont pas déjà disponibles
if (!_sl.isRegistered<ApiClient>()) {
_sl.registerLazySingleton<ApiClient>(() => ApiClientImpl());
}
if (!_sl.isRegistered<NetworkInfo>()) {
_sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl());
}
if (!_sl.isRegistered<SharedPreferences>()) {
final sharedPreferences = await SharedPreferences.getInstance();
_sl.registerLazySingleton<SharedPreferences>(() => sharedPreferences);
}
}
/// Nettoie toutes les dépendances du module solidarité
static Future<void> dispose() async {
// Use Cases - Demandes d'aide
_sl.unregister<CreerDemandeAideUseCase>();
_sl.unregister<MettreAJourDemandeAideUseCase>();
_sl.unregister<ObtenirDemandeAideUseCase>();
_sl.unregister<SoumettreDemandeAideUseCase>();
_sl.unregister<EvaluerDemandeAideUseCase>();
_sl.unregister<RechercherDemandesAideUseCase>();
_sl.unregister<ObtenirDemandesUrgentesUseCase>();
_sl.unregister<ObtenirMesDemandesUseCase>();
_sl.unregister<ValiderDemandeAideUseCase>();
_sl.unregister<CalculerPrioriteDemandeUseCase>();
// Use Cases - Propositions d'aide
_sl.unregister<CreerPropositionAideUseCase>();
_sl.unregister<MettreAJourPropositionAideUseCase>();
_sl.unregister<ObtenirPropositionAideUseCase>();
_sl.unregister<ChangerStatutPropositionUseCase>();
_sl.unregister<RechercherPropositionsAideUseCase>();
_sl.unregister<ObtenirPropositionsActivesUseCase>();
_sl.unregister<ObtenirMeilleuresPropositionsUseCase>();
_sl.unregister<ObtenirMesPropositionsUseCase>();
_sl.unregister<ValiderPropositionAideUseCase>();
_sl.unregister<CalculerScorePropositionUseCase>();
// Use Cases - Matching
_sl.unregister<TrouverPropositionsCompatiblesUseCase>();
_sl.unregister<TrouverDemandesCompatiblesUseCase>();
_sl.unregister<RechercherProposantsFinanciersUseCase>();
_sl.unregister<CalculerScoreCompatibiliteUseCase>();
_sl.unregister<EffectuerMatchingIntelligentUseCase>();
_sl.unregister<AnalyserTendancesMatchingUseCase>();
// Use Cases - Évaluations
_sl.unregister<CreerEvaluationUseCase>();
_sl.unregister<MettreAJourEvaluationUseCase>();
_sl.unregister<ObtenirEvaluationUseCase>();
_sl.unregister<ObtenirEvaluationsDemandeUseCase>();
_sl.unregister<ObtenirEvaluationsPropositionUseCase>();
_sl.unregister<SignalerEvaluationUseCase>();
_sl.unregister<CalculerMoyenneDemandeUseCase>();
_sl.unregister<CalculerMoyennePropositionUseCase>();
_sl.unregister<ValiderEvaluationUseCase>();
_sl.unregister<CalculerScoreQualiteEvaluationUseCase>();
_sl.unregister<AnalyserTendancesEvaluationUseCase>();
// Use Cases - Statistiques
_sl.unregister<ObtenirStatistiquesSolidariteUseCase>();
_sl.unregister<CalculerKPIsPerformanceUseCase>();
_sl.unregister<GenererRapportActiviteUseCase>();
// Repository et Data Sources
_sl.unregister<SolidariteRepository>();
_sl.unregister<SolidariteRemoteDataSource>();
_sl.unregister<SolidariteLocalDataSource>();
}
/// Obtient une instance d'un service enregistré
static T get<T extends Object>() => _sl.get<T>();
/// Vérifie si un service est enregistré
static bool isRegistered<T extends Object>() => _sl.isRegistered<T>();
/// Réinitialise complètement le container
static Future<void> reset() async {
await dispose();
await init();
}
/// Obtient des statistiques sur les services enregistrés
static Map<String, dynamic> getStats() {
return {
'totalServices': _sl.allReadySync().length,
'solidariteServices': {
'useCases': {
'demandes': 10,
'propositions': 10,
'matching': 6,
'evaluations': 11,
'statistiques': 3,
},
'repositories': 1,
'dataSources': 2,
},
'isInitialized': _sl.isRegistered<SolidariteRepository>(),
};
}
/// Valide que tous les services critiques sont enregistrés
static bool validateConfiguration() {
try {
// Vérifier les services critiques
final criticalServices = [
SolidariteRepository,
SolidariteRemoteDataSource,
SolidariteLocalDataSource,
CreerDemandeAideUseCase,
CreerPropositionAideUseCase,
CreerEvaluationUseCase,
ObtenirStatistiquesSolidariteUseCase,
];
for (final serviceType in criticalServices) {
if (!_sl.isRegistered(instance: serviceType)) {
return false;
}
}
return true;
} catch (e) {
return false;
}
}
/// Effectue un test de santé des services
static Future<Map<String, bool>> healthCheck() async {
final results = <String, bool>{};
try {
// Test du repository
final repository = _sl.get<SolidariteRepository>();
results['repository'] = repository != null;
// Test des data sources
final remoteDataSource = _sl.get<SolidariteRemoteDataSource>();
results['remoteDataSource'] = remoteDataSource != null;
final localDataSource = _sl.get<SolidariteLocalDataSource>();
results['localDataSource'] = localDataSource != null;
// Test des use cases critiques
final creerDemandeUseCase = _sl.get<CreerDemandeAideUseCase>();
results['creerDemandeUseCase'] = creerDemandeUseCase != null;
final creerPropositionUseCase = _sl.get<CreerPropositionAideUseCase>();
results['creerPropositionUseCase'] = creerPropositionUseCase != null;
final creerEvaluationUseCase = _sl.get<CreerEvaluationUseCase>();
results['creerEvaluationUseCase'] = creerEvaluationUseCase != null;
// Test des services de base
results['networkInfo'] = _sl.isRegistered<NetworkInfo>();
results['apiClient'] = _sl.isRegistered<ApiClient>();
results['sharedPreferences'] = _sl.isRegistered<SharedPreferences>();
} catch (e) {
results['error'] = false;
}
return results;
}
}
/// Extension pour faciliter l'accès aux services depuis les widgets
extension SolidariteServiceLocator on GetIt {
// Use Cases - Demandes d'aide
CreerDemandeAideUseCase get creerDemandeAide => get<CreerDemandeAideUseCase>();
MettreAJourDemandeAideUseCase get mettreAJourDemandeAide => get<MettreAJourDemandeAideUseCase>();
ObtenirDemandeAideUseCase get obtenirDemandeAide => get<ObtenirDemandeAideUseCase>();
SoumettreDemandeAideUseCase get soumettreDemandeAide => get<SoumettreDemandeAideUseCase>();
EvaluerDemandeAideUseCase get evaluerDemandeAide => get<EvaluerDemandeAideUseCase>();
RechercherDemandesAideUseCase get rechercherDemandesAide => get<RechercherDemandesAideUseCase>();
ObtenirDemandesUrgentesUseCase get obtenirDemandesUrgentes => get<ObtenirDemandesUrgentesUseCase>();
ObtenirMesDemandesUseCase get obtenirMesdemandes => get<ObtenirMesDemandesUseCase>();
ValiderDemandeAideUseCase get validerDemandeAide => get<ValiderDemandeAideUseCase>();
CalculerPrioriteDemandeUseCase get calculerPrioriteDemande => get<CalculerPrioriteDemandeUseCase>();
// Use Cases - Propositions d'aide
CreerPropositionAideUseCase get creerPropositionAide => get<CreerPropositionAideUseCase>();
MettreAJourPropositionAideUseCase get mettreAJourPropositionAide => get<MettreAJourPropositionAideUseCase>();
ObtenirPropositionAideUseCase get obtenirPropositionAide => get<ObtenirPropositionAideUseCase>();
ChangerStatutPropositionUseCase get changerStatutProposition => get<ChangerStatutPropositionUseCase>();
RechercherPropositionsAideUseCase get rechercherPropositionsAide => get<RechercherPropositionsAideUseCase>();
ObtenirPropositionsActivesUseCase get obtenirPropositionsActives => get<ObtenirPropositionsActivesUseCase>();
ObtenirMeilleuresPropositionsUseCase get obtenirMeilleuresPropositions => get<ObtenirMeilleuresPropositionsUseCase>();
ObtenirMesPropositionsUseCase get obtenirMesPropositions => get<ObtenirMesPropositionsUseCase>();
ValiderPropositionAideUseCase get validerPropositionAide => get<ValiderPropositionAideUseCase>();
CalculerScorePropositionUseCase get calculerScoreProposition => get<CalculerScorePropositionUseCase>();
// Use Cases - Matching
TrouverPropositionsCompatiblesUseCase get trouverPropositionsCompatibles => get<TrouverPropositionsCompatiblesUseCase>();
TrouverDemandesCompatiblesUseCase get trouverDemandesCompatibles => get<TrouverDemandesCompatiblesUseCase>();
RechercherProposantsFinanciersUseCase get rechercherProposantsFinanciers => get<RechercherProposantsFinanciersUseCase>();
CalculerScoreCompatibiliteUseCase get calculerScoreCompatibilite => get<CalculerScoreCompatibiliteUseCase>();
EffectuerMatchingIntelligentUseCase get effectuerMatchingIntelligent => get<EffectuerMatchingIntelligentUseCase>();
AnalyserTendancesMatchingUseCase get analyserTendancesMatching => get<AnalyserTendancesMatchingUseCase>();
// Use Cases - Évaluations
CreerEvaluationUseCase get creerEvaluation => get<CreerEvaluationUseCase>();
MettreAJourEvaluationUseCase get mettreAJourEvaluation => get<MettreAJourEvaluationUseCase>();
ObtenirEvaluationUseCase get obtenirEvaluation => get<ObtenirEvaluationUseCase>();
ObtenirEvaluationsDemandeUseCase get obtenirEvaluationsDemande => get<ObtenirEvaluationsDemandeUseCase>();
ObtenirEvaluationsPropositionUseCase get obtenirEvaluationsProposition => get<ObtenirEvaluationsPropositionUseCase>();
SignalerEvaluationUseCase get signalerEvaluation => get<SignalerEvaluationUseCase>();
CalculerMoyenneDemandeUseCase get calculerMoyenneDemande => get<CalculerMoyenneDemandeUseCase>();
CalculerMoyennePropositionUseCase get calculerMoyenneProposition => get<CalculerMoyennePropositionUseCase>();
ValiderEvaluationUseCase get validerEvaluation => get<ValiderEvaluationUseCase>();
CalculerScoreQualiteEvaluationUseCase get calculerScoreQualiteEvaluation => get<CalculerScoreQualiteEvaluationUseCase>();
AnalyserTendancesEvaluationUseCase get analyserTendancesEvaluation => get<AnalyserTendancesEvaluationUseCase>();
// Use Cases - Statistiques
ObtenirStatistiquesSolidariteUseCase get obtenirStatistiquesSolidarite => get<ObtenirStatistiquesSolidariteUseCase>();
CalculerKPIsPerformanceUseCase get calculerKPIsPerformance => get<CalculerKPIsPerformanceUseCase>();
GenererRapportActiviteUseCase get genererRapportActivite => get<GenererRapportActiviteUseCase>();
// Repository
SolidariteRepository get solidariteRepository => get<SolidariteRepository>();
}

View File

@@ -0,0 +1,524 @@
import '../../domain/entities/demande_aide.dart';
/// Modèle de données pour les demandes d'aide
///
/// Ce modèle fait la conversion entre les DTOs de l'API REST
/// et les entités du domaine pour les demandes d'aide.
class DemandeAideModel extends DemandeAide {
const DemandeAideModel({
required super.id,
required super.numeroReference,
required super.titre,
required super.description,
required super.typeAide,
required super.statut,
required super.priorite,
required super.demandeurId,
required super.nomDemandeur,
required super.organisationId,
super.montantDemande,
super.montantApprouve,
super.montantVerse,
required super.dateCreation,
required super.dateModification,
super.dateSoumission,
super.dateEvaluation,
super.dateApprobation,
super.dateLimiteTraitement,
super.evaluateurId,
super.commentairesEvaluateur,
super.motifRejet,
super.informationsRequises,
super.justificationUrgence,
super.contactUrgence,
super.localisation,
super.beneficiaires,
super.piecesJustificatives,
super.historiqueStatuts,
super.commentaires,
super.donneesPersonnalisees,
super.estModifiable,
super.estUrgente,
super.delaiDepasse,
super.estTerminee,
});
/// Crée un modèle à partir d'un JSON (API Response)
factory DemandeAideModel.fromJson(Map<String, dynamic> json) {
return DemandeAideModel(
id: json['id'] as String,
numeroReference: json['numeroReference'] as String,
titre: json['titre'] as String,
description: json['description'] as String,
typeAide: _parseTypeAide(json['typeAide'] as String),
statut: _parseStatutAide(json['statut'] as String),
priorite: _parsePrioriteAide(json['priorite'] as String),
demandeurId: json['demandeurId'] as String,
nomDemandeur: json['nomDemandeur'] as String,
organisationId: json['organisationId'] as String,
montantDemande: json['montantDemande']?.toDouble(),
montantApprouve: json['montantApprouve']?.toDouble(),
montantVerse: json['montantVerse']?.toDouble(),
dateCreation: DateTime.parse(json['dateCreation'] as String),
dateModification: DateTime.parse(json['dateModification'] as String),
dateSoumission: json['dateSoumission'] != null
? DateTime.parse(json['dateSoumission'] as String)
: null,
dateEvaluation: json['dateEvaluation'] != null
? DateTime.parse(json['dateEvaluation'] as String)
: null,
dateApprobation: json['dateApprobation'] != null
? DateTime.parse(json['dateApprobation'] as String)
: null,
dateLimiteTraitement: json['dateLimiteTraitement'] != null
? DateTime.parse(json['dateLimiteTraitement'] as String)
: null,
evaluateurId: json['evaluateurId'] as String?,
commentairesEvaluateur: json['commentairesEvaluateur'] as String?,
motifRejet: json['motifRejet'] as String?,
informationsRequises: json['informationsRequises'] as String?,
justificationUrgence: json['justificationUrgence'] as String?,
contactUrgence: json['contactUrgence'] != null
? ContactUrgenceModel.fromJson(json['contactUrgence'] as Map<String, dynamic>)
: null,
localisation: json['localisation'] != null
? LocalisationModel.fromJson(json['localisation'] as Map<String, dynamic>)
: null,
beneficiaires: (json['beneficiaires'] as List<dynamic>?)
?.map((e) => BeneficiaireAideModel.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
piecesJustificatives: (json['piecesJustificatives'] as List<dynamic>?)
?.map((e) => PieceJustificativeModel.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
historiqueStatuts: (json['historiqueStatuts'] as List<dynamic>?)
?.map((e) => HistoriqueStatutModel.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
commentaires: (json['commentaires'] as List<dynamic>?)
?.map((e) => CommentaireAideModel.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
donneesPersonnalisees: Map<String, dynamic>.from(json['donneesPersonnalisees'] ?? {}),
estModifiable: json['estModifiable'] as bool? ?? false,
estUrgente: json['estUrgente'] as bool? ?? false,
delaiDepasse: json['delaiDepasse'] as bool? ?? false,
estTerminee: json['estTerminee'] as bool? ?? false,
);
}
/// Convertit le modèle en JSON (API Request)
Map<String, dynamic> toJson() {
return {
'id': id,
'numeroReference': numeroReference,
'titre': titre,
'description': description,
'typeAide': typeAide.name,
'statut': statut.name,
'priorite': priorite.name,
'demandeurId': demandeurId,
'nomDemandeur': nomDemandeur,
'organisationId': organisationId,
'montantDemande': montantDemande,
'montantApprouve': montantApprouve,
'montantVerse': montantVerse,
'dateCreation': dateCreation.toIso8601String(),
'dateModification': dateModification.toIso8601String(),
'dateSoumission': dateSoumission?.toIso8601String(),
'dateEvaluation': dateEvaluation?.toIso8601String(),
'dateApprobation': dateApprobation?.toIso8601String(),
'dateLimiteTraitement': dateLimiteTraitement?.toIso8601String(),
'evaluateurId': evaluateurId,
'commentairesEvaluateur': commentairesEvaluateur,
'motifRejet': motifRejet,
'informationsRequises': informationsRequises,
'justificationUrgence': justificationUrgence,
'contactUrgence': contactUrgence != null
? (contactUrgence as ContactUrgenceModel).toJson()
: null,
'localisation': localisation != null
? (localisation as LocalisationModel).toJson()
: null,
'beneficiaires': beneficiaires
.map((e) => (e as BeneficiaireAideModel).toJson())
.toList(),
'piecesJustificatives': piecesJustificatives
.map((e) => (e as PieceJustificativeModel).toJson())
.toList(),
'historiqueStatuts': historiqueStatuts
.map((e) => (e as HistoriqueStatutModel).toJson())
.toList(),
'commentaires': commentaires
.map((e) => (e as CommentaireAideModel).toJson())
.toList(),
'donneesPersonnalisees': donneesPersonnalisees,
'estModifiable': estModifiable,
'estUrgente': estUrgente,
'delaiDepasse': delaiDepasse,
'estTerminee': estTerminee,
};
}
/// Crée un modèle à partir d'une entité du domaine
factory DemandeAideModel.fromEntity(DemandeAide entity) {
return DemandeAideModel(
id: entity.id,
numeroReference: entity.numeroReference,
titre: entity.titre,
description: entity.description,
typeAide: entity.typeAide,
statut: entity.statut,
priorite: entity.priorite,
demandeurId: entity.demandeurId,
nomDemandeur: entity.nomDemandeur,
organisationId: entity.organisationId,
montantDemande: entity.montantDemande,
montantApprouve: entity.montantApprouve,
montantVerse: entity.montantVerse,
dateCreation: entity.dateCreation,
dateModification: entity.dateModification,
dateSoumission: entity.dateSoumission,
dateEvaluation: entity.dateEvaluation,
dateApprobation: entity.dateApprobation,
dateLimiteTraitement: entity.dateLimiteTraitement,
evaluateurId: entity.evaluateurId,
commentairesEvaluateur: entity.commentairesEvaluateur,
motifRejet: entity.motifRejet,
informationsRequises: entity.informationsRequises,
justificationUrgence: entity.justificationUrgence,
contactUrgence: entity.contactUrgence != null
? ContactUrgenceModel.fromEntity(entity.contactUrgence!)
: null,
localisation: entity.localisation != null
? LocalisationModel.fromEntity(entity.localisation!)
: null,
beneficiaires: entity.beneficiaires
.map((e) => BeneficiaireAideModel.fromEntity(e))
.toList(),
piecesJustificatives: entity.piecesJustificatives
.map((e) => PieceJustificativeModel.fromEntity(e))
.toList(),
historiqueStatuts: entity.historiqueStatuts
.map((e) => HistoriqueStatutModel.fromEntity(e))
.toList(),
commentaires: entity.commentaires
.map((e) => CommentaireAideModel.fromEntity(e))
.toList(),
donneesPersonnalisees: Map<String, dynamic>.from(entity.donneesPersonnalisees),
estModifiable: entity.estModifiable,
estUrgente: entity.estUrgente,
delaiDepasse: entity.delaiDepasse,
estTerminee: entity.estTerminee,
);
}
/// Convertit le modèle en entité du domaine
DemandeAide toEntity() {
return DemandeAide(
id: id,
numeroReference: numeroReference,
titre: titre,
description: description,
typeAide: typeAide,
statut: statut,
priorite: priorite,
demandeurId: demandeurId,
nomDemandeur: nomDemandeur,
organisationId: organisationId,
montantDemande: montantDemande,
montantApprouve: montantApprouve,
montantVerse: montantVerse,
dateCreation: dateCreation,
dateModification: dateModification,
dateSoumission: dateSoumission,
dateEvaluation: dateEvaluation,
dateApprobation: dateApprobation,
dateLimiteTraitement: dateLimiteTraitement,
evaluateurId: evaluateurId,
commentairesEvaluateur: commentairesEvaluateur,
motifRejet: motifRejet,
informationsRequises: informationsRequises,
justificationUrgence: justificationUrgence,
contactUrgence: contactUrgence,
localisation: localisation,
beneficiaires: beneficiaires,
piecesJustificatives: piecesJustificatives,
historiqueStatuts: historiqueStatuts,
commentaires: commentaires,
donneesPersonnalisees: donneesPersonnalisees,
estModifiable: estModifiable,
estUrgente: estUrgente,
delaiDepasse: delaiDepasse,
estTerminee: estTerminee,
);
}
// Méthodes utilitaires de parsing
static TypeAide _parseTypeAide(String value) {
return TypeAide.values.firstWhere(
(e) => e.name == value,
orElse: () => TypeAide.autre,
);
}
static StatutAide _parseStatutAide(String value) {
return StatutAide.values.firstWhere(
(e) => e.name == value,
orElse: () => StatutAide.brouillon,
);
}
static PrioriteAide _parsePrioriteAide(String value) {
return PrioriteAide.values.firstWhere(
(e) => e.name == value,
orElse: () => PrioriteAide.normale,
);
}
}
/// Modèles pour les classes auxiliaires
class ContactUrgenceModel extends ContactUrgence {
const ContactUrgenceModel({
required super.nom,
required super.telephone,
super.email,
required super.relation,
});
factory ContactUrgenceModel.fromJson(Map<String, dynamic> json) {
return ContactUrgenceModel(
nom: json['nom'] as String,
telephone: json['telephone'] as String,
email: json['email'] as String?,
relation: json['relation'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'nom': nom,
'telephone': telephone,
'email': email,
'relation': relation,
};
}
factory ContactUrgenceModel.fromEntity(ContactUrgence entity) {
return ContactUrgenceModel(
nom: entity.nom,
telephone: entity.telephone,
email: entity.email,
relation: entity.relation,
);
}
}
class LocalisationModel extends Localisation {
const LocalisationModel({
required super.adresse,
required super.ville,
super.codePostal,
super.pays,
super.latitude,
super.longitude,
});
factory LocalisationModel.fromJson(Map<String, dynamic> json) {
return LocalisationModel(
adresse: json['adresse'] as String,
ville: json['ville'] as String,
codePostal: json['codePostal'] as String?,
pays: json['pays'] as String?,
latitude: json['latitude']?.toDouble(),
longitude: json['longitude']?.toDouble(),
);
}
Map<String, dynamic> toJson() {
return {
'adresse': adresse,
'ville': ville,
'codePostal': codePostal,
'pays': pays,
'latitude': latitude,
'longitude': longitude,
};
}
factory LocalisationModel.fromEntity(Localisation entity) {
return LocalisationModel(
adresse: entity.adresse,
ville: entity.ville,
codePostal: entity.codePostal,
pays: entity.pays,
latitude: entity.latitude,
longitude: entity.longitude,
);
}
}
class BeneficiaireAideModel extends BeneficiaireAide {
const BeneficiaireAideModel({
required super.nom,
required super.prenom,
required super.age,
required super.relation,
super.telephone,
});
factory BeneficiaireAideModel.fromJson(Map<String, dynamic> json) {
return BeneficiaireAideModel(
nom: json['nom'] as String,
prenom: json['prenom'] as String,
age: json['age'] as int,
relation: json['relation'] as String,
telephone: json['telephone'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'nom': nom,
'prenom': prenom,
'age': age,
'relation': relation,
'telephone': telephone,
};
}
factory BeneficiaireAideModel.fromEntity(BeneficiaireAide entity) {
return BeneficiaireAideModel(
nom: entity.nom,
prenom: entity.prenom,
age: entity.age,
relation: entity.relation,
telephone: entity.telephone,
);
}
}
class PieceJustificativeModel extends PieceJustificative {
const PieceJustificativeModel({
required super.id,
required super.nom,
required super.type,
required super.url,
required super.taille,
required super.dateAjout,
});
factory PieceJustificativeModel.fromJson(Map<String, dynamic> json) {
return PieceJustificativeModel(
id: json['id'] as String,
nom: json['nom'] as String,
type: json['type'] as String,
url: json['url'] as String,
taille: json['taille'] as int,
dateAjout: DateTime.parse(json['dateAjout'] as String),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'nom': nom,
'type': type,
'url': url,
'taille': taille,
'dateAjout': dateAjout.toIso8601String(),
};
}
factory PieceJustificativeModel.fromEntity(PieceJustificative entity) {
return PieceJustificativeModel(
id: entity.id,
nom: entity.nom,
type: entity.type,
url: entity.url,
taille: entity.taille,
dateAjout: entity.dateAjout,
);
}
}
class HistoriqueStatutModel extends HistoriqueStatut {
const HistoriqueStatutModel({
required super.ancienStatut,
required super.nouveauStatut,
required super.dateChangement,
super.commentaire,
super.utilisateurId,
});
factory HistoriqueStatutModel.fromJson(Map<String, dynamic> json) {
return HistoriqueStatutModel(
ancienStatut: DemandeAideModel._parseStatutAide(json['ancienStatut'] as String),
nouveauStatut: DemandeAideModel._parseStatutAide(json['nouveauStatut'] as String),
dateChangement: DateTime.parse(json['dateChangement'] as String),
commentaire: json['commentaire'] as String?,
utilisateurId: json['utilisateurId'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'ancienStatut': ancienStatut.name,
'nouveauStatut': nouveauStatut.name,
'dateChangement': dateChangement.toIso8601String(),
'commentaire': commentaire,
'utilisateurId': utilisateurId,
};
}
factory HistoriqueStatutModel.fromEntity(HistoriqueStatut entity) {
return HistoriqueStatutModel(
ancienStatut: entity.ancienStatut,
nouveauStatut: entity.nouveauStatut,
dateChangement: entity.dateChangement,
commentaire: entity.commentaire,
utilisateurId: entity.utilisateurId,
);
}
}
class CommentaireAideModel extends CommentaireAide {
const CommentaireAideModel({
required super.id,
required super.contenu,
required super.auteurId,
required super.nomAuteur,
required super.dateCreation,
super.estPrive,
});
factory CommentaireAideModel.fromJson(Map<String, dynamic> json) {
return CommentaireAideModel(
id: json['id'] as String,
contenu: json['contenu'] as String,
auteurId: json['auteurId'] as String,
nomAuteur: json['nomAuteur'] as String,
dateCreation: DateTime.parse(json['dateCreation'] as String),
estPrive: json['estPrive'] as bool? ?? false,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'contenu': contenu,
'auteurId': auteurId,
'nomAuteur': nomAuteur,
'dateCreation': dateCreation.toIso8601String(),
'estPrive': estPrive,
};
}
factory CommentaireAideModel.fromEntity(CommentaireAide entity) {
return CommentaireAideModel(
id: entity.id,
contenu: entity.contenu,
auteurId: entity.auteurId,
nomAuteur: entity.nomAuteur,
dateCreation: entity.dateCreation,
estPrive: entity.estPrive,
);
}
}

View File

@@ -0,0 +1,388 @@
import '../../domain/entities/evaluation_aide.dart';
/// Modèle de données pour les évaluations d'aide
///
/// Ce modèle fait la conversion entre les DTOs de l'API REST
/// et les entités du domaine pour les évaluations d'aide.
class EvaluationAideModel extends EvaluationAide {
const EvaluationAideModel({
required super.id,
required super.demandeId,
super.propositionId,
required super.evaluateurId,
required super.nomEvaluateur,
required super.typeEvaluateur,
required super.statut,
required super.noteGlobale,
super.noteDelaiReponse,
super.noteCommunication,
super.noteProfessionnalisme,
super.noteRespectEngagements,
required super.commentairePrincipal,
super.pointsPositifs,
super.pointsAmelioration,
super.recommandations,
super.recommande,
required super.dateCreation,
required super.dateModification,
super.dateValidation,
super.validateurId,
super.motifSignalement,
super.nombreSignalements,
super.estModeree,
super.estPublique,
super.donneesPersonnalisees,
});
/// Crée un modèle à partir d'un JSON (API Response)
factory EvaluationAideModel.fromJson(Map<String, dynamic> json) {
return EvaluationAideModel(
id: json['id'] as String,
demandeId: json['demandeId'] as String,
propositionId: json['propositionId'] as String?,
evaluateurId: json['evaluateurId'] as String,
nomEvaluateur: json['nomEvaluateur'] as String,
typeEvaluateur: _parseTypeEvaluateur(json['typeEvaluateur'] as String),
statut: _parseStatutEvaluation(json['statut'] as String),
noteGlobale: json['noteGlobale'].toDouble(),
noteDelaiReponse: json['noteDelaiReponse']?.toDouble(),
noteCommunication: json['noteCommunication']?.toDouble(),
noteProfessionnalisme: json['noteProfessionnalisme']?.toDouble(),
noteRespectEngagements: json['noteRespectEngagements']?.toDouble(),
commentairePrincipal: json['commentairePrincipal'] as String,
pointsPositifs: json['pointsPositifs'] as String?,
pointsAmelioration: json['pointsAmelioration'] as String?,
recommandations: json['recommandations'] as String?,
recommande: json['recommande'] as bool?,
dateCreation: DateTime.parse(json['dateCreation'] as String),
dateModification: DateTime.parse(json['dateModification'] as String),
dateValidation: json['dateValidation'] != null
? DateTime.parse(json['dateValidation'] as String)
: null,
validateurId: json['validateurId'] as String?,
motifSignalement: json['motifSignalement'] as String?,
nombreSignalements: json['nombreSignalements'] as int? ?? 0,
estModeree: json['estModeree'] as bool? ?? false,
estPublique: json['estPublique'] as bool? ?? true,
donneesPersonnalisees: Map<String, dynamic>.from(json['donneesPersonnalisees'] ?? {}),
);
}
/// Convertit le modèle en JSON (API Request)
Map<String, dynamic> toJson() {
return {
'id': id,
'demandeId': demandeId,
'propositionId': propositionId,
'evaluateurId': evaluateurId,
'nomEvaluateur': nomEvaluateur,
'typeEvaluateur': typeEvaluateur.name,
'statut': statut.name,
'noteGlobale': noteGlobale,
'noteDelaiReponse': noteDelaiReponse,
'noteCommunication': noteCommunication,
'noteProfessionnalisme': noteProfessionnalisme,
'noteRespectEngagements': noteRespectEngagements,
'commentairePrincipal': commentairePrincipal,
'pointsPositifs': pointsPositifs,
'pointsAmelioration': pointsAmelioration,
'recommandations': recommandations,
'recommande': recommande,
'dateCreation': dateCreation.toIso8601String(),
'dateModification': dateModification.toIso8601String(),
'dateValidation': dateValidation?.toIso8601String(),
'validateurId': validateurId,
'motifSignalement': motifSignalement,
'nombreSignalements': nombreSignalements,
'estModeree': estModeree,
'estPublique': estPublique,
'donneesPersonnalisees': donneesPersonnalisees,
};
}
/// Crée un modèle à partir d'une entité du domaine
factory EvaluationAideModel.fromEntity(EvaluationAide entity) {
return EvaluationAideModel(
id: entity.id,
demandeId: entity.demandeId,
propositionId: entity.propositionId,
evaluateurId: entity.evaluateurId,
nomEvaluateur: entity.nomEvaluateur,
typeEvaluateur: entity.typeEvaluateur,
statut: entity.statut,
noteGlobale: entity.noteGlobale,
noteDelaiReponse: entity.noteDelaiReponse,
noteCommunication: entity.noteCommunication,
noteProfessionnalisme: entity.noteProfessionnalisme,
noteRespectEngagements: entity.noteRespectEngagements,
commentairePrincipal: entity.commentairePrincipal,
pointsPositifs: entity.pointsPositifs,
pointsAmelioration: entity.pointsAmelioration,
recommandations: entity.recommandations,
recommande: entity.recommande,
dateCreation: entity.dateCreation,
dateModification: entity.dateModification,
dateValidation: entity.dateValidation,
validateurId: entity.validateurId,
motifSignalement: entity.motifSignalement,
nombreSignalements: entity.nombreSignalements,
estModeree: entity.estModeree,
estPublique: entity.estPublique,
donneesPersonnalisees: Map<String, dynamic>.from(entity.donneesPersonnalisees),
);
}
/// Convertit le modèle en entité du domaine
EvaluationAide toEntity() {
return EvaluationAide(
id: id,
demandeId: demandeId,
propositionId: propositionId,
evaluateurId: evaluateurId,
nomEvaluateur: nomEvaluateur,
typeEvaluateur: typeEvaluateur,
statut: statut,
noteGlobale: noteGlobale,
noteDelaiReponse: noteDelaiReponse,
noteCommunication: noteCommunication,
noteProfessionnalisme: noteProfessionnalisme,
noteRespectEngagements: noteRespectEngagements,
commentairePrincipal: commentairePrincipal,
pointsPositifs: pointsPositifs,
pointsAmelioration: pointsAmelioration,
recommandations: recommandations,
recommande: recommande,
dateCreation: dateCreation,
dateModification: dateModification,
dateValidation: dateValidation,
validateurId: validateurId,
motifSignalement: motifSignalement,
nombreSignalements: nombreSignalements,
estModeree: estModeree,
estPublique: estPublique,
donneesPersonnalisees: donneesPersonnalisees,
);
}
// Méthodes utilitaires de parsing
static TypeEvaluateur _parseTypeEvaluateur(String value) {
return TypeEvaluateur.values.firstWhere(
(e) => e.name == value,
orElse: () => TypeEvaluateur.beneficiaire,
);
}
static StatutEvaluation _parseStatutEvaluation(String value) {
return StatutEvaluation.values.firstWhere(
(e) => e.name == value,
orElse: () => StatutEvaluation.brouillon,
);
}
}
/// Modèle pour les statistiques d'évaluation
class StatistiquesEvaluationModel {
final double noteMoyenne;
final int nombreEvaluations;
final Map<int, int> repartitionNotes;
final double pourcentageRecommandations;
final List<EvaluationAideModel> evaluationsRecentes;
final DateTime dateCalcul;
const StatistiquesEvaluationModel({
required this.noteMoyenne,
required this.nombreEvaluations,
required this.repartitionNotes,
required this.pourcentageRecommandations,
required this.evaluationsRecentes,
required this.dateCalcul,
});
/// Crée un modèle à partir d'un JSON (API Response)
factory StatistiquesEvaluationModel.fromJson(Map<String, dynamic> json) {
return StatistiquesEvaluationModel(
noteMoyenne: json['noteMoyenne'].toDouble(),
nombreEvaluations: json['nombreEvaluations'] as int,
repartitionNotes: Map<int, int>.from(json['repartitionNotes']),
pourcentageRecommandations: json['pourcentageRecommandations'].toDouble(),
evaluationsRecentes: (json['evaluationsRecentes'] as List<dynamic>)
.map((e) => EvaluationAideModel.fromJson(e as Map<String, dynamic>))
.toList(),
dateCalcul: DateTime.parse(json['dateCalcul'] as String),
);
}
/// Convertit le modèle en JSON
Map<String, dynamic> toJson() {
return {
'noteMoyenne': noteMoyenne,
'nombreEvaluations': nombreEvaluations,
'repartitionNotes': repartitionNotes,
'pourcentageRecommandations': pourcentageRecommandations,
'evaluationsRecentes': evaluationsRecentes
.map((e) => e.toJson())
.toList(),
'dateCalcul': dateCalcul.toIso8601String(),
};
}
/// Convertit le modèle en entité du domaine
StatistiquesEvaluation toEntity() {
return StatistiquesEvaluation(
noteMoyenne: noteMoyenne,
nombreEvaluations: nombreEvaluations,
repartitionNotes: repartitionNotes,
pourcentageRecommandations: pourcentageRecommandations,
evaluationsRecentes: evaluationsRecentes
.map((e) => e.toEntity())
.toList(),
dateCalcul: dateCalcul,
);
}
/// Crée un modèle à partir d'une entité du domaine
factory StatistiquesEvaluationModel.fromEntity(StatistiquesEvaluation entity) {
return StatistiquesEvaluationModel(
noteMoyenne: entity.noteMoyenne,
nombreEvaluations: entity.nombreEvaluations,
repartitionNotes: Map<int, int>.from(entity.repartitionNotes),
pourcentageRecommandations: entity.pourcentageRecommandations,
evaluationsRecentes: entity.evaluationsRecentes
.map((e) => EvaluationAideModel.fromEntity(e))
.toList(),
dateCalcul: entity.dateCalcul,
);
}
}
/// Modèle pour les réponses de recherche d'évaluations
class RechercheEvaluationsResponse {
final List<EvaluationAideModel> evaluations;
final int totalElements;
final int totalPages;
final int currentPage;
final int pageSize;
final bool hasNext;
final bool hasPrevious;
const RechercheEvaluationsResponse({
required this.evaluations,
required this.totalElements,
required this.totalPages,
required this.currentPage,
required this.pageSize,
required this.hasNext,
required this.hasPrevious,
});
/// Crée un modèle à partir d'un JSON (API Response)
factory RechercheEvaluationsResponse.fromJson(Map<String, dynamic> json) {
return RechercheEvaluationsResponse(
evaluations: (json['content'] as List<dynamic>)
.map((e) => EvaluationAideModel.fromJson(e as Map<String, dynamic>))
.toList(),
totalElements: json['totalElements'] as int,
totalPages: json['totalPages'] as int,
currentPage: json['number'] as int,
pageSize: json['size'] as int,
hasNext: !(json['last'] as bool),
hasPrevious: !(json['first'] as bool),
);
}
/// Convertit le modèle en JSON
Map<String, dynamic> toJson() {
return {
'content': evaluations.map((e) => e.toJson()).toList(),
'totalElements': totalElements,
'totalPages': totalPages,
'number': currentPage,
'size': pageSize,
'last': !hasNext,
'first': !hasPrevious,
};
}
}
/// Modèle pour les requêtes de création d'évaluation
class CreerEvaluationRequest {
final String demandeId;
final String? propositionId;
final String evaluateurId;
final TypeEvaluateur typeEvaluateur;
final double noteGlobale;
final double? noteDelaiReponse;
final double? noteCommunication;
final double? noteProfessionnalisme;
final double? noteRespectEngagements;
final String commentairePrincipal;
final String? pointsPositifs;
final String? pointsAmelioration;
final String? recommandations;
final bool? recommande;
final bool estPublique;
final Map<String, dynamic> donneesPersonnalisees;
const CreerEvaluationRequest({
required this.demandeId,
this.propositionId,
required this.evaluateurId,
required this.typeEvaluateur,
required this.noteGlobale,
this.noteDelaiReponse,
this.noteCommunication,
this.noteProfessionnalisme,
this.noteRespectEngagements,
required this.commentairePrincipal,
this.pointsPositifs,
this.pointsAmelioration,
this.recommandations,
this.recommande,
this.estPublique = true,
this.donneesPersonnalisees = const {},
});
/// Convertit la requête en JSON
Map<String, dynamic> toJson() {
return {
'demandeId': demandeId,
'propositionId': propositionId,
'evaluateurId': evaluateurId,
'typeEvaluateur': typeEvaluateur.name,
'noteGlobale': noteGlobale,
'noteDelaiReponse': noteDelaiReponse,
'noteCommunication': noteCommunication,
'noteProfessionnalisme': noteProfessionnalisme,
'noteRespectEngagements': noteRespectEngagements,
'commentairePrincipal': commentairePrincipal,
'pointsPositifs': pointsPositifs,
'pointsAmelioration': pointsAmelioration,
'recommandations': recommandations,
'recommande': recommande,
'estPublique': estPublique,
'donneesPersonnalisees': donneesPersonnalisees,
};
}
/// Crée une requête à partir d'une entité d'évaluation
factory CreerEvaluationRequest.fromEntity(EvaluationAide entity) {
return CreerEvaluationRequest(
demandeId: entity.demandeId,
propositionId: entity.propositionId,
evaluateurId: entity.evaluateurId,
typeEvaluateur: entity.typeEvaluateur,
noteGlobale: entity.noteGlobale,
noteDelaiReponse: entity.noteDelaiReponse,
noteCommunication: entity.noteCommunication,
noteProfessionnalisme: entity.noteProfessionnalisme,
noteRespectEngagements: entity.noteRespectEngagements,
commentairePrincipal: entity.commentairePrincipal,
pointsPositifs: entity.pointsPositifs,
pointsAmelioration: entity.pointsAmelioration,
recommandations: entity.recommandations,
recommande: entity.recommande,
estPublique: entity.estPublique,
donneesPersonnalisees: entity.donneesPersonnalisees,
);
}
}

View File

@@ -0,0 +1,335 @@
import '../../domain/entities/proposition_aide.dart';
import '../../domain/entities/demande_aide.dart';
/// Modèle de données pour les propositions d'aide
///
/// Ce modèle fait la conversion entre les DTOs de l'API REST
/// et les entités du domaine pour les propositions d'aide.
class PropositionAideModel extends PropositionAide {
const PropositionAideModel({
required super.id,
required super.titre,
required super.description,
required super.typeAide,
required super.statut,
required super.proposantId,
required super.nomProposant,
required super.organisationId,
required super.nombreMaxBeneficiaires,
super.montantMaximum,
super.montantMinimum,
required super.delaiReponseHeures,
required super.dateCreation,
required super.dateModification,
super.dateExpiration,
super.dateActivation,
super.dateDesactivation,
required super.contactProposant,
super.zonesGeographiques,
super.creneauxDisponibilite,
super.criteresSelection,
super.conditionsSpeciales,
super.nombreBeneficiairesAides,
super.nombreVues,
super.nombreCandidatures,
super.noteMoyenne,
super.nombreEvaluations,
super.donneesPersonnalisees,
super.estVerifiee,
super.estPromue,
});
/// Crée un modèle à partir d'un JSON (API Response)
factory PropositionAideModel.fromJson(Map<String, dynamic> json) {
return PropositionAideModel(
id: json['id'] as String,
titre: json['titre'] as String,
description: json['description'] as String,
typeAide: _parseTypeAide(json['typeAide'] as String),
statut: _parseStatutProposition(json['statut'] as String),
proposantId: json['proposantId'] as String,
nomProposant: json['nomProposant'] as String,
organisationId: json['organisationId'] as String,
nombreMaxBeneficiaires: json['nombreMaxBeneficiaires'] as int,
montantMaximum: json['montantMaximum']?.toDouble(),
montantMinimum: json['montantMinimum']?.toDouble(),
delaiReponseHeures: json['delaiReponseHeures'] as int,
dateCreation: DateTime.parse(json['dateCreation'] as String),
dateModification: DateTime.parse(json['dateModification'] as String),
dateExpiration: json['dateExpiration'] != null
? DateTime.parse(json['dateExpiration'] as String)
: null,
dateActivation: json['dateActivation'] != null
? DateTime.parse(json['dateActivation'] as String)
: null,
dateDesactivation: json['dateDesactivation'] != null
? DateTime.parse(json['dateDesactivation'] as String)
: null,
contactProposant: ContactProposantModel.fromJson(
json['contactProposant'] as Map<String, dynamic>
),
zonesGeographiques: (json['zonesGeographiques'] as List<dynamic>?)
?.cast<String>() ?? [],
creneauxDisponibilite: (json['creneauxDisponibilite'] as List<dynamic>?)
?.map((e) => CreneauDisponibiliteModel.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
criteresSelection: (json['criteresSelection'] as List<dynamic>?)
?.map((e) => CritereSelectionModel.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
conditionsSpeciales: (json['conditionsSpeciales'] as List<dynamic>?)
?.cast<String>() ?? [],
nombreBeneficiairesAides: json['nombreBeneficiairesAides'] as int? ?? 0,
nombreVues: json['nombreVues'] as int? ?? 0,
nombreCandidatures: json['nombreCandidatures'] as int? ?? 0,
noteMoyenne: json['noteMoyenne']?.toDouble(),
nombreEvaluations: json['nombreEvaluations'] as int? ?? 0,
donneesPersonnalisees: Map<String, dynamic>.from(json['donneesPersonnalisees'] ?? {}),
estVerifiee: json['estVerifiee'] as bool? ?? false,
estPromue: json['estPromue'] as bool? ?? false,
);
}
/// Convertit le modèle en JSON (API Request)
Map<String, dynamic> toJson() {
return {
'id': id,
'titre': titre,
'description': description,
'typeAide': typeAide.name,
'statut': statut.name,
'proposantId': proposantId,
'nomProposant': nomProposant,
'organisationId': organisationId,
'nombreMaxBeneficiaires': nombreMaxBeneficiaires,
'montantMaximum': montantMaximum,
'montantMinimum': montantMinimum,
'delaiReponseHeures': delaiReponseHeures,
'dateCreation': dateCreation.toIso8601String(),
'dateModification': dateModification.toIso8601String(),
'dateExpiration': dateExpiration?.toIso8601String(),
'dateActivation': dateActivation?.toIso8601String(),
'dateDesactivation': dateDesactivation?.toIso8601String(),
'contactProposant': (contactProposant as ContactProposantModel).toJson(),
'zonesGeographiques': zonesGeographiques,
'creneauxDisponibilite': creneauxDisponibilite
.map((e) => (e as CreneauDisponibiliteModel).toJson())
.toList(),
'criteresSelection': criteresSelection
.map((e) => (e as CritereSelectionModel).toJson())
.toList(),
'conditionsSpeciales': conditionsSpeciales,
'nombreBeneficiairesAides': nombreBeneficiairesAides,
'nombreVues': nombreVues,
'nombreCandidatures': nombreCandidatures,
'noteMoyenne': noteMoyenne,
'nombreEvaluations': nombreEvaluations,
'donneesPersonnalisees': donneesPersonnalisees,
'estVerifiee': estVerifiee,
'estPromue': estPromue,
};
}
/// Crée un modèle à partir d'une entité du domaine
factory PropositionAideModel.fromEntity(PropositionAide entity) {
return PropositionAideModel(
id: entity.id,
titre: entity.titre,
description: entity.description,
typeAide: entity.typeAide,
statut: entity.statut,
proposantId: entity.proposantId,
nomProposant: entity.nomProposant,
organisationId: entity.organisationId,
nombreMaxBeneficiaires: entity.nombreMaxBeneficiaires,
montantMaximum: entity.montantMaximum,
montantMinimum: entity.montantMinimum,
delaiReponseHeures: entity.delaiReponseHeures,
dateCreation: entity.dateCreation,
dateModification: entity.dateModification,
dateExpiration: entity.dateExpiration,
dateActivation: entity.dateActivation,
dateDesactivation: entity.dateDesactivation,
contactProposant: ContactProposantModel.fromEntity(entity.contactProposant),
zonesGeographiques: List<String>.from(entity.zonesGeographiques),
creneauxDisponibilite: entity.creneauxDisponibilite
.map((e) => CreneauDisponibiliteModel.fromEntity(e))
.toList(),
criteresSelection: entity.criteresSelection
.map((e) => CritereSelectionModel.fromEntity(e))
.toList(),
conditionsSpeciales: List<String>.from(entity.conditionsSpeciales),
nombreBeneficiairesAides: entity.nombreBeneficiairesAides,
nombreVues: entity.nombreVues,
nombreCandidatures: entity.nombreCandidatures,
noteMoyenne: entity.noteMoyenne,
nombreEvaluations: entity.nombreEvaluations,
donneesPersonnalisees: Map<String, dynamic>.from(entity.donneesPersonnalisees),
estVerifiee: entity.estVerifiee,
estPromue: entity.estPromue,
);
}
/// Convertit le modèle en entité du domaine
PropositionAide toEntity() {
return PropositionAide(
id: id,
titre: titre,
description: description,
typeAide: typeAide,
statut: statut,
proposantId: proposantId,
nomProposant: nomProposant,
organisationId: organisationId,
nombreMaxBeneficiaires: nombreMaxBeneficiaires,
montantMaximum: montantMaximum,
montantMinimum: montantMinimum,
delaiReponseHeures: delaiReponseHeures,
dateCreation: dateCreation,
dateModification: dateModification,
dateExpiration: dateExpiration,
dateActivation: dateActivation,
dateDesactivation: dateDesactivation,
contactProposant: contactProposant,
zonesGeographiques: zonesGeographiques,
creneauxDisponibilite: creneauxDisponibilite,
criteresSelection: criteresSelection,
conditionsSpeciales: conditionsSpeciales,
nombreBeneficiairesAides: nombreBeneficiairesAides,
nombreVues: nombreVues,
nombreCandidatures: nombreCandidatures,
noteMoyenne: noteMoyenne,
nombreEvaluations: nombreEvaluations,
donneesPersonnalisees: donneesPersonnalisees,
estVerifiee: estVerifiee,
estPromue: estPromue,
);
}
// Méthodes utilitaires de parsing
static TypeAide _parseTypeAide(String value) {
return TypeAide.values.firstWhere(
(e) => e.name == value,
orElse: () => TypeAide.autre,
);
}
static StatutProposition _parseStatutProposition(String value) {
return StatutProposition.values.firstWhere(
(e) => e.name == value,
orElse: () => StatutProposition.brouillon,
);
}
}
/// Modèles pour les classes auxiliaires
class ContactProposantModel extends ContactProposant {
const ContactProposantModel({
required super.nom,
required super.telephone,
super.email,
super.adresse,
super.heuresDisponibilite,
});
factory ContactProposantModel.fromJson(Map<String, dynamic> json) {
return ContactProposantModel(
nom: json['nom'] as String,
telephone: json['telephone'] as String,
email: json['email'] as String?,
adresse: json['adresse'] as String?,
heuresDisponibilite: json['heuresDisponibilite'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'nom': nom,
'telephone': telephone,
'email': email,
'adresse': adresse,
'heuresDisponibilite': heuresDisponibilite,
};
}
factory ContactProposantModel.fromEntity(ContactProposant entity) {
return ContactProposantModel(
nom: entity.nom,
telephone: entity.telephone,
email: entity.email,
adresse: entity.adresse,
heuresDisponibilite: entity.heuresDisponibilite,
);
}
}
class CreneauDisponibiliteModel extends CreneauDisponibilite {
const CreneauDisponibiliteModel({
required super.jourSemaine,
required super.heureDebut,
required super.heureFin,
super.commentaire,
});
factory CreneauDisponibiliteModel.fromJson(Map<String, dynamic> json) {
return CreneauDisponibiliteModel(
jourSemaine: json['jourSemaine'] as String,
heureDebut: json['heureDebut'] as String,
heureFin: json['heureFin'] as String,
commentaire: json['commentaire'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'jourSemaine': jourSemaine,
'heureDebut': heureDebut,
'heureFin': heureFin,
'commentaire': commentaire,
};
}
factory CreneauDisponibiliteModel.fromEntity(CreneauDisponibilite entity) {
return CreneauDisponibiliteModel(
jourSemaine: entity.jourSemaine,
heureDebut: entity.heureDebut,
heureFin: entity.heureFin,
commentaire: entity.commentaire,
);
}
}
class CritereSelectionModel extends CritereSelection {
const CritereSelectionModel({
required super.nom,
required super.description,
required super.obligatoire,
super.valeurAttendue,
});
factory CritereSelectionModel.fromJson(Map<String, dynamic> json) {
return CritereSelectionModel(
nom: json['nom'] as String,
description: json['description'] as String,
obligatoire: json['obligatoire'] as bool,
valeurAttendue: json['valeurAttendue'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'nom': nom,
'description': description,
'obligatoire': obligatoire,
'valeurAttendue': valeurAttendue,
};
}
factory CritereSelectionModel.fromEntity(CritereSelection entity) {
return CritereSelectionModel(
nom: entity.nom,
description: entity.description,
obligatoire: entity.obligatoire,
valeurAttendue: entity.valeurAttendue,
);
}
}

View File

@@ -0,0 +1,561 @@
import 'package:dartz/dartz.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/error/exceptions.dart';
import '../../../../core/network/network_info.dart';
import '../../domain/entities/demande_aide.dart';
import '../../domain/entities/proposition_aide.dart';
import '../../domain/entities/evaluation_aide.dart';
import '../../domain/repositories/solidarite_repository.dart';
import '../datasources/solidarite_remote_data_source.dart';
import '../datasources/solidarite_local_data_source.dart';
import '../models/demande_aide_model.dart';
import '../models/proposition_aide_model.dart';
import '../models/evaluation_aide_model.dart';
/// Implémentation du repository de solidarité
///
/// Cette classe implémente le contrat défini dans le domaine
/// en combinant les sources de données locale et distante.
class SolidariteRepositoryImpl implements SolidariteRepository {
final SolidariteRemoteDataSource remoteDataSource;
final SolidariteLocalDataSource localDataSource;
final NetworkInfo networkInfo;
SolidariteRepositoryImpl({
required this.remoteDataSource,
required this.localDataSource,
required this.networkInfo,
});
// Demandes d'aide
@override
Future<Either<Failure, DemandeAide>> creerDemandeAide(DemandeAide demande) async {
try {
if (await networkInfo.isConnected) {
final demandeModel = DemandeAideModel.fromEntity(demande);
final result = await remoteDataSource.creerDemandeAide(demandeModel);
// Mettre en cache le résultat
await localDataSource.cacherDemandeAide(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
// Continuer même si la mise en cache échoue
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, DemandeAide>> mettreAJourDemandeAide(DemandeAide demande) async {
try {
if (await networkInfo.isConnected) {
final demandeModel = DemandeAideModel.fromEntity(demande);
final result = await remoteDataSource.mettreAJourDemandeAide(demandeModel);
// Mettre à jour le cache
await localDataSource.cacherDemandeAide(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, DemandeAide>> obtenirDemandeAide(String id) async {
try {
// Essayer d'abord le cache local
final cachedDemande = await localDataSource.obtenirDemandeAideCachee(id);
if (cachedDemande != null && await _estCacheValide()) {
return Right(cachedDemande.toEntity());
}
// Si pas en cache ou cache expiré, aller chercher sur le serveur
if (await networkInfo.isConnected) {
final result = await remoteDataSource.obtenirDemandeAide(id);
// Mettre en cache le résultat
await localDataSource.cacherDemandeAide(result);
return Right(result.toEntity());
} else {
// Si pas de connexion, utiliser le cache même s'il est expiré
if (cachedDemande != null) {
return Right(cachedDemande.toEntity());
}
return Left(NetworkFailure('Aucune connexion internet et aucune donnée en cache'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on NotFoundException catch (e) {
return Left(NotFoundFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, DemandeAide>> soumettreDemande(String demandeId) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.soumettreDemande(demandeId);
// Mettre à jour le cache
await localDataSource.cacherDemandeAide(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, DemandeAide>> evaluerDemande({
required String demandeId,
required String evaluateurId,
required StatutAide decision,
String? commentaire,
double? montantApprouve,
}) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.evaluerDemande(
demandeId: demandeId,
evaluateurId: evaluateurId,
decision: decision.name,
commentaire: commentaire,
montantApprouve: montantApprouve,
);
// Mettre à jour le cache
await localDataSource.cacherDemandeAide(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, List<DemandeAide>>> rechercherDemandes({
String? organisationId,
TypeAide? typeAide,
StatutAide? statut,
String? demandeurId,
bool? urgente,
int page = 0,
int taille = 20,
}) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.rechercherDemandes(
organisationId: organisationId,
typeAide: typeAide?.name,
statut: statut?.name,
demandeurId: demandeurId,
urgente: urgente,
page: page,
taille: taille,
);
// Mettre en cache les résultats
for (final demande in result) {
await localDataSource.cacherDemandeAide(demande);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : rechercher dans le cache local
final cachedDemandes = await localDataSource.obtenirDemandesAideCachees();
var filteredDemandes = cachedDemandes.where((demande) {
if (organisationId != null && demande.organisationId != organisationId) return false;
if (typeAide != null && demande.typeAide != typeAide) return false;
if (statut != null && demande.statut != statut) return false;
if (demandeurId != null && demande.demandeurId != demandeurId) return false;
if (urgente != null && demande.estUrgente != urgente) return false;
return true;
}).toList();
// Pagination locale
final startIndex = page * taille;
final endIndex = (startIndex + taille).clamp(0, filteredDemandes.length);
if (startIndex < filteredDemandes.length) {
filteredDemandes = filteredDemandes.sublist(startIndex, endIndex);
} else {
filteredDemandes = [];
}
return Right(filteredDemandes.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, List<DemandeAide>>> obtenirDemandesUrgentes(String organisationId) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.obtenirDemandesUrgentes(organisationId);
// Mettre en cache les résultats
for (final demande in result) {
await localDataSource.cacherDemandeAide(demande);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : filtrer le cache local
final cachedDemandes = await localDataSource.obtenirDemandesAideCachees();
final demandesUrgentes = cachedDemandes
.where((demande) => demande.organisationId == organisationId && demande.estUrgente)
.toList();
return Right(demandesUrgentes.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, List<DemandeAide>>> obtenirMesdemandes(String utilisateurId) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.obtenirMesdemandes(utilisateurId);
// Mettre en cache les résultats
for (final demande in result) {
await localDataSource.cacherDemandeAide(demande);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : filtrer le cache local
final cachedDemandes = await localDataSource.obtenirDemandesAideCachees();
final mesdemandes = cachedDemandes
.where((demande) => demande.demandeurId == utilisateurId)
.toList();
return Right(mesdemandes.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
// Propositions d'aide
@override
Future<Either<Failure, PropositionAide>> creerPropositionAide(PropositionAide proposition) async {
try {
if (await networkInfo.isConnected) {
final propositionModel = PropositionAideModel.fromEntity(proposition);
final result = await remoteDataSource.creerPropositionAide(propositionModel);
// Mettre en cache le résultat
await localDataSource.cacherPropositionAide(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, PropositionAide>> mettreAJourPropositionAide(PropositionAide proposition) async {
try {
if (await networkInfo.isConnected) {
final propositionModel = PropositionAideModel.fromEntity(proposition);
final result = await remoteDataSource.mettreAJourPropositionAide(propositionModel);
// Mettre à jour le cache
await localDataSource.cacherPropositionAide(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, PropositionAide>> obtenirPropositionAide(String id) async {
try {
// Essayer d'abord le cache local
final cachedProposition = await localDataSource.obtenirPropositionAideCachee(id);
if (cachedProposition != null && await _estCacheValide()) {
return Right(cachedProposition.toEntity());
}
// Si pas en cache ou cache expiré, aller chercher sur le serveur
if (await networkInfo.isConnected) {
final result = await remoteDataSource.obtenirPropositionAide(id);
// Mettre en cache le résultat
await localDataSource.cacherPropositionAide(result);
return Right(result.toEntity());
} else {
// Si pas de connexion, utiliser le cache même s'il est expiré
if (cachedProposition != null) {
return Right(cachedProposition.toEntity());
}
return Left(NetworkFailure('Aucune connexion internet et aucune donnée en cache'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on NotFoundException catch (e) {
return Left(NotFoundFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, PropositionAide>> changerStatutProposition({
required String propositionId,
required bool activer,
}) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.changerStatutProposition(
propositionId: propositionId,
activer: activer,
);
// Mettre à jour le cache
await localDataSource.cacherPropositionAide(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, List<PropositionAide>>> rechercherPropositions({
String? organisationId,
TypeAide? typeAide,
String? proposantId,
bool? actives,
int page = 0,
int taille = 20,
}) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.rechercherPropositions(
organisationId: organisationId,
typeAide: typeAide?.name,
proposantId: proposantId,
actives: actives,
page: page,
taille: taille,
);
// Mettre en cache les résultats
for (final proposition in result) {
await localDataSource.cacherPropositionAide(proposition);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : rechercher dans le cache local
final cachedPropositions = await localDataSource.obtenirPropositionsAideCachees();
var filteredPropositions = cachedPropositions.where((proposition) {
if (organisationId != null && proposition.organisationId != organisationId) return false;
if (typeAide != null && proposition.typeAide != typeAide) return false;
if (proposantId != null && proposition.proposantId != proposantId) return false;
if (actives != null && proposition.isActiveEtDisponible != actives) return false;
return true;
}).toList();
// Pagination locale
final startIndex = page * taille;
final endIndex = (startIndex + taille).clamp(0, filteredPropositions.length);
if (startIndex < filteredPropositions.length) {
filteredPropositions = filteredPropositions.sublist(startIndex, endIndex);
} else {
filteredPropositions = [];
}
return Right(filteredPropositions.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, List<PropositionAide>>> obtenirPropositionsActives(TypeAide typeAide) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.obtenirPropositionsActives(typeAide.name);
// Mettre en cache les résultats
for (final proposition in result) {
await localDataSource.cacherPropositionAide(proposition);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : filtrer le cache local
final cachedPropositions = await localDataSource.obtenirPropositionsAideCachees();
final propositionsActives = cachedPropositions
.where((proposition) => proposition.typeAide == typeAide && proposition.isActiveEtDisponible)
.toList();
return Right(propositionsActives.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, List<PropositionAide>>> obtenirMeilleuresPropositions(int limite) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.obtenirMeilleuresPropositions(limite);
// Mettre en cache les résultats
for (final proposition in result) {
await localDataSource.cacherPropositionAide(proposition);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : trier le cache local par note moyenne
final cachedPropositions = await localDataSource.obtenirPropositionsAideCachees();
cachedPropositions.sort((a, b) {
final noteA = a.noteMoyenne ?? 0.0;
final noteB = b.noteMoyenne ?? 0.0;
return noteB.compareTo(noteA);
});
final meilleuresPropositions = cachedPropositions.take(limite).toList();
return Right(meilleuresPropositions.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
@override
Future<Either<Failure, List<PropositionAide>>> obtenirMesPropositions(String utilisateurId) async {
try {
if (await networkInfo.isConnected) {
final result = await remoteDataSource.obtenirMesPropositions(utilisateurId);
// Mettre en cache les résultats
for (final proposition in result) {
await localDataSource.cacherPropositionAide(proposition);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : filtrer le cache local
final cachedPropositions = await localDataSource.obtenirPropositionsAideCachees();
final mesPropositions = cachedPropositions
.where((proposition) => proposition.proposantId == utilisateurId)
.toList();
return Right(mesPropositions.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
// Méthodes utilitaires privées
Future<bool> _estCacheValide() async {
try {
final localDataSourceImpl = localDataSource as SolidariteLocalDataSourceImpl;
return await localDataSourceImpl.estCacheDemandesValide() &&
await localDataSourceImpl.estCachePropositionsValide();
} catch (e) {
return false;
}
}
}

View File

@@ -0,0 +1,338 @@
// Partie 2 de l'implémentation du repository de solidarité
// Cette partie contient les méthodes pour le matching, les évaluations et les statistiques
import 'package:dartz/dartz.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/error/exceptions.dart';
import '../../domain/entities/demande_aide.dart';
import '../../domain/entities/proposition_aide.dart';
import '../../domain/entities/evaluation_aide.dart';
import '../datasources/solidarite_remote_data_source.dart';
import '../datasources/solidarite_local_data_source.dart';
import '../models/demande_aide_model.dart';
import '../models/proposition_aide_model.dart';
import '../models/evaluation_aide_model.dart';
/// Extension de l'implémentation du repository de solidarité
/// Cette partie sera intégrée dans la classe principale
mixin SolidariteRepositoryImplPart2 {
SolidariteRemoteDataSource get remoteDataSource;
SolidariteLocalDataSource get localDataSource;
bool Function() get isConnected;
// Matching
Future<Either<Failure, List<PropositionAide>>> trouverPropositionsCompatibles(String demandeId) async {
try {
if (await isConnected()) {
final result = await remoteDataSource.trouverPropositionsCompatibles(demandeId);
// Mettre en cache les résultats
for (final proposition in result) {
await localDataSource.cacherPropositionAide(proposition);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, List<DemandeAide>>> trouverDemandesCompatibles(String propositionId) async {
try {
if (await isConnected()) {
final result = await remoteDataSource.trouverDemandesCompatibles(propositionId);
// Mettre en cache les résultats
for (final demande in result) {
await localDataSource.cacherDemandeAide(demande);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, List<PropositionAide>>> rechercherProposantsFinanciers(String demandeId) async {
try {
if (await isConnected()) {
final result = await remoteDataSource.rechercherProposantsFinanciers(demandeId);
// Mettre en cache les résultats
for (final proposition in result) {
await localDataSource.cacherPropositionAide(proposition);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
// Évaluations
Future<Either<Failure, EvaluationAide>> creerEvaluation(EvaluationAide evaluation) async {
try {
if (await isConnected()) {
final evaluationModel = EvaluationAideModel.fromEntity(evaluation);
final result = await remoteDataSource.creerEvaluation(evaluationModel);
// Mettre en cache le résultat
await localDataSource.cacherEvaluation(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, EvaluationAide>> mettreAJourEvaluation(EvaluationAide evaluation) async {
try {
if (await isConnected()) {
final evaluationModel = EvaluationAideModel.fromEntity(evaluation);
final result = await remoteDataSource.mettreAJourEvaluation(evaluationModel);
// Mettre à jour le cache
await localDataSource.cacherEvaluation(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, EvaluationAide>> obtenirEvaluation(String id) async {
try {
// Essayer d'abord le cache local
final cachedEvaluation = await localDataSource.obtenirEvaluationCachee(id);
if (cachedEvaluation != null && await _estCacheEvaluationsValide()) {
return Right(cachedEvaluation.toEntity());
}
// Si pas en cache ou cache expiré, aller chercher sur le serveur
if (await isConnected()) {
final result = await remoteDataSource.obtenirEvaluation(id);
// Mettre en cache le résultat
await localDataSource.cacherEvaluation(result);
return Right(result.toEntity());
} else {
// Si pas de connexion, utiliser le cache même s'il est expiré
if (cachedEvaluation != null) {
return Right(cachedEvaluation.toEntity());
}
return Left(NetworkFailure('Aucune connexion internet et aucune donnée en cache'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on NotFoundException catch (e) {
return Left(NotFoundFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, List<EvaluationAide>>> obtenirEvaluationsDemande(String demandeId) async {
try {
if (await isConnected()) {
final result = await remoteDataSource.obtenirEvaluationsDemande(demandeId);
// Mettre en cache les résultats
for (final evaluation in result) {
await localDataSource.cacherEvaluation(evaluation);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : filtrer le cache local
final cachedEvaluations = await localDataSource.obtenirEvaluationsCachees();
final evaluationsDemande = cachedEvaluations
.where((evaluation) => evaluation.demandeId == demandeId)
.toList();
return Right(evaluationsDemande.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, List<EvaluationAide>>> obtenirEvaluationsProposition(String propositionId) async {
try {
if (await isConnected()) {
final result = await remoteDataSource.obtenirEvaluationsProposition(propositionId);
// Mettre en cache les résultats
for (final evaluation in result) {
await localDataSource.cacherEvaluation(evaluation);
}
return Right(result.map((model) => model.toEntity()).toList());
} else {
// Mode hors ligne : filtrer le cache local
final cachedEvaluations = await localDataSource.obtenirEvaluationsCachees();
final evaluationsProposition = cachedEvaluations
.where((evaluation) => evaluation.propositionId == propositionId)
.toList();
return Right(evaluationsProposition.map((model) => model.toEntity()).toList());
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, EvaluationAide>> signalerEvaluation({
required String evaluationId,
required String motif,
}) async {
try {
if (await isConnected()) {
final result = await remoteDataSource.signalerEvaluation(
evaluationId: evaluationId,
motif: motif,
);
// Mettre à jour le cache
await localDataSource.cacherEvaluation(result);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, StatistiquesEvaluation>> calculerMoyenneDemande(String demandeId) async {
try {
if (await isConnected()) {
final result = await remoteDataSource.calculerMoyenneDemande(demandeId);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
Future<Either<Failure, StatistiquesEvaluation>> calculerMoyenneProposition(String propositionId) async {
try {
if (await isConnected()) {
final result = await remoteDataSource.calculerMoyenneProposition(propositionId);
return Right(result.toEntity());
} else {
return Left(NetworkFailure('Aucune connexion internet disponible'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
// Statistiques
Future<Either<Failure, Map<String, dynamic>>> obtenirStatistiquesSolidarite(String organisationId) async {
try {
// Essayer d'abord le cache local
final cachedStats = await localDataSource.obtenirStatistiquesCachees(organisationId);
if (cachedStats != null && await _estCacheStatistiquesValide(organisationId)) {
return Right(cachedStats);
}
// Si pas en cache ou cache expiré, aller chercher sur le serveur
if (await isConnected()) {
final result = await remoteDataSource.obtenirStatistiquesSolidarite(organisationId);
// Mettre en cache le résultat
await localDataSource.cacherStatistiques(organisationId, result);
return Right(result);
} else {
// Si pas de connexion, utiliser le cache même s'il est expiré
if (cachedStats != null) {
return Right(cachedStats);
}
return Left(NetworkFailure('Aucune connexion internet et aucune donnée en cache'));
}
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} on CacheException catch (e) {
return Left(CacheFailure(e.message));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
// Méthodes utilitaires privées
Future<bool> _estCacheEvaluationsValide() async {
try {
final localDataSourceImpl = localDataSource as SolidariteLocalDataSourceImpl;
return await localDataSourceImpl.estCacheEvaluationsValide();
} catch (e) {
return false;
}
}
Future<bool> _estCacheStatistiquesValide(String organisationId) async {
try {
final localDataSourceImpl = localDataSource as SolidariteLocalDataSourceImpl;
return await localDataSourceImpl.estCacheStatistiquesValide(organisationId);
} catch (e) {
return false;
}
}
}