import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart' as path_provider; import '../../../data/models/hive_inspiration_image.dart'; import '../../../data/models/hive_image_folder.dart'; import '../../../data/models/hive_image_tag.dart'; import '../../../core/utils/logger.dart'; import 'database_migration.dart'; /// Hive数据库管理类 - 负责本地数据存储和初始化 /// 提供应用程序所有数据的本地持久化存储功能 class HiveDatabase { /// 图片数据存储盒名称 - 存储所有灵感图片数据 static const String _imagesBoxName = 'inspiration_images'; /// 文件夹数据存储盒名称 - 存储所有文件夹信息 static const String _foldersBoxName = 'image_folders'; /// 标签数据存储盒名称 - 存储所有标签信息 static const String _tagsBoxName = 'image_tags'; /// 应用设置存储盒名称 - 存储应用配置和数据库版本 static const String _settingsBoxName = 'app_settings'; /// 当前数据库版本号 - 用于数据迁移管理 /// 每次数据库结构变更时,需要递增此版本号 static const int _currentVersion = DatabaseMigration.currentVersion; /// 数据库版本键名 - 在设置盒中存储版本信息的键 static const String _versionKey = DatabaseMigration.versionKey; /// 初始化Hive数据库 - 应用程序启动时调用 /// 设置存储路径、注册适配器、打开数据盒并执行数据迁移 static Future init() async { final appDocumentDir = await path_provider.getApplicationDocumentsDirectory(); Hive.init(appDocumentDir.path); _registerAdapters(); await Hive.openBox(_imagesBoxName); await Hive.openBox(_foldersBoxName); await Hive.openBox(_tagsBoxName); await Hive.openBox(_settingsBoxName); await _performMigration(); } /// 注册Hive类型适配器 - 将自定义类型注册到Hive /// 确保类型ID不冲突,每个类型有唯一的typeId static void _registerAdapters() { if (!Hive.isAdapterRegistered(0)) { Hive.registerAdapter(HiveInspirationImageAdapter()); } if (!Hive.isAdapterRegistered(1)) { Hive.registerAdapter(HiveImageFolderAdapter()); } if (!Hive.isAdapterRegistered(2)) { Hive.registerAdapter(HiveImageTagAdapter()); } } /// 执行数据库迁移 - 检查版本并执行必要的迁移操作 /// 使用专门的迁移管理类来处理复杂的迁移逻辑 static Future _performMigration() async { final settingsBox = Hive.box(_settingsBoxName); final currentDbVersion = settingsBox.get(_versionKey, defaultValue: 0) as int; if (currentDbVersion < _currentVersion) { try { Logger.info('检测到数据库版本更新: $currentDbVersion -> $_currentVersion'); // 使用专门的迁移管理类执行迁移 await DatabaseMigration.migrateFromVersion( currentDbVersion, imagesBox, foldersBox, tagsBox, settingsBox, ); Logger.info('数据库迁移完成'); } catch (e) { Logger.error('数据库迁移失败', error: e); // 如果迁移失败,尝试恢复到已知状态 await _handleMigrationFailure(currentDbVersion, e); } } else if (currentDbVersion > _currentVersion) { // 处理降级情况(通常不应该发生) Logger.warning('数据库版本高于应用版本 ($currentDbVersion > $_currentVersion)'); await settingsBox.put(_versionKey, _currentVersion); } } /// 处理迁移失败 - 当迁移过程中出现错误时的恢复逻辑 /// [fromVersion] 迁移起始版本 /// [error] 迁移过程中出现的错误 static Future _handleMigrationFailure(int fromVersion, dynamic error) async { Logger.error('处理迁移失败,尝试恢复到安全状态...', error: error); try { // 如果是最初版本(0)的迁移失败,可以尝试重新创建基础结构 if (fromVersion == 0) { await _createEmergencyData(); } // 记录错误信息到设置中,便于后续分析 final settingsBox = Hive.box(_settingsBoxName); await settingsBox.put('last_migration_error', error.toString()); await settingsBox.put('last_migration_time', DateTime.now().toIso8601String()); } catch (recoveryError) { Logger.fatal('迁移恢复失败', error: recoveryError); // 如果恢复也失败,只能抛出异常让上层处理 throw DatabaseMigrationException( fromVersion: fromVersion, toVersion: _currentVersion, message: '数据库迁移失败且无法恢复', originalError: error, ); } } /// 创建默认数据 - 初始化数据库时的默认数据创建 /// 创建系统必需的默认文件夹和标签 static Future _createDefaultData() async { final foldersBox = Hive.box(_foldersBoxName); final tagsBox = Hive.box(_tagsBoxName); // 使用迁移管理类创建默认数据 await DatabaseMigration.createDefaultFolders(foldersBox); await DatabaseMigration.createDefaultTags(tagsBox); } /// 创建紧急恢复数据 - 当迁移失败时的紧急恢复方案 /// 确保应用至少能正常运行,包含最基本的数据结构 static Future _createEmergencyData() async { Logger.warning('创建紧急恢复数据...'); try { final foldersBox = Hive.box(_foldersBoxName); final settingsBox = Hive.box(_settingsBoxName); // 确保至少有一个默认文件夹存在 if (!foldersBox.containsKey('default')) { final now = DateTime.now(); final emergencyFolder = HiveImageFolder( id: 'default', name: '默认', icon: 'folder', createdAt: now, updatedAt: now, lastUsedAt: now, ); await foldersBox.put('default', emergencyFolder); print('创建紧急默认文件夹'); } // 设置当前版本号,避免重复迁移 await settingsBox.put(_versionKey, _currentVersion); print('紧急恢复数据创建完成'); } catch (e) { print('紧急恢复数据创建失败: $e'); // 如果连紧急恢复都失败,只能抛出致命异常 throw Exception('数据库初始化失败,应用无法正常运行: $e'); } } // Getters for boxes static Box get imagesBox => Hive.box(_imagesBoxName); static Box get foldersBox => Hive.box(_foldersBoxName); static Box get tagsBox => Hive.box(_tagsBoxName); static Box get settingsBox => Hive.box(_settingsBoxName); /// 清理所有数据 - 清空数据库中的所有数据 /// 主要用于测试和调试,生产环境慎用 /// 返回清理操作是否成功 static Future clearAll() async { try { print('开始清理所有数据库数据...'); await imagesBox.clear(); await foldersBox.clear(); await tagsBox.clear(); await settingsBox.clear(); print('数据库清理完成'); return true; } catch (e) { print('数据库清理失败: $e'); return false; } } /// 关闭数据库 - 释放所有Hive资源 /// 应用退出时调用,确保数据完整性 /// 返回关闭操作是否成功 static Future close() async { try { print('正在关闭Hive数据库...'); await Hive.close(); print('Hive数据库已关闭'); return true; } catch (e) { print('关闭Hive数据库失败: $e'); return false; } } /// 验证数据库完整性 - 检查数据库是否损坏或缺少必要数据 /// 返回数据库是否通过完整性检查 static Future validateIntegrity() async { try { print('开始验证数据库完整性...'); final isValid = await DatabaseMigration.validateDatabaseIntegrity( imagesBox, foldersBox, tagsBox, ); if (isValid) { print('数据库完整性验证通过'); } else { print('数据库完整性验证发现问题并已修复'); } return isValid; } catch (e) { print('数据库完整性验证失败: $e'); return false; } } /// 获取数据库统计信息 - 获取数据库的使用统计 /// 返回包含统计信息的Map,包括版本、数量、大小等 static Map getStatistics() { try { return DatabaseMigration.getDatabaseStats( imagesBox, foldersBox, tagsBox, ); } catch (e) { print('获取数据库统计信息失败: $e'); return { 'error': '获取统计信息失败', 'message': e.toString(), }; } } /// 清理数据库 - 清理无效数据和临时文件 /// 返回清理结果的统计信息,包括清理的项目数量 static Future> cleanup() async { try { print('开始清理数据库...'); final cleanupStats = await DatabaseMigration.cleanupDatabase( imagesBox, foldersBox, tagsBox, ); print('数据库清理完成: $cleanupStats'); return cleanupStats; } catch (e) { print('数据库清理失败: $e'); return { 'error': 1, }; } } }