snap_wish/lib/data/datasources/local/hive_database.dart
2025-09-17 09:49:37 +08:00

275 lines
9.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 '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<void> init() async {
final appDocumentDir = await path_provider.getApplicationDocumentsDirectory();
Hive.init(appDocumentDir.path);
_registerAdapters();
await Hive.openBox<HiveInspirationImage>(_imagesBoxName);
await Hive.openBox<HiveImageFolder>(_foldersBoxName);
await Hive.openBox<HiveImageTag>(_tagsBoxName);
await Hive.openBox<dynamic>(_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<void> _performMigration() async {
final settingsBox = Hive.box<dynamic>(_settingsBoxName);
final currentDbVersion = settingsBox.get(_versionKey, defaultValue: 0) as int;
if (currentDbVersion < _currentVersion) {
try {
print('检测到数据库版本更新: $currentDbVersion -> $_currentVersion');
// 使用专门的迁移管理类执行迁移
await DatabaseMigration.migrateFromVersion(
currentDbVersion,
imagesBox,
foldersBox,
tagsBox,
settingsBox,
);
print('数据库迁移完成');
} catch (e) {
print('数据库迁移失败: $e');
// 如果迁移失败,尝试恢复到已知状态
await _handleMigrationFailure(currentDbVersion, e);
}
} else if (currentDbVersion > _currentVersion) {
// 处理降级情况(通常不应该发生)
print('警告:数据库版本高于应用版本 ($currentDbVersion > $_currentVersion)');
await settingsBox.put(_versionKey, _currentVersion);
}
}
/// 处理迁移失败 - 当迁移过程中出现错误时的恢复逻辑
/// [fromVersion] 迁移起始版本
/// [error] 迁移过程中出现的错误
static Future<void> _handleMigrationFailure(int fromVersion, dynamic error) async {
print('处理迁移失败,尝试恢复到安全状态...');
try {
// 如果是最初版本0的迁移失败可以尝试重新创建基础结构
if (fromVersion == 0) {
await _createEmergencyData();
}
// 记录错误信息到设置中,便于后续分析
final settingsBox = Hive.box<dynamic>(_settingsBoxName);
await settingsBox.put('last_migration_error', error.toString());
await settingsBox.put('last_migration_time', DateTime.now().toIso8601String());
} catch (recoveryError) {
print('迁移恢复失败: $recoveryError');
// 如果恢复也失败,只能抛出异常让上层处理
throw DatabaseMigrationException(
fromVersion: fromVersion,
toVersion: _currentVersion,
message: '数据库迁移失败且无法恢复',
originalError: error,
);
}
}
/// 创建默认数据 - 初始化数据库时的默认数据创建
/// 创建系统必需的默认文件夹和标签
static Future<void> _createDefaultData() async {
final foldersBox = Hive.box<HiveImageFolder>(_foldersBoxName);
final tagsBox = Hive.box<HiveImageTag>(_tagsBoxName);
// 使用迁移管理类创建默认数据
await DatabaseMigration.createDefaultFolders(foldersBox);
await DatabaseMigration.createDefaultTags(tagsBox);
}
/// 创建紧急恢复数据 - 当迁移失败时的紧急恢复方案
/// 确保应用至少能正常运行,包含最基本的数据结构
static Future<void> _createEmergencyData() async {
print('创建紧急恢复数据...');
try {
final foldersBox = Hive.box<HiveImageFolder>(_foldersBoxName);
final settingsBox = Hive.box<dynamic>(_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<HiveInspirationImage> get imagesBox => Hive.box<HiveInspirationImage>(_imagesBoxName);
static Box<HiveImageFolder> get foldersBox => Hive.box<HiveImageFolder>(_foldersBoxName);
static Box<HiveImageTag> get tagsBox => Hive.box<HiveImageTag>(_tagsBoxName);
static Box<dynamic> get settingsBox => Hive.box<dynamic>(_settingsBoxName);
/// 清理所有数据 - 清空数据库中的所有数据
/// 主要用于测试和调试,生产环境慎用
/// 返回清理操作是否成功
static Future<bool> 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<bool> close() async {
try {
print('正在关闭Hive数据库...');
await Hive.close();
print('Hive数据库已关闭');
return true;
} catch (e) {
print('关闭Hive数据库失败: $e');
return false;
}
}
/// 验证数据库完整性 - 检查数据库是否损坏或缺少必要数据
/// 返回数据库是否通过完整性检查
static Future<bool> 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<String, dynamic> getStatistics() {
try {
return DatabaseMigration.getDatabaseStats(
imagesBox,
foldersBox,
tagsBox,
);
} catch (e) {
print('获取数据库统计信息失败: $e');
return {
'error': '获取统计信息失败',
'message': e.toString(),
};
}
}
/// 清理数据库 - 清理无效数据和临时文件
/// 返回清理结果的统计信息,包括清理的项目数量
static Future<Map<String, int>> cleanup() async {
try {
print('开始清理数据库...');
final cleanupStats = await DatabaseMigration.cleanupDatabase(
imagesBox,
foldersBox,
tagsBox,
);
print('数据库清理完成: $cleanupStats');
return cleanupStats;
} catch (e) {
print('数据库清理失败: $e');
return {
'error': 1,
};
}
}
}