# -*- coding: utf-8 -*- """默认头像池:新用户未提供头像时,从头像池随机分配一个默认头像。 头像池 = 对象存储桶 avatar/images/images/ 目录下的 1321 张头像 (对象 key 清单见 ``serverdata/avatar_pool.json``,由 code/images/images_data/images 本地目录与桶内对象一一对应生成)。 数据库只存对象相对路径 ``/oss/avatar/images/images/``, 读取时经 ``resolve_url`` 补全为 CDN 直链(见 app/infrastructure/oss.py)。 语义约定: - 新用户创建时若未显式提供头像(avatar 为空)→ 随机分配一个默认头像; - 用户一旦自行更换头像(上传真实头像)→ 走 update 流程,不再回退/重随机。 """ from __future__ import annotations import json import random from pathlib import Path from typing import List from .. import config _POOL_CACHE: List[str] | None = None _POOL_PATH: Path = config.SERVERDATA_DIR / "avatar_pool.json" def _load_pool() -> List[str]: """读取头像池对象 key 清单(内存缓存)。文件缺失/损坏时返回空池。""" global _POOL_CACHE if _POOL_CACHE is not None: return _POOL_CACHE try: raw = json.loads(_POOL_PATH.read_text(encoding="utf-8")) keys = [ str(k).strip() for k in raw if isinstance(k, str) and k.strip() ] except Exception: # noqa: BLE001 keys = [] _POOL_CACHE = keys return keys def random_default_avatar() -> str: """从默认头像池随机返回一个对象路径(``/oss/...``);池为空时返回空串。""" pool = _load_pool() if not pool: return "" return "/oss/" + random.choice(pool)