优化社交图展示并为通讯录接入本地头像缓存
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -669,7 +669,7 @@
|
||||
|
||||
<el-dialog title="公共好友详情" :visible.sync="publicDetailDialogVisible" width="50%">
|
||||
<div class="detail-avatar-wrap">
|
||||
<el-avatar size="large" :src="currentPublic.small_head_img_url" @error="() => true" class="detail-avatar">
|
||||
<el-avatar size="large" :src="getHeadImage(currentPublic.wxid)" @error="() => true" class="detail-avatar">
|
||||
<img src="/static/logo.png"/>
|
||||
</el-avatar>
|
||||
</div>
|
||||
@@ -968,6 +968,10 @@
|
||||
},
|
||||
refreshContacts() { this.loadContactsData(); this.$message.success('联系人数据已刷新'); },
|
||||
handleTabClick() { this.currentPage = 1; },
|
||||
// 通讯录头像统一走后台代理接口:
|
||||
// 1. 优先命中服务端已缓存的本地头像;
|
||||
// 2. 头像更新后会附带版本参数,浏览器不会一直吃旧图;
|
||||
// 3. 代理接口兜底远端地址,因此这里保持简单读取即可。
|
||||
getHeadImage(wxid) { return this.headImages[wxid] || ''; },
|
||||
handleSizeChange(size) { this.pageSize = size; },
|
||||
handleCurrentChange(page) { this.currentPage = page; },
|
||||
|
||||
Reference in New Issue
Block a user