Files
server-core/scripts/sync_default_avatars.py
T

76 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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)))