Files
Simon bea288e414 feat: 2026-07-11 全量更新 - 代办集成+会议室预定+知识迭代修复+UI统一+Bug修复
== 已部署上线 (9项) ==
- 代办事项真实数据源集成 (企微审批API 8bug修复链)
- H5/坐席端 Logo样式统一+绿色背景
- 视频引导页修复 (localStorage key v2)
- 坐席端 v9 Vue版本修复 (ElMessage._context)
- 截图按钮 v10 修复 (getDisplayMedia user gesture)
- 扫码样式恢复+H5扫码登录跳转修复
- H5截图快捷键提示

== 代码完成待部署 (3项) ==
- 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查)
- 会议室预定-小鱼易联终端 (40文件, 40/40测试通过)
- IT资产升级审批推送 (asset_service.py)

== 需求文档 (2项) ==
- 坐席端AI辅助消息框-PRD (4项新功能确认)
- 坐席端布局优化建议 v2.0 (7天计划)

== 新增文档 ==
- 日报-2026-07-11.md
- 知识迭代Bug修复报告-20260711.md
- 会议室预定-部署指南.md
- CHANGELOG.md 更新

== 测试 ==
- test_todo_integration.py: 40/40
- test_meetingroom.py: 40/40
- test_bugfix_ki_suggestions.py: 21/21
2026-07-11 23:13:10 +08:00

79 lines
3.0 KiB
Python
Raw Permalink 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.
# =============================================================================
# 企微IT智能服务台 — 头像代理 API
# =============================================================================
# 说明:代理企微头像图片,解决 COEP/Mixed Content/CSP 跨域问题。
# 前端通过 /api/avatar/proxy?url=<encoded_url> 访问企微头像,
# 后端服务器请求图片并返回,避免浏览器直接请求 wework.qpic.cn。
# =============================================================================
import logging
from urllib.parse import unquote
import httpx
from fastapi import APIRouter, Query, Response
from app.utils.response import AppException
logger = logging.getLogger(__name__)
router = APIRouter()
# 允许代理的域名白名单
ALLOWED_AVATAR_DOMAINS = ["wework.qpic.cn"]
@router.get("/avatar/proxy")
async def proxy_avatar(
url: str = Query(..., description="要代理的头像 URLURL 编码)"),
):
"""代理企微头像图片。
解决浏览器 COEP/CSP/Mixed Content 策略导致 wework.qpic.cn 图片无法加载的问题。
后端服务器请求图片后返回给前端,代理 URL 为同源 HTTPS,不受跨域策略限制。
Args:
url: 要代理的头像 URL(URL 编码后的)
Returns:
Response: 图片二进制数据,带 Content-Type 和 Cache-Control 头
Raises:
AppException: URL 不在白名单中或图片获取失败
"""
# 解码 URL
avatar_url = unquote(url)
# 安全校验:只允许代理白名单域名的图片
if not any(domain in avatar_url for domain in ALLOWED_AVATAR_DOMAINS):
logger.warning(f"头像代理被拒绝(域名不在白名单): {avatar_url[:80]}")
raise AppException(code=403, message="只允许代理企微头像")
# 确保 HTTPS
if avatar_url.startswith("http://"):
avatar_url = "https://" + avatar_url[7:]
# 请求图片
try:
async with httpx.AsyncClient(timeout=10.0, follow_redirects=True) as client:
resp = await client.get(avatar_url)
if resp.status_code == 200:
content_type = resp.headers.get("content-type", "image/jpeg")
return Response(
content=resp.content,
media_type=content_type,
headers={
"Cache-Control": "public, max-age=86400", # 浏览器缓存 1 天
},
)
else:
logger.warning(
f"头像代理获取失败: url={avatar_url[:80]}, status={resp.status_code}"
)
raise AppException(code=404, message="头像获取失败")
except httpx.TimeoutException:
logger.warning(f"头像代理超时: url={avatar_url[:80]}")
raise AppException(code=504, message="头像获取超时")
except Exception as e:
logger.warning(f"头像代理异常: url={avatar_url[:80]}, error={e}")
raise AppException(code=500, message="头像代理失败")