89 lines
3.8 KiB
Python
89 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
import sys
|
|
|
|
from app.models.base import async_session
|
|
from app.enums.upload_resource import UploadResourceEventEnum
|
|
from app.services.upload_resource.backfill_service import BackfillOptions, run_upload_resource_backfill
|
|
from app.services.upload_resource.log_service import log_upload_resource_exception, safe_rollback_with_log
|
|
|
|
|
|
VALID_MODULES = {"common", "hot_opening_replicate", "shot_replicate"}
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(description="补录 storage/uploads 下的上传资源账本,并可重绑模块归属。")
|
|
parser.add_argument("--root", default="storage/uploads", help="扫描根目录,默认 storage/uploads")
|
|
parser.add_argument("--batch-size", type=int, default=500, help="每批处理数量,默认 500")
|
|
parser.add_argument("--dry-run", action="store_true", help="试跑,不写数据库")
|
|
parser.add_argument("--commit", action="store_true", help="真实执行写入")
|
|
parser.add_argument("--include-legacy", action="store_true", help="补录 gen_{user_id}_* 等旧版散落文件")
|
|
parser.add_argument("--rebind-modules", action="store_true", help="根据 hot/shot 业务表中保存的 URL 重新绑定模块归属")
|
|
parser.add_argument("--rebuild-stats", action="store_true", help="补录完成后按资源账本重算容量统计")
|
|
parser.add_argument("--only-user-id", default=None, help="只处理指定用户ID")
|
|
parser.add_argument("--only-module", choices=sorted(VALID_MODULES), default=None, help="只处理指定模块")
|
|
parser.add_argument("--cleanup-pending-files", action="store_true", help="清理已软删但真实文件待删除/删除失败的 UploadResource 文件")
|
|
parser.add_argument("--cleanup-limit", type=int, default=500, help="每次最多清理待删除真实文件数量,默认 500")
|
|
return parser
|
|
|
|
|
|
async def amain(argv: list[str]) -> int:
|
|
parser = build_parser()
|
|
args = parser.parse_args(argv)
|
|
if args.dry_run and args.commit:
|
|
parser.error("--dry-run 和 --commit 不能同时使用")
|
|
if not args.dry_run and not args.commit:
|
|
args.dry_run = True
|
|
if args.batch_size <= 0:
|
|
parser.error("--batch-size 必须大于 0")
|
|
|
|
options = BackfillOptions(
|
|
root=args.root,
|
|
batch_size=args.batch_size,
|
|
dry_run=not args.commit,
|
|
include_legacy=args.include_legacy,
|
|
rebind_modules=args.rebind_modules,
|
|
rebuild_stats=args.rebuild_stats,
|
|
only_user_id=args.only_user_id,
|
|
only_module=args.only_module,
|
|
cleanup_pending_files=args.cleanup_pending_files,
|
|
cleanup_limit=args.cleanup_limit,
|
|
)
|
|
|
|
async with async_session() as db:
|
|
try:
|
|
result = await run_upload_resource_backfill(db, options)
|
|
if options.dry_run:
|
|
await db.rollback()
|
|
else:
|
|
await db.commit()
|
|
except Exception as exc: # noqa: BLE001
|
|
await safe_rollback_with_log(
|
|
db,
|
|
event_type=UploadResourceEventEnum.UPLOAD_RESOURCE_BACKFILL_CLI_ROLLBACK_FAILED.value,
|
|
message="UploadResource 补录/清理 CLI 回滚失败",
|
|
detail={"argv": argv, "options": vars(args)},
|
|
original_exc=exc,
|
|
)
|
|
log_upload_resource_exception(
|
|
event_type=UploadResourceEventEnum.UPLOAD_RESOURCE_BACKFILL_CLI_FAILED.value,
|
|
message=f"UploadResource 补录/清理 CLI 执行失败: {exc}",
|
|
detail={"argv": argv, "options": vars(args)},
|
|
exc=exc,
|
|
)
|
|
raise
|
|
|
|
print(json.dumps(result.to_dict(), ensure_ascii=False, indent=2, default=str))
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
return asyncio.run(amain(sys.argv[1:]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|