mirror of
https://github.com/zhouxiaoka/autoclip.git
synced 2026-09-03 06:24:14 +08:00
主要改进: 1. 清理重复文件和冗余代码 - 删除重复的API文件 (upload_backup.py, upload_new.py等) - 删除旧版本的B站服务文件 (bilibili_upload_v2.py到v6.py) - 统一B站服务接口 2. 优化数据存储架构 - 创建OptimizedStorageService解决双重存储问题 - 数据库只存储元数据,文件系统存储实际文件 - 提供数据迁移脚本和一致性检查工具 3. 统一错误处理机制 - 创建统一错误处理中间件 - 提供错误处理装饰器和上下文管理器 - 统一错误响应格式 4. 完善配置管理 - 创建UnifiedConfig统一配置系统 - 整合所有配置源(环境变量、配置文件、默认值) - 提供配置迁移脚本 5. 增强进度系统 - 创建EnhancedProgressService - 支持Redis缓存、数据库持久化和内存缓存 - 提供完整的进度跟踪和状态管理 新增文件: - backend/services/optimized_storage_service.py - backend/core/error_middleware.py - backend/core/unified_config.py - backend/services/enhanced_progress_service.py - scripts/migrate_to_optimized_storage.py - scripts/check_data_consistency.py - scripts/migrate_config.py - docs/ERROR_HANDLING_GUIDE.md - docs/PROGRESS_SYSTEM_GUIDE.md 修复文件: - backend/services/bilibili_service.py (统一上传接口) - backend/main.py (使用新的错误处理中间件)
247 lines
8.4 KiB
Python
247 lines
8.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
数据存储优化迁移脚本
|
|
将双重存储模式迁移到优化存储模式(数据库存储元数据,文件系统存储实际文件)
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import json
|
|
import logging
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
# 添加项目根目录到Python路径
|
|
project_root = Path(__file__).parent.parent
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
from backend.core.database import SessionLocal
|
|
from backend.services.optimized_storage_service import OptimizedStorageService
|
|
from backend.models.project import Project
|
|
from backend.models.clip import Clip
|
|
from backend.models.collection import Collection
|
|
|
|
# 配置日志
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def analyze_current_storage():
|
|
"""分析当前存储状况"""
|
|
logger.info("🔍 分析当前存储状况...")
|
|
|
|
data_dir = project_root / "data"
|
|
projects_dir = data_dir / "projects"
|
|
|
|
if not projects_dir.exists():
|
|
logger.warning("项目目录不存在")
|
|
return {"projects": [], "total_size": 0}
|
|
|
|
projects_info = []
|
|
total_size = 0
|
|
|
|
for project_dir in projects_dir.iterdir():
|
|
if project_dir.is_dir() and not project_dir.name.startswith('.'):
|
|
project_id = project_dir.name
|
|
project_size = sum(f.stat().st_size for f in project_dir.rglob('*') if f.is_file())
|
|
|
|
# 检查是否有重复数据
|
|
has_duplicate_data = False
|
|
if (project_dir / "clips_metadata.json").exists():
|
|
has_duplicate_data = True
|
|
|
|
projects_info.append({
|
|
"project_id": project_id,
|
|
"size_mb": round(project_size / (1024 * 1024), 2),
|
|
"has_duplicate_data": has_duplicate_data,
|
|
"files_count": len(list(project_dir.rglob('*')))
|
|
})
|
|
|
|
total_size += project_size
|
|
|
|
logger.info(f"📊 分析完成: {len(projects_info)} 个项目, 总大小: {round(total_size / (1024 * 1024), 2)} MB")
|
|
|
|
return {
|
|
"projects": projects_info,
|
|
"total_size": total_size
|
|
}
|
|
|
|
|
|
def migrate_project_to_optimized_storage(db, project_id: str, dry_run: bool = True):
|
|
"""迁移单个项目到优化存储模式"""
|
|
logger.info(f"🔄 迁移项目: {project_id} (dry_run={dry_run})")
|
|
|
|
try:
|
|
# 获取项目信息
|
|
project = db.query(Project).filter(Project.id == project_id).first()
|
|
if not project:
|
|
logger.warning(f"项目 {project_id} 在数据库中不存在")
|
|
return {"success": False, "error": "项目不存在"}
|
|
|
|
# 检查项目目录
|
|
data_dir = project_root / "data"
|
|
old_project_dir = data_dir / "projects" / project_id
|
|
|
|
if not old_project_dir.exists():
|
|
logger.warning(f"项目目录不存在: {old_project_dir}")
|
|
return {"success": False, "error": "项目目录不存在"}
|
|
|
|
if dry_run:
|
|
logger.info(f"🔍 模拟迁移项目 {project_id}")
|
|
return {"success": True, "dry_run": True, "message": "模拟迁移成功"}
|
|
|
|
# 创建优化存储服务
|
|
storage_service = OptimizedStorageService(db, project_id)
|
|
|
|
# 执行迁移
|
|
migration_result = storage_service.migrate_from_old_storage(old_project_dir)
|
|
|
|
if migration_result["success"]:
|
|
logger.info(f"✅ 项目 {project_id} 迁移成功")
|
|
|
|
# 清理旧的双重存储文件
|
|
cleanup_old_duplicate_files(old_project_dir)
|
|
|
|
return {
|
|
"success": True,
|
|
"migrated_files": migration_result["migrated_files"],
|
|
"migrated_metadata": migration_result["migrated_metadata"]
|
|
}
|
|
else:
|
|
logger.error(f"❌ 项目 {project_id} 迁移失败: {migration_result['error']}")
|
|
return migration_result
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ 迁移项目 {project_id} 时发生错误: {e}")
|
|
return {"success": False, "error": str(e)}
|
|
|
|
|
|
def cleanup_old_duplicate_files(project_dir: Path):
|
|
"""清理旧的双重存储文件"""
|
|
try:
|
|
logger.info(f"🧹 清理项目 {project_dir.name} 的重复文件...")
|
|
|
|
# 删除重复的元数据文件
|
|
duplicate_files = [
|
|
"clips_metadata.json",
|
|
"collections_metadata.json",
|
|
"step1_outline.json",
|
|
"step2_timeline.json",
|
|
"step3_scoring.json",
|
|
"step4_titles.json",
|
|
"step5_collections.json"
|
|
]
|
|
|
|
cleaned_count = 0
|
|
for file_name in duplicate_files:
|
|
file_path = project_dir / file_name
|
|
if file_path.exists():
|
|
# 备份文件
|
|
backup_path = project_dir / f"{file_name}.backup"
|
|
file_path.rename(backup_path)
|
|
cleaned_count += 1
|
|
logger.info(f"📦 备份重复文件: {file_name}")
|
|
|
|
logger.info(f"✅ 清理完成,备份了 {cleaned_count} 个重复文件")
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ 清理重复文件失败: {e}")
|
|
|
|
|
|
def main():
|
|
"""主函数"""
|
|
logger.info("🚀 开始数据存储优化迁移...")
|
|
|
|
# 分析当前存储状况
|
|
storage_info = analyze_current_storage()
|
|
|
|
if not storage_info["projects"]:
|
|
logger.info("📭 没有找到需要迁移的项目")
|
|
return
|
|
|
|
# 显示分析结果
|
|
print("\n📊 当前存储状况分析:")
|
|
print("=" * 60)
|
|
for project in storage_info["projects"]:
|
|
status = "⚠️ 有重复数据" if project["has_duplicate_data"] else "✅ 正常"
|
|
print(f"项目 {project['project_id'][:8]}... | {project['size_mb']:>8.2f} MB | {project['files_count']:>4} 文件 | {status}")
|
|
|
|
print(f"\n总大小: {round(storage_info['total_size'] / (1024 * 1024), 2)} MB")
|
|
|
|
# 询问是否继续
|
|
print("\n" + "=" * 60)
|
|
print("🔧 迁移选项:")
|
|
print("1. 模拟迁移 (dry run) - 查看迁移效果但不实际执行")
|
|
print("2. 执行迁移 - 实际迁移数据并清理重复文件")
|
|
print("3. 退出")
|
|
|
|
while True:
|
|
choice = input("\n请选择操作 (1/2/3): ").strip()
|
|
if choice in ['1', '2', '3']:
|
|
break
|
|
print("❌ 无效选择,请输入 1、2 或 3")
|
|
|
|
if choice == '3':
|
|
logger.info("👋 用户取消迁移")
|
|
return
|
|
|
|
dry_run = (choice == '1')
|
|
|
|
if dry_run:
|
|
logger.info("🔍 开始模拟迁移...")
|
|
else:
|
|
logger.info("🚀 开始实际迁移...")
|
|
# 创建备份
|
|
backup_dir = project_root / f"migration_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
backup_dir.mkdir(exist_ok=True)
|
|
logger.info(f"📦 创建备份目录: {backup_dir}")
|
|
|
|
# 执行迁移
|
|
db = SessionLocal()
|
|
try:
|
|
success_count = 0
|
|
failed_count = 0
|
|
|
|
for project_info in storage_info["projects"]:
|
|
project_id = project_info["project_id"]
|
|
|
|
result = migrate_project_to_optimized_storage(db, project_id, dry_run)
|
|
|
|
if result["success"]:
|
|
success_count += 1
|
|
if dry_run:
|
|
logger.info(f"✅ 模拟迁移成功: {project_id}")
|
|
else:
|
|
logger.info(f"✅ 迁移成功: {project_id}")
|
|
else:
|
|
failed_count += 1
|
|
logger.error(f"❌ 迁移失败: {project_id} - {result.get('error', '未知错误')}")
|
|
|
|
# 显示迁移结果
|
|
print("\n" + "=" * 60)
|
|
print("📊 迁移结果:")
|
|
print(f"✅ 成功: {success_count}")
|
|
print(f"❌ 失败: {failed_count}")
|
|
print(f"📊 总计: {success_count + failed_count}")
|
|
|
|
if not dry_run and success_count > 0:
|
|
print(f"\n💾 备份位置: {backup_dir}")
|
|
print("🔧 建议:")
|
|
print("1. 测试系统功能是否正常")
|
|
print("2. 确认无误后可以删除备份文件")
|
|
print("3. 运行数据一致性检查")
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ 迁移过程中发生错误: {e}")
|
|
finally:
|
|
db.close()
|
|
|
|
logger.info("🎉 迁移完成!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|