Files
unionflow-server-impl-quarkus/unionflow-mobile-apps/lib/core/services/cache_service.dart
2025-09-15 20:15:34 +00:00

250 lines
8.2 KiB
Dart

import 'dart:convert';
import 'package:injectable/injectable.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/cotisation_model.dart';
import '../models/cotisation_statistics_model.dart';
import '../models/payment_model.dart';
/// Service de gestion du cache local
/// Permet de stocker et récupérer des données en mode hors-ligne
@LazySingleton()
class CacheService {
static const String _cotisationsCacheKey = 'cotisations_cache';
static const String _cotisationsStatsCacheKey = 'cotisations_stats_cache';
static const String _paymentsCacheKey = 'payments_cache';
static const String _lastSyncKey = 'last_sync_timestamp';
static const Duration _cacheValidityDuration = Duration(minutes: 30);
final SharedPreferences _prefs;
CacheService(this._prefs);
/// Sauvegarde une liste de cotisations dans le cache
Future<void> saveCotisations(List<CotisationModel> cotisations, {String? key}) async {
final cacheKey = key ?? _cotisationsCacheKey;
final jsonList = cotisations.map((c) => c.toJson()).toList();
final jsonString = jsonEncode({
'data': jsonList,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
await _prefs.setString(cacheKey, jsonString);
}
/// Récupère une liste de cotisations depuis le cache
Future<List<CotisationModel>?> getCotisations({String? key}) async {
final cacheKey = key ?? _cotisationsCacheKey;
final jsonString = _prefs.getString(cacheKey);
if (jsonString == null) return null;
try {
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
final timestamp = DateTime.fromMillisecondsSinceEpoch(jsonData['timestamp'] as int);
// Vérifier si le cache est encore valide
if (DateTime.now().difference(timestamp) > _cacheValidityDuration) {
await clearCotisations(key: key);
return null;
}
final jsonList = jsonData['data'] as List<dynamic>;
return jsonList.map((json) => CotisationModel.fromJson(json as Map<String, dynamic>)).toList();
} catch (e) {
// En cas d'erreur, nettoyer le cache corrompu
await clearCotisations(key: key);
return null;
}
}
/// Sauvegarde les statistiques des cotisations
Future<void> saveCotisationsStats(CotisationStatisticsModel stats) async {
final jsonString = jsonEncode({
'data': stats.toJson(),
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
await _prefs.setString(_cotisationsStatsCacheKey, jsonString);
}
/// Récupère les statistiques des cotisations depuis le cache
Future<CotisationStatisticsModel?> getCotisationsStats() async {
final jsonString = _prefs.getString(_cotisationsStatsCacheKey);
if (jsonString == null) return null;
try {
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
final timestamp = DateTime.fromMillisecondsSinceEpoch(jsonData['timestamp'] as int);
// Vérifier si le cache est encore valide
if (DateTime.now().difference(timestamp) > _cacheValidityDuration) {
await clearCotisationsStats();
return null;
}
return CotisationStatisticsModel.fromJson(jsonData['data'] as Map<String, dynamic>);
} catch (e) {
await clearCotisationsStats();
return null;
}
}
/// Sauvegarde une liste de paiements dans le cache
Future<void> savePayments(List<PaymentModel> payments) async {
final jsonList = payments.map((p) => p.toJson()).toList();
final jsonString = jsonEncode({
'data': jsonList,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
await _prefs.setString(_paymentsCacheKey, jsonString);
}
/// Récupère une liste de paiements depuis le cache
Future<List<PaymentModel>?> getPayments() async {
final jsonString = _prefs.getString(_paymentsCacheKey);
if (jsonString == null) return null;
try {
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
final timestamp = DateTime.fromMillisecondsSinceEpoch(jsonData['timestamp'] as int);
// Vérifier si le cache est encore valide
if (DateTime.now().difference(timestamp) > _cacheValidityDuration) {
await clearPayments();
return null;
}
final jsonList = jsonData['data'] as List<dynamic>;
return jsonList.map((json) => PaymentModel.fromJson(json as Map<String, dynamic>)).toList();
} catch (e) {
await clearPayments();
return null;
}
}
/// Sauvegarde une cotisation individuelle dans le cache
Future<void> saveCotisation(CotisationModel cotisation) async {
final key = 'cotisation_${cotisation.id}';
final jsonString = jsonEncode({
'data': cotisation.toJson(),
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
await _prefs.setString(key, jsonString);
}
/// Récupère une cotisation individuelle depuis le cache
Future<CotisationModel?> getCotisation(String id) async {
final key = 'cotisation_$id';
final jsonString = _prefs.getString(key);
if (jsonString == null) return null;
try {
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
final timestamp = DateTime.fromMillisecondsSinceEpoch(jsonData['timestamp'] as int);
// Vérifier si le cache est encore valide
if (DateTime.now().difference(timestamp) > _cacheValidityDuration) {
await clearCotisation(id);
return null;
}
return CotisationModel.fromJson(jsonData['data'] as Map<String, dynamic>);
} catch (e) {
await clearCotisation(id);
return null;
}
}
/// Met à jour le timestamp de la dernière synchronisation
Future<void> updateLastSyncTimestamp() async {
await _prefs.setInt(_lastSyncKey, DateTime.now().millisecondsSinceEpoch);
}
/// Récupère le timestamp de la dernière synchronisation
DateTime? getLastSyncTimestamp() {
final timestamp = _prefs.getInt(_lastSyncKey);
return timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp) : null;
}
/// Vérifie si une synchronisation est nécessaire
bool needsSync() {
final lastSync = getLastSyncTimestamp();
if (lastSync == null) return true;
return DateTime.now().difference(lastSync) > const Duration(minutes: 15);
}
/// Nettoie le cache des cotisations
Future<void> clearCotisations({String? key}) async {
final cacheKey = key ?? _cotisationsCacheKey;
await _prefs.remove(cacheKey);
}
/// Nettoie le cache des statistiques
Future<void> clearCotisationsStats() async {
await _prefs.remove(_cotisationsStatsCacheKey);
}
/// Nettoie le cache des paiements
Future<void> clearPayments() async {
await _prefs.remove(_paymentsCacheKey);
}
/// Nettoie une cotisation individuelle du cache
Future<void> clearCotisation(String id) async {
final key = 'cotisation_$id';
await _prefs.remove(key);
}
/// Nettoie tout le cache des cotisations
Future<void> clearAllCotisationsCache() async {
final keys = _prefs.getKeys().where((key) =>
key.startsWith('cotisation') ||
key == _cotisationsStatsCacheKey ||
key == _paymentsCacheKey
).toList();
for (final key in keys) {
await _prefs.remove(key);
}
}
/// Retourne la taille du cache en octets (approximation)
int getCacheSize() {
int totalSize = 0;
final keys = _prefs.getKeys().where((key) =>
key.startsWith('cotisation') ||
key == _cotisationsStatsCacheKey ||
key == _paymentsCacheKey
);
for (final key in keys) {
final value = _prefs.getString(key);
if (value != null) {
totalSize += value.length * 2; // Approximation UTF-16
}
}
return totalSize;
}
/// Retourne des informations sur le cache
Map<String, dynamic> getCacheInfo() {
final lastSync = getLastSyncTimestamp();
return {
'lastSync': lastSync?.toIso8601String(),
'needsSync': needsSync(),
'cacheSize': getCacheSize(),
'cacheSizeFormatted': _formatBytes(getCacheSize()),
};
}
/// Formate la taille en octets en format lisible
String _formatBytes(int bytes) {
if (bytes < 1024) return '$bytes B';
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
}
}