Files
server-core/scripts/sync_default_avatars.py
T

76 lines
2.6 KiB
Python
Raw Normal View History

#!/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)))