优化社交图展示并为通讯录接入本地头像缓存

This commit is contained in:
liuwei
2026-04-27 09:13:01 +08:00
parent 0636e0453f
commit e573fd9c37
4 changed files with 274 additions and 37 deletions

View File

@@ -4,7 +4,8 @@ import re
import threading
import xml.etree.ElementTree as ET
from concurrent.futures import ThreadPoolExecutor
from flask import Blueprint, render_template, jsonify, request, current_app
from urllib.parse import quote
from flask import Blueprint, render_template, jsonify, request, current_app, redirect, send_file
from .auth import login_required
from loguru import logger
@@ -242,6 +243,27 @@ def _normalize_recent_message(server, raw_message: dict, chat_type: str, target_
}
def _build_dashboard_head_images(contact_manager):
"""构造后台可直接使用的头像地址映射。
说明:
1. 前端统一访问本蓝图的头像代理接口,这样可以优先命中本地缓存;
2. 头像 URL 哈希会拼到查询参数里,头像变更后浏览器会自然拉取最新版本;
3. 即便本地缓存暂时不存在,代理接口也还能回退到远端头像地址,不影响页面展示。
"""
result = {}
for wxid, remote_url in (contact_manager.get_all_head_images() or {}).items():
if not remote_url:
result[wxid] = ""
continue
version = contact_manager.get_head_image_version(wxid)
avatar_url = f"/contacts/api/avatar/{quote(str(wxid), safe='')}"
if version:
avatar_url = f"{avatar_url}?v={version}"
result[wxid] = avatar_url
return result
# 联系人管理页面
@contacts_bp.route('/')
@login_required
@@ -375,7 +397,9 @@ def api_head_images():
"""获取联系人头像信息API"""
try:
server = current_app.dashboard_server
head_images = server.contact_manager.get_all_head_images()
# 后台页拿到的是“可展示地址”而不是原始远端 URL
# 这样通讯录页会优先读本地缓存,头像变化时也能自动刷新最新版本。
head_images = _build_dashboard_head_images(server.contact_manager)
return jsonify({
"success": True,
@@ -388,6 +412,27 @@ def api_head_images():
return jsonify({"success": False, "error": str(e)}), 500
@contacts_bp.route('/api/avatar/<path:wxid>', methods=['GET'])
@login_required
def api_contact_avatar(wxid):
"""返回通讯录头像,本地缓存优先,远端地址兜底。"""
try:
server = current_app.dashboard_server
# 先尝试把头像补齐到本地缓存。
# 这样页面首次访问某个联系人时,也能顺手把缓存热起来。
cached_path = server.contact_manager.ensure_head_image_cached(wxid)
if cached_path and os.path.exists(cached_path):
return send_file(cached_path, conditional=True, max_age=86400)
remote_url = str(server.contact_manager.get_head_image(wxid) or "").strip()
if remote_url:
return redirect(remote_url, code=302)
return jsonify({"success": False, "error": "头像不存在"}), 404
except Exception as e:
logger.error(f"读取联系人头像失败 wxid={wxid}: {e}")
return jsonify({"success": False, "error": str(e)}), 500
@contacts_bp.route('/api/group_members/<roomid>', methods=['GET'])
@login_required
def api_group_members(roomid):