You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
8.3 KiB
258 lines
8.3 KiB
import 'package:get/get.dart';
|
|
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
|
|
|
class PermissionCacheService extends GetxController {
|
|
static final PermissionCacheService instance = PermissionCacheService._init();
|
|
PermissionCacheService._init();
|
|
|
|
// Cache en mémoire optimisé
|
|
final Map<String, Map<String, bool>> _permissionCache = {};
|
|
final Map<String, List<Map<String, dynamic>>> _menuCache = {};
|
|
bool _isLoaded = false;
|
|
String _currentUsername = '';
|
|
|
|
/// ✅ OPTIMISÉ: Une seule requête complexe pour charger tout
|
|
Future<void> loadUserPermissions(String username) async {
|
|
if (_isLoaded && _currentUsername == username && _permissionCache.containsKey(username)) {
|
|
print("📋 Permissions déjà en cache pour: $username");
|
|
return;
|
|
}
|
|
|
|
print("🔄 Chargement OPTIMISÉ des permissions pour: $username");
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
try {
|
|
final db = AppDatabase.instance;
|
|
|
|
// 🚀 UNE SEULE REQUÊTE pour tout récupérer
|
|
final userPermissions = await _getUserPermissionsOptimized(db, username);
|
|
|
|
// Organiser les données
|
|
Map<String, bool> permissions = {};
|
|
Set<Map<String, dynamic>> accessibleMenus = {};
|
|
|
|
for (var row in userPermissions) {
|
|
final menuId = row['menu_id'] as int;
|
|
final menuName = row['menu_name'] as String;
|
|
final menuRoute = row['menu_route'] as String;
|
|
final permissionName = row['permission_name'] as String;
|
|
|
|
// Ajouter la permission
|
|
final key = "${permissionName}_$menuRoute";
|
|
permissions[key] = true;
|
|
|
|
// Ajouter le menu aux accessibles
|
|
accessibleMenus.add({
|
|
'id': menuId,
|
|
'name': menuName,
|
|
'route': menuRoute,
|
|
});
|
|
}
|
|
|
|
// Mettre en cache
|
|
_permissionCache[username] = permissions;
|
|
_menuCache[username] = accessibleMenus.toList();
|
|
_currentUsername = username;
|
|
_isLoaded = true;
|
|
|
|
stopwatch.stop();
|
|
print("✅ Permissions chargées en ${stopwatch.elapsedMilliseconds}ms");
|
|
print(" - ${permissions.length} permissions");
|
|
print(" - ${accessibleMenus.length} menus accessibles");
|
|
|
|
} catch (e) {
|
|
stopwatch.stop();
|
|
print("❌ Erreur après ${stopwatch.elapsedMilliseconds}ms: $e");
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// 🚀 NOUVELLE MÉTHODE: Une seule requête optimisée
|
|
Future<List<Map<String, dynamic>>> _getUserPermissionsOptimized(
|
|
AppDatabase db, String username) async {
|
|
|
|
final connection = await db.database;
|
|
|
|
final result = await connection.query('''
|
|
SELECT DISTINCT
|
|
m.id as menu_id,
|
|
m.name as menu_name,
|
|
m.route as menu_route,
|
|
p.name as permission_name
|
|
FROM users u
|
|
INNER JOIN roles r ON u.role_id = r.id
|
|
INNER JOIN role_menu_permissions rmp ON r.id = rmp.role_id
|
|
INNER JOIN menu m ON rmp.menu_id = m.id
|
|
INNER JOIN permissions p ON rmp.permission_id = p.id
|
|
WHERE u.username = ?
|
|
ORDER BY m.name, p.name
|
|
''', [username]);
|
|
|
|
return result.map((row) => row.fields).toList();
|
|
}
|
|
|
|
/// ✅ Vérification rapide depuis le cache
|
|
bool hasPermission(String username, String permissionName, String menuRoute) {
|
|
final userPermissions = _permissionCache[username];
|
|
if (userPermissions == null) {
|
|
print("⚠️ Cache non initialisé pour: $username");
|
|
return false;
|
|
}
|
|
|
|
final key = "${permissionName}_$menuRoute";
|
|
return userPermissions[key] ?? false;
|
|
}
|
|
|
|
/// ✅ Récupération rapide des menus
|
|
List<Map<String, dynamic>> getUserMenus(String username) {
|
|
return _menuCache[username] ?? [];
|
|
}
|
|
|
|
/// ✅ Vérification d'accès menu
|
|
bool hasMenuAccess(String username, String menuRoute) {
|
|
final userMenus = _menuCache[username] ?? [];
|
|
return userMenus.any((menu) => menu['route'] == menuRoute);
|
|
}
|
|
|
|
/// ✅ Préchargement asynchrone en arrière-plan
|
|
Future<void> preloadUserDataAsync(String username) async {
|
|
// Lancer en arrière-plan sans bloquer l'UI
|
|
unawaited(_preloadInBackground(username));
|
|
}
|
|
|
|
Future<void> _preloadInBackground(String username) async {
|
|
try {
|
|
print("🔄 Préchargement en arrière-plan pour: $username");
|
|
await loadUserPermissions(username);
|
|
print("✅ Préchargement terminé");
|
|
} catch (e) {
|
|
print("⚠️ Erreur préchargement: $e");
|
|
}
|
|
}
|
|
|
|
/// ✅ Préchargement synchrone (pour la connexion)
|
|
Future<void> preloadUserData(String username) async {
|
|
try {
|
|
print("🔄 Préchargement synchrone pour: $username");
|
|
await loadUserPermissions(username);
|
|
print("✅ Données préchargées avec succès");
|
|
} catch (e) {
|
|
print("❌ Erreur lors du préchargement: $e");
|
|
// Ne pas bloquer la connexion
|
|
}
|
|
}
|
|
|
|
/// ✅ Vider le cache
|
|
void clearAllCache() {
|
|
_permissionCache.clear();
|
|
_menuCache.clear();
|
|
_isLoaded = false;
|
|
_currentUsername = '';
|
|
print("🗑️ Cache vidé complètement");
|
|
}
|
|
|
|
/// ✅ Rechargement forcé
|
|
Future<void> refreshUserPermissions(String username) async {
|
|
_permissionCache.remove(username);
|
|
_menuCache.remove(username);
|
|
_isLoaded = false;
|
|
|
|
await loadUserPermissions(username);
|
|
print("🔄 Permissions rechargées pour: $username");
|
|
}
|
|
|
|
/// ✅ Status du cache
|
|
bool get isLoaded => _isLoaded && _currentUsername.isNotEmpty;
|
|
String get currentCachedUser => _currentUsername;
|
|
|
|
/// ✅ Statistiques
|
|
Map<String, dynamic> getCacheStats() {
|
|
return {
|
|
'is_loaded': _isLoaded,
|
|
'current_user': _currentUsername,
|
|
'users_cached': _permissionCache.length,
|
|
'total_permissions': _permissionCache.values
|
|
.map((perms) => perms.length)
|
|
.fold(0, (a, b) => a + b),
|
|
'total_menus': _menuCache.values
|
|
.map((menus) => menus.length)
|
|
.fold(0, (a, b) => a + b),
|
|
};
|
|
}
|
|
|
|
/// ✅ Debug amélioré
|
|
void debugPrintCache() {
|
|
print("=== DEBUG CACHE OPTIMISÉ ===");
|
|
print("Chargé: $_isLoaded");
|
|
print("Utilisateur actuel: $_currentUsername");
|
|
print("Utilisateurs en cache: ${_permissionCache.keys.toList()}");
|
|
|
|
for (var username in _permissionCache.keys) {
|
|
final permissions = _permissionCache[username]!;
|
|
final menus = _menuCache[username] ?? [];
|
|
print("$username: ${permissions.length} permissions, ${menus.length} menus");
|
|
|
|
// Détail des menus pour debug
|
|
for (var menu in menus.take(3)) {
|
|
print(" → ${menu['name']} (${menu['route']})");
|
|
}
|
|
}
|
|
print("============================");
|
|
}
|
|
|
|
/// ✅ NOUVEAU: Validation de l'intégrité du cache
|
|
Future<bool> validateCacheIntegrity(String username) async {
|
|
if (!_permissionCache.containsKey(username)) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
final db = AppDatabase.instance;
|
|
final connection = await db.database;
|
|
|
|
// Vérification rapide: compter les permissions de l'utilisateur
|
|
final result = await connection.query('''
|
|
SELECT COUNT(DISTINCT CONCAT(p.name, '_', m.route)) as permission_count
|
|
FROM users u
|
|
INNER JOIN roles r ON u.role_id = r.id
|
|
INNER JOIN role_menu_permissions rmp ON r.id = rmp.role_id
|
|
INNER JOIN menu m ON rmp.menu_id = m.id
|
|
INNER JOIN permissions p ON rmp.permission_id = p.id
|
|
WHERE u.username = ?
|
|
''', [username]);
|
|
|
|
final dbCount = result.first['permission_count'] as int;
|
|
final cacheCount = _permissionCache[username]!.length;
|
|
|
|
final isValid = dbCount == cacheCount;
|
|
if (!isValid) {
|
|
print("⚠️ Cache invalide: DB=$dbCount, Cache=$cacheCount");
|
|
}
|
|
|
|
return isValid;
|
|
} catch (e) {
|
|
print("❌ Erreur validation cache: $e");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// ✅ NOUVEAU: Rechargement intelligent
|
|
Future<void> smartRefresh(String username) async {
|
|
final isValid = await validateCacheIntegrity(username);
|
|
|
|
if (!isValid) {
|
|
print("🔄 Cache invalide, rechargement nécessaire");
|
|
await refreshUserPermissions(username);
|
|
} else {
|
|
print("✅ Cache valide, pas de rechargement nécessaire");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// ✅ Extension pour éviter l'import de dart:async
|
|
void unawaited(Future future) {
|
|
// Ignorer le warning sur le Future non attendu
|
|
future.catchError((error) {
|
|
print("Erreur tâche en arrière-plan: $error");
|
|
});
|
|
}
|