76 lines
2.6 KiB
Python
76 lines
2.6 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""存量用户默认头像同步脚本。
|
|||
|
|
|
|||
|
|
给「头像为空」的存量用户随机分配一个默认头像(来自 avatar_pool.json 池)。
|
|||
|
|
已有真实头像(非空、非默认池前缀)的用户不会被覆盖。
|
|||
|
|
|
|||
|
|
用法:
|
|||
|
|
python scripts/sync_default_avatars.py # 实际执行
|
|||
|
|
python scripts/sync_default_avatars.py --dry-run # 只预览不写库
|
|||
|
|
"""
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import argparse
|
|||
|
|
import asyncio
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
from dotenv import load_dotenv
|
|||
|
|
|
|||
|
|
load_dotenv(Path(__file__).resolve().parent.parent / ".env")
|
|||
|
|
|
|||
|
|
from sqlalchemy import text # noqa: E402
|
|||
|
|
|
|||
|
|
from app.infrastructure.db import AsyncSessionLocal # noqa: E402
|
|||
|
|
from app.infrastructure.repositories import utcnow_iso # noqa: E402
|
|||
|
|
from app.services.avatar_pool import random_default_avatar # noqa: E402
|
|||
|
|
|
|||
|
|
DEFAULT_POOL_PREFIX = "/oss/avatar/images/images/"
|
|||
|
|
|
|||
|
|
|
|||
|
|
async def main(dry_run: bool) -> int:
|
|||
|
|
async with AsyncSessionLocal() as session:
|
|||
|
|
# 仅处理头像为空(或 NULL)的存量用户;已设置真实头像的不动
|
|||
|
|
rows = (
|
|||
|
|
await session.execute(
|
|||
|
|
text(
|
|||
|
|
"SELECT id, nickname, username FROM users "
|
|||
|
|
"WHERE avatar IS NULL OR avatar = ''"
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
).all()
|
|||
|
|
total = len(rows)
|
|||
|
|
print(f"待同步用户数(头像为空): {total}")
|
|||
|
|
if total == 0:
|
|||
|
|
print("无需同步。")
|
|||
|
|
return 0
|
|||
|
|
|
|||
|
|
updated = 0
|
|||
|
|
for row in rows:
|
|||
|
|
avatar = random_default_avatar()
|
|||
|
|
if not avatar:
|
|||
|
|
print("警告:默认头像池为空,跳过(请检查 serverdata/avatar_pool.json)")
|
|||
|
|
return 1
|
|||
|
|
print(
|
|||
|
|
f" {'[DRY] ' if dry_run else '[set] '}{row.id} "
|
|||
|
|
f"({row.nickname or row.username or ''}) -> {avatar}"
|
|||
|
|
)
|
|||
|
|
if not dry_run:
|
|||
|
|
await session.execute(
|
|||
|
|
text("UPDATE users SET avatar = :av, updated_at = :now WHERE id = :uid"),
|
|||
|
|
{"av": avatar, "uid": row.id, "now": utcnow_iso()},
|
|||
|
|
)
|
|||
|
|
updated += 1
|
|||
|
|
|
|||
|
|
if not dry_run:
|
|||
|
|
await session.commit()
|
|||
|
|
print(f"完成:{'(dry-run 未写库)' if dry_run else ''}计划/已更新 {updated} 人。")
|
|||
|
|
return 0
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
parser = argparse.ArgumentParser(description="存量用户默认头像同步")
|
|||
|
|
parser.add_argument("--dry-run", action="store_true", help="只预览不写库")
|
|||
|
|
args = parser.parse_args()
|
|||
|
|
raise SystemExit(asyncio.run(main(args.dry_run)))
|