import 'package:hive/hive.dart'; import '../../models/hive_inspiration_image.dart'; import '../../models/hive_image_folder.dart'; import '../../models/hive_image_tag.dart'; import '../../../core/utils/logger.dart'; /// 数据库迁移管理类 - 负责处理数据库版本升级和数据迁移 /// 确保应用在升级时能够正确迁移旧版本数据 class DatabaseMigration { /// 当前数据库版本号 - 每次数据库结构变更时递增 static const int currentVersion = 1; /// 数据库版本键名 - 在设置盒中存储版本信息的键 static const String versionKey = 'database_version'; /// 执行数据库迁移 - 根据当前版本和目标版本执行相应的迁移逻辑 /// [fromVersion] 当前数据库版本 /// [imagesBox] 图片数据盒 /// [foldersBox] 文件夹数据盒 /// [tagsBox] 标签数据盒 /// [settingsBox] 设置数据盒 static Future migrateFromVersion( int fromVersion, Box imagesBox, Box foldersBox, Box tagsBox, Box settingsBox, ) async { // TODO: 使用日志系统替代print // print('开始数据库迁移: 从版本 $fromVersion 到版本 $currentVersion'); // 按版本顺序执行迁移 for (int version = fromVersion + 1; version <= currentVersion; version++) { // TODO: 使用日志系统替代print // print('执行迁移到版本 $version'); switch (version) { case 1: await _migrateToVersion1(foldersBox, tagsBox); break; // case 2: // await _migrateToVersion2(imagesBox, foldersBox, tagsBox); // break; // 在这里添加更多版本的迁移逻辑 default: // TODO: 使用日志系统替代print // print('未知的数据库版本: $version'); break; } // 更新版本号 await settingsBox.put(versionKey, version); // TODO: 使用日志系统替代print // print('成功迁移到版本 $version'); } // TODO: 使用日志系统替代print // print('数据库迁移完成'); } /// 迁移到版本1 - 初始版本,创建默认数据 /// [foldersBox] 文件夹数据盒 /// [tagsBox] 标签数据盒 static Future _migrateToVersion1( Box foldersBox, Box tagsBox, ) async { // TODO: 使用日志系统替代print // print('创建版本1的默认数据...'); // 创建默认文件夹 await createDefaultFolders(foldersBox); // 创建默认标签 await createDefaultTags(tagsBox); // TODO: 使用日志系统替代print // print('版本1默认数据创建完成'); } /// 创建默认文件夹 - 初始化系统必需的文件夹 /// [foldersBox] 文件夹数据盒 static Future createDefaultFolders(Box foldersBox) async { // TODO: 使用日志系统替代print // print('创建默认文件夹...'); // 默认文件夹配置 final defaultFolders = [ { 'id': 'default', 'name': '默认', 'icon': 'folder', 'description': '默认文件夹,用于存放未分类的图片', }, { 'id': 'favorites', 'name': '收藏', 'icon': 'favorite', 'description': '收藏的图片', }, { 'id': 'inspiration', 'name': '灵感', 'icon': 'lightbulb', 'description': '灵感素材', }, ]; for (final folderConfig in defaultFolders) { // 检查文件夹是否已存在 if (!foldersBox.containsKey(folderConfig['id'])) { final now = DateTime.now(); final folder = HiveImageFolder( id: folderConfig['id'] as String, name: folderConfig['name'] as String, icon: folderConfig['icon'] as String, createdAt: now, updatedAt: now, lastUsedAt: now, ); await foldersBox.put(folder.id, folder); } else { Logger.debug('默认文件夹已存在: ${folderConfig['name']}'); } } } /// 创建默认标签 - 初始化系统常用的标签 /// [tagsBox] 标签数据盒 static Future createDefaultTags(Box tagsBox) async { Logger.info('创建默认标签...'); // 默认标签配置 final defaultTags = [ { 'id': 'tag_favorite', 'name': '收藏', 'icon': 'favorite', 'color': '#FF4444', 'description': '收藏的标签', }, { 'id': 'tag_inspiration', 'name': '灵感', 'icon': 'lightbulb', 'color': '#FFD700', 'description': '灵感素材', }, { 'id': 'tag_design', 'name': '设计', 'icon': 'palette', 'color': '#9C27B0', 'description': '设计相关', }, { 'id': 'tag_photo', 'name': '摄影', 'icon': 'camera_alt', 'color': '#2196F3', 'description': '摄影作品', }, { 'id': 'tag_nature', 'name': '自然', 'icon': 'nature', 'color': '#4CAF50', 'description': '自然风光', }, { 'id': 'tag_architecture', 'name': '建筑', 'icon': 'location_city', 'color': '#795548', 'description': '建筑摄影', }, { 'id': 'tag_food', 'name': '美食', 'icon': 'restaurant', 'color': '#FF9800', 'description': '美食摄影', }, { 'id': 'tag_travel', 'name': '旅行', 'icon': 'flight', 'color': '#00BCD4', 'description': '旅行记录', }, ]; for (final tagConfig in defaultTags) { // 检查标签是否已存在 if (!tagsBox.containsKey(tagConfig['id'])) { final now = DateTime.now(); final tag = HiveImageTag( id: tagConfig['id'] as String, name: tagConfig['name'] as String, icon: tagConfig['icon'] as String, color: tagConfig['color'] as String, usageCount: 0, lastUsedAt: now, ); await tagsBox.put(tag.id, tag); } } } /// 验证数据库完整性 - 检查数据库是否损坏或缺少必要数据 /// [imagesBox] 图片数据盒 /// [foldersBox] 文件夹数据盒 /// [tagsBox] 标签数据盒 /// 返回验证结果,如果发现问题会尝试修复 static Future validateDatabaseIntegrity( Box imagesBox, Box foldersBox, Box tagsBox, ) async { Logger.info('开始数据库完整性验证...'); bool isValid = true; try { // 验证默认文件夹是否存在 if (!foldersBox.containsKey('default')) { // TODO: 使用日志系统替代print // print('警告:缺少默认文件夹,正在创建...'); await createDefaultFolders(foldersBox); isValid = false; } // 验证图片数据完整性 for (final image in imagesBox.values) { // 验证文件夹关联 if (image.folderId != null && !foldersBox.containsKey(image.folderId)) { // TODO: 使用日志系统替代print // print('警告:图片 ${image.id} 关联了不存在的文件夹 ${image.folderId},正在修复...'); // 将图片移动到默认文件夹 final updatedImage = HiveInspirationImage( id: image.id, filePath: image.filePath, thumbnailPath: image.thumbnailPath, folderId: 'default', tags: image.tags, note: image.note, createdAt: image.createdAt, updatedAt: image.updatedAt, originalName: image.originalName, fileSize: image.fileSize, mimeType: image.mimeType, width: image.width, height: image.height, isFavorite: image.isFavorite, ); await imagesBox.put(image.id, updatedImage); isValid = false; } // 验证标签关联 for (final tagId in image.tags) { if (!tagsBox.containsKey(tagId)) { // TODO: 使用日志系统替代print // print('警告:图片 ${image.id} 关联了不存在的标签 $tagId'); // 移除无效的标签关联 final updatedTags = List.from(image.tags)..remove(tagId); final updatedImage = HiveInspirationImage( id: image.id, filePath: image.filePath, thumbnailPath: image.thumbnailPath, folderId: image.folderId, tags: updatedTags, note: image.note, createdAt: image.createdAt, updatedAt: image.updatedAt, originalName: image.originalName, fileSize: image.fileSize, mimeType: image.mimeType, width: image.width, height: image.height, isFavorite: image.isFavorite, ); await imagesBox.put(image.id, updatedImage); isValid = false; } } } // 验证标签使用次数统计 for (final tag in tagsBox.values) { final actualUsageCount = imagesBox.values .where((image) => image.tags.contains(tag.id)) .length; if (tag.usageCount != actualUsageCount) { final updatedTag = HiveImageTag( id: tag.id, name: tag.name, icon: tag.icon, color: tag.color, usageCount: actualUsageCount, lastUsedAt: tag.lastUsedAt, ); await tagsBox.put(tag.id, updatedTag); isValid = false; } } } catch (e) { // TODO: 使用日志系统替代print // print('数据库完整性验证失败: $e'); isValid = false; } if (isValid) { // TODO: 使用日志系统替代print // print('数据库完整性验证通过'); } else { // TODO: 使用日志系统替代print // print('数据库完整性验证发现问题并已修复'); } return isValid; } /// 备份数据库 - 创建数据库的备份副本 /// [backupPath] 备份文件路径 /// 返回是否备份成功 static Future backupDatabase(String backupPath) async { try { // TODO: 使用日志系统替代print // print('开始备份数据库到: $backupPath'); // TODO: 实现数据库备份逻辑 // 这需要根据实际的Hive存储路径来实现 // TODO: 使用日志系统替代print // print('数据库备份完成'); return true; } catch (e) { // TODO: 使用日志系统替代print // print('数据库备份失败: $e'); return false; } } /// 恢复数据库 - 从备份文件恢复数据库 /// [backupPath] 备份文件路径 /// 返回是否恢复成功 static Future restoreDatabase(String backupPath) async { try { // TODO: 使用日志系统替代print // print('开始从备份恢复数据库: $backupPath'); // TODO: 实现数据库恢复逻辑 // 这需要根据实际的Hive存储路径来实现 // TODO: 使用日志系统替代print // print('数据库恢复完成'); return true; } catch (e) { // TODO: 使用日志系统替代print // print('数据库恢复失败: $e'); return false; } } /// 获取数据库统计信息 - 获取数据库的使用统计 /// [imagesBox] 图片数据盒 /// [foldersBox] 文件夹数据盒 /// [tagsBox] 标签数据盒 /// 返回包含统计信息的Map static Map getDatabaseStats( Box imagesBox, Box foldersBox, Box tagsBox, ) { return { 'version': currentVersion, 'images_count': imagesBox.length, 'folders_count': foldersBox.length, 'tags_count': tagsBox.length, 'total_size': _calculateTotalSize(imagesBox), 'last_updated': DateTime.now().toIso8601String(), }; } /// 计算总数据大小 - 估算数据库的总大小 /// [imagesBox] 图片数据盒 /// 返回估算的总大小(字节) static int _calculateTotalSize(Box imagesBox) { int totalSize = 0; for (final image in imagesBox.values) { totalSize += image.fileSize; } return totalSize; } /// 清理数据库 - 清理无效数据和临时文件 /// [imagesBox] 图片数据盒 /// [foldersBox] 文件夹数据盒 /// [tagsBox] 标签数据盒 /// 返回清理结果的统计信息 static Future> cleanupDatabase( Box imagesBox, Box foldersBox, Box tagsBox, ) async { // TODO: 使用日志系统替代print // print('开始清理数据库...'); final cleanupStats = {}; try { // 清理未使用的标签 final unusedTags = tagsBox.values.where((tag) => tag.usageCount == 0).toList(); cleanupStats['unused_tags_removed'] = unusedTags.length; for (final tag in unusedTags) { await tagsBox.delete(tag.id); } // 清理无效的图片文件引用 final invalidImages = imagesBox.values.where((image) { // 这里可以添加文件存在性检查 // 暂时只检查文件夹关联 return image.folderId != null && !foldersBox.containsKey(image.folderId); }).toList(); cleanupStats['invalid_images_fixed'] = invalidImages.length; for (final image in invalidImages) { final updatedImage = HiveInspirationImage( id: image.id, filePath: image.filePath, thumbnailPath: image.thumbnailPath, folderId: 'default', tags: image.tags, note: image.note, createdAt: image.createdAt, updatedAt: image.updatedAt, originalName: image.originalName, fileSize: image.fileSize, mimeType: image.mimeType, width: image.width, height: image.height, isFavorite: image.isFavorite, ); await imagesBox.put(image.id, updatedImage); } // TODO: 使用日志系统替代print // print('数据库清理完成: $cleanupStats'); } catch (e) { // TODO: 使用日志系统替代print // print('数据库清理失败: $e'); } return cleanupStats; } } /// 数据库异常类 - 数据库操作相关的异常 class DatabaseException implements Exception { final String message; final String? code; final dynamic originalError; DatabaseException(this.message, {this.code, this.originalError}); @override String toString() => 'DatabaseException: $message${code != null ? ' (代码: $code)' : ''}'; } /// 数据库迁移异常类 - 数据迁移过程中的异常 class DatabaseMigrationException extends DatabaseException { final int fromVersion; final int toVersion; DatabaseMigrationException({ required this.fromVersion, required this.toVersion, required String message, String? code, dynamic originalError, }) : super( '数据库迁移失败 (版本 $fromVersion -> $toVersion): $message', code: code, originalError: originalError, ); }