feat(routes): 迁移个人中心/公开播放/前端错误端点 + 修 QR 异步回归

迁移(/api/v1/*,与 Flask 逐字段对齐):
- user/qr/create、user/qr/check、user/status、user/logout、user/playlists、
  user/playlist、user/collect、user/comment
- api/playlist/play(密钥校验 + 逐曲直链解析:ThreadPoolExecutor(4) → asyncio.gather(4))
- fe_error(前端错误上报,10/分/IP + 字段清洗)

修复我此前引入的异步回归(重要):
- music_api.QRLoginManager 异步化后,main.py 4 处 Flask 同步路由仍当同步调用,
  返回协程 → Flask 侧扫码登录必坏。已改经 run_sync 桥接,实测两栈均返回 SVG。
- web/routers/user.py 同样改为 await。

契约奇偶校验:33 项(新增 user:status/logout/playlists401/playlist401/play:badkey);
ruff All checks passed;pyright 0 errors;冒烟 10/10。
This commit is contained in:
snowy-yang committed 2026-09-26 09:57:42 +08:00
1 parent 91c032a1c3
commit b7616e9831
3 files changed
+455 -4

No files matched your search

+6 -4
View File
@@ -72,6 +72,8 @@ except ImportError as e:
# ============ 框架无关核心层(core/)============
# 常量/配置、运行时状态、纯工具、音频签名已迁出至 core 包,以**同一对象/同一实现**
# 供 Flask 与迁移目标 FastAPI 共用。此处以原名称再导出(别名),既有 250+ 调用点零改动。
# QRLoginManager 等已异步化的上游方法,Flask 同步路由经 run_sync 桥接调用。
from core.async_bridge import run_sync
from core.config import (
APP_VERSION,
BATCH_ZIP_CONCURRENCY,
@@ -2306,7 +2308,7 @@ def admin_qr_create():
return auth_error
client = _admin_client()
unikey = client.qr_manager.generate_qr_key()
unikey = run_sync(client.qr_manager.generate_qr_key())
if not unikey:
return APIResponse.error("生成二维码失败", 500)
@@ -2343,7 +2345,7 @@ def admin_qr_check():
return APIResponse.error("缺少 unikey 参数")
client = _admin_client()
code, cookie_dict = client.qr_manager.check_qr_login(unikey)
code, cookie_dict = run_sync(client.qr_manager.check_qr_login(unikey))
if code == 803:
# 登录成功,自动保存 Cookie
@@ -2462,7 +2464,7 @@ def user_qr_create():
"""生成个人登录二维码(返回 SVG),游客无需密码"""
try:
client = _user_client()
unikey = client.qr_manager.generate_qr_key()
unikey = run_sync(client.qr_manager.generate_qr_key())
if not unikey:
return APIResponse.error("生成二维码失败", 500)
@@ -2496,7 +2498,7 @@ def user_qr_check():
return APIResponse.error("缺少 unikey 参数")
client = _user_client()
code, cookie_dict = client.qr_manager.check_qr_login(unikey)
code, cookie_dict = run_sync(client.qr_manager.check_qr_login(unikey))
if code == 803:
musu_u = cookie_dict.get('MUSIC_U', '')