Refactoring - Version OK
This commit is contained in:
400
unionflow-mobile-apps/lib/features/dashboard/data/cache/dashboard_cache_manager.dart
vendored
Normal file
400
unionflow-mobile-apps/lib/features/dashboard/data/cache/dashboard_cache_manager.dart
vendored
Normal file
@@ -0,0 +1,400 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/dashboard_stats_model.dart';
|
||||
import '../../config/dashboard_config.dart';
|
||||
|
||||
/// Gestionnaire de cache avancé pour le Dashboard
|
||||
class DashboardCacheManager {
|
||||
static const String _keyPrefix = 'dashboard_cache_';
|
||||
static const String _keyDashboardData = '${_keyPrefix}data';
|
||||
static const String _keyDashboardStats = '${_keyPrefix}stats';
|
||||
static const String _keyRecentActivities = '${_keyPrefix}activities';
|
||||
static const String _keyUpcomingEvents = '${_keyPrefix}events';
|
||||
static const String _keyLastUpdate = '${_keyPrefix}last_update';
|
||||
static const String _keyUserPreferences = '${_keyPrefix}user_prefs';
|
||||
|
||||
SharedPreferences? _prefs;
|
||||
final Map<String, dynamic> _memoryCache = {};
|
||||
final Map<String, DateTime> _cacheTimestamps = {};
|
||||
Timer? _cleanupTimer;
|
||||
|
||||
/// Initialise le gestionnaire de cache
|
||||
Future<void> initialize() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
_startCleanupTimer();
|
||||
await _loadMemoryCache();
|
||||
}
|
||||
|
||||
/// Démarre le timer de nettoyage automatique
|
||||
void _startCleanupTimer() {
|
||||
_cleanupTimer = Timer.periodic(
|
||||
const Duration(minutes: 30),
|
||||
(_) => _cleanupExpiredCache(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Charge le cache en mémoire au démarrage
|
||||
Future<void> _loadMemoryCache() async {
|
||||
if (_prefs == null) return;
|
||||
|
||||
final keys = _prefs!.getKeys().where((key) => key.startsWith(_keyPrefix));
|
||||
|
||||
for (final key in keys) {
|
||||
final value = _prefs!.getString(key);
|
||||
if (value != null) {
|
||||
try {
|
||||
final data = jsonDecode(value);
|
||||
_memoryCache[key] = data;
|
||||
|
||||
// Charger le timestamp si disponible
|
||||
final timestampKey = '${key}_timestamp';
|
||||
final timestamp = _prefs!.getInt(timestampKey);
|
||||
if (timestamp != null) {
|
||||
_cacheTimestamps[key] = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||
}
|
||||
} catch (e) {
|
||||
// Supprimer les données corrompues
|
||||
await _prefs!.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sauvegarde les données complètes du dashboard
|
||||
Future<void> cacheDashboardData(
|
||||
DashboardDataModel data,
|
||||
String organizationId,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyDashboardData}_${organizationId}_$userId';
|
||||
await _cacheData(key, data.toJson());
|
||||
}
|
||||
|
||||
/// Récupère les données complètes du dashboard
|
||||
Future<DashboardDataModel?> getCachedDashboardData(
|
||||
String organizationId,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyDashboardData}_${organizationId}_$userId';
|
||||
final data = await _getCachedData(key);
|
||||
|
||||
if (data != null) {
|
||||
try {
|
||||
return DashboardDataModel.fromJson(data);
|
||||
} catch (e) {
|
||||
// Supprimer les données corrompues
|
||||
await _removeCachedData(key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Sauvegarde les statistiques du dashboard
|
||||
Future<void> cacheDashboardStats(
|
||||
DashboardStatsModel stats,
|
||||
String organizationId,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyDashboardStats}_${organizationId}_$userId';
|
||||
await _cacheData(key, stats.toJson());
|
||||
}
|
||||
|
||||
/// Récupère les statistiques du dashboard
|
||||
Future<DashboardStatsModel?> getCachedDashboardStats(
|
||||
String organizationId,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyDashboardStats}_${organizationId}_$userId';
|
||||
final data = await _getCachedData(key);
|
||||
|
||||
if (data != null) {
|
||||
try {
|
||||
return DashboardStatsModel.fromJson(data);
|
||||
} catch (e) {
|
||||
await _removeCachedData(key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Sauvegarde les activités récentes
|
||||
Future<void> cacheRecentActivities(
|
||||
List<RecentActivityModel> activities,
|
||||
String organizationId,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyRecentActivities}_${organizationId}_$userId';
|
||||
final data = activities.map((activity) => activity.toJson()).toList();
|
||||
await _cacheData(key, data);
|
||||
}
|
||||
|
||||
/// Récupère les activités récentes
|
||||
Future<List<RecentActivityModel>?> getCachedRecentActivities(
|
||||
String organizationId,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyRecentActivities}_${organizationId}_$userId';
|
||||
final data = await _getCachedData(key);
|
||||
|
||||
if (data != null && data is List) {
|
||||
try {
|
||||
return data
|
||||
.map((item) => RecentActivityModel.fromJson(item))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
await _removeCachedData(key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Sauvegarde les événements à venir
|
||||
Future<void> cacheUpcomingEvents(
|
||||
List<UpcomingEventModel> events,
|
||||
String organizationId,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyUpcomingEvents}_${organizationId}_$userId';
|
||||
final data = events.map((event) => event.toJson()).toList();
|
||||
await _cacheData(key, data);
|
||||
}
|
||||
|
||||
/// Récupère les événements à venir
|
||||
Future<List<UpcomingEventModel>?> getCachedUpcomingEvents(
|
||||
String organizationId,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyUpcomingEvents}_${organizationId}_$userId';
|
||||
final data = await _getCachedData(key);
|
||||
|
||||
if (data != null && data is List) {
|
||||
try {
|
||||
return data
|
||||
.map((item) => UpcomingEventModel.fromJson(item))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
await _removeCachedData(key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Sauvegarde les préférences utilisateur
|
||||
Future<void> cacheUserPreferences(
|
||||
Map<String, dynamic> preferences,
|
||||
String userId,
|
||||
) async {
|
||||
final key = '${_keyUserPreferences}_$userId';
|
||||
await _cacheData(key, preferences);
|
||||
}
|
||||
|
||||
/// Récupère les préférences utilisateur
|
||||
Future<Map<String, dynamic>?> getCachedUserPreferences(String userId) async {
|
||||
final key = '${_keyUserPreferences}_$userId';
|
||||
final data = await _getCachedData(key);
|
||||
|
||||
if (data != null && data is Map<String, dynamic>) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Méthode générique pour sauvegarder des données
|
||||
Future<void> _cacheData(String key, dynamic data) async {
|
||||
if (_prefs == null) return;
|
||||
|
||||
try {
|
||||
final jsonString = jsonEncode(data);
|
||||
await _prefs!.setString(key, jsonString);
|
||||
|
||||
// Sauvegarder le timestamp
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
await _prefs!.setInt('${key}_timestamp', timestamp);
|
||||
|
||||
// Mettre à jour le cache mémoire
|
||||
_memoryCache[key] = data;
|
||||
_cacheTimestamps[key] = DateTime.now();
|
||||
|
||||
} catch (e) {
|
||||
// Erreur de sérialisation, ignorer
|
||||
}
|
||||
}
|
||||
|
||||
/// Méthode générique pour récupérer des données
|
||||
Future<dynamic> _getCachedData(String key) async {
|
||||
// Vérifier d'abord le cache mémoire
|
||||
if (_memoryCache.containsKey(key)) {
|
||||
if (_isCacheValid(key)) {
|
||||
return _memoryCache[key];
|
||||
} else {
|
||||
// Cache expiré, le supprimer
|
||||
await _removeCachedData(key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier le cache persistant
|
||||
if (_prefs == null) return null;
|
||||
|
||||
final jsonString = _prefs!.getString(key);
|
||||
if (jsonString != null) {
|
||||
try {
|
||||
final data = jsonDecode(jsonString);
|
||||
|
||||
// Vérifier la validité du cache
|
||||
if (_isCacheValid(key)) {
|
||||
// Charger en mémoire pour les prochains accès
|
||||
_memoryCache[key] = data;
|
||||
return data;
|
||||
} else {
|
||||
// Cache expiré, le supprimer
|
||||
await _removeCachedData(key);
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
// Données corrompues, les supprimer
|
||||
await _removeCachedData(key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Vérifie si le cache est encore valide
|
||||
bool _isCacheValid(String key) {
|
||||
final timestamp = _cacheTimestamps[key];
|
||||
if (timestamp == null) {
|
||||
// Essayer de récupérer le timestamp depuis SharedPreferences
|
||||
final timestampMs = _prefs?.getInt('${key}_timestamp');
|
||||
if (timestampMs != null) {
|
||||
final cacheTime = DateTime.fromMillisecondsSinceEpoch(timestampMs);
|
||||
_cacheTimestamps[key] = cacheTime;
|
||||
return DateTime.now().difference(cacheTime) < DashboardConfig.cacheExpiration;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return DateTime.now().difference(timestamp) < DashboardConfig.cacheExpiration;
|
||||
}
|
||||
|
||||
/// Supprime des données du cache
|
||||
Future<void> _removeCachedData(String key) async {
|
||||
_memoryCache.remove(key);
|
||||
_cacheTimestamps.remove(key);
|
||||
|
||||
if (_prefs != null) {
|
||||
await _prefs!.remove(key);
|
||||
await _prefs!.remove('${key}_timestamp');
|
||||
}
|
||||
}
|
||||
|
||||
/// Nettoie le cache expiré
|
||||
Future<void> _cleanupExpiredCache() async {
|
||||
final keysToRemove = <String>[];
|
||||
|
||||
for (final key in _cacheTimestamps.keys) {
|
||||
if (!_isCacheValid(key)) {
|
||||
keysToRemove.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (final key in keysToRemove) {
|
||||
await _removeCachedData(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Vide tout le cache
|
||||
Future<void> clearCache() async {
|
||||
_memoryCache.clear();
|
||||
_cacheTimestamps.clear();
|
||||
|
||||
if (_prefs != null) {
|
||||
final keys = _prefs!.getKeys().where((key) => key.startsWith(_keyPrefix));
|
||||
for (final key in keys) {
|
||||
await _prefs!.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Vide le cache pour un utilisateur spécifique
|
||||
Future<void> clearUserCache(String organizationId, String userId) async {
|
||||
final userKeys = [
|
||||
'${_keyDashboardData}_${organizationId}_$userId',
|
||||
'${_keyDashboardStats}_${organizationId}_$userId',
|
||||
'${_keyRecentActivities}_${organizationId}_$userId',
|
||||
'${_keyUpcomingEvents}_${organizationId}_$userId',
|
||||
'${_keyUserPreferences}_$userId',
|
||||
];
|
||||
|
||||
for (final key in userKeys) {
|
||||
await _removeCachedData(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtient les statistiques du cache
|
||||
Map<String, dynamic> getCacheStats() {
|
||||
final totalKeys = _memoryCache.length;
|
||||
final validKeys = _cacheTimestamps.keys.where(_isCacheValid).length;
|
||||
final expiredKeys = totalKeys - validKeys;
|
||||
|
||||
return {
|
||||
'totalKeys': totalKeys,
|
||||
'validKeys': validKeys,
|
||||
'expiredKeys': expiredKeys,
|
||||
'memoryUsage': _calculateMemoryUsage(),
|
||||
'oldestEntry': _getOldestEntryAge(),
|
||||
'newestEntry': _getNewestEntryAge(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Calcule l'utilisation mémoire approximative
|
||||
int _calculateMemoryUsage() {
|
||||
int totalSize = 0;
|
||||
for (final data in _memoryCache.values) {
|
||||
try {
|
||||
totalSize += jsonEncode(data).length;
|
||||
} catch (e) {
|
||||
// Ignorer les erreurs de sérialisation
|
||||
}
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
/// Obtient l'âge de l'entrée la plus ancienne
|
||||
Duration? _getOldestEntryAge() {
|
||||
if (_cacheTimestamps.isEmpty) return null;
|
||||
|
||||
final oldestTimestamp = _cacheTimestamps.values
|
||||
.reduce((a, b) => a.isBefore(b) ? a : b);
|
||||
|
||||
return DateTime.now().difference(oldestTimestamp);
|
||||
}
|
||||
|
||||
/// Obtient l'âge de l'entrée la plus récente
|
||||
Duration? _getNewestEntryAge() {
|
||||
if (_cacheTimestamps.isEmpty) return null;
|
||||
|
||||
final newestTimestamp = _cacheTimestamps.values
|
||||
.reduce((a, b) => a.isAfter(b) ? a : b);
|
||||
|
||||
return DateTime.now().difference(newestTimestamp);
|
||||
}
|
||||
|
||||
/// Libère les ressources
|
||||
void dispose() {
|
||||
_cleanupTimer?.cancel();
|
||||
_memoryCache.clear();
|
||||
_cacheTimestamps.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user