优化864登录三态展示并更新适配路线文档

This commit is contained in:
liuwei
2026-05-07 12:28:59 +08:00
parent 539bebd58b
commit 8579b7ec27
5 changed files with 194 additions and 3 deletions

View File

@@ -20,6 +20,10 @@ def _serialize_login_qr_state(server) -> dict:
"logged_in": False,
"active": False,
"status": "unavailable",
"provider_name": "",
"provider_stage": "status_unavailable",
"connection_ready": False,
"login_required": False,
"status_text": "机器人运行态暂不可用",
"current": {},
"history": [],
@@ -31,6 +35,10 @@ def _serialize_login_qr_state(server) -> dict:
"logged_in": bool(state.get("logged_in", False)),
"active": bool(state.get("active", False)),
"status": str(state.get("status", "idle") or "idle"),
"provider_name": str(state.get("provider_name", "") or ""),
"provider_stage": str(state.get("provider_stage", "bootstrap") or "bootstrap"),
"connection_ready": bool(state.get("connection_ready", False)),
"login_required": bool(state.get("login_required", False)),
"status_text": str(state.get("status_text", "尚未进入扫码登录流程") or "尚未进入扫码登录流程"),
"runtime_running": bool(state.get("runtime_running", False)),
"wxid": str(state.get("wxid", "") or ""),

View File

@@ -26,7 +26,7 @@
<template slot="title">
<div class="login-qr-banner__content">
<div>
<div class="login-qr-banner__title">当前微信未登录,首页已进入扫码引导模式</div>
<div class="login-qr-banner__title">{% raw %}{{ loginQrBannerTitle }}{% endraw %}</div>
<div class="login-qr-banner__desc">
{% raw %}{{ loginQrDialog.status_text || '请使用手机微信扫码登录当前环境。' }}{% endraw %}
</div>
@@ -575,6 +575,10 @@
logged_in: false,
active: false,
status: 'idle',
provider_name: '',
provider_stage: 'bootstrap',
connection_ready: false,
login_required: false,
status_text: '尚未进入扫码登录流程',
current: {},
history: [],
@@ -622,8 +626,23 @@
return !this.loginQrDialog.logged_in;
},
loginQrStatusTone() {
if (this.loginQrDialog.provider_stage === 'connection_pending') {
return 'soft';
}
if (this.loginQrDialog.provider_stage === 'login_required') {
return 'danger';
}
return this.mapLoginQrTone(this.loginQrDialog.status);
},
loginQrBannerTitle() {
if (this.loginQrDialog.provider_stage === 'connection_pending') {
return '当前 864 服务连接尚未建立,首页已进入登录准备模式';
}
if (this.loginQrDialog.provider_stage === 'login_required') {
return '当前微信登录态已失效,首页已进入重新登录模式';
}
return '当前微信未登录,首页已进入扫码引导模式';
},
loginQrStatusText() {
const toneMap = {
waiting: '等待扫码',
@@ -633,9 +652,22 @@
idle: '等待登录流程',
unavailable: '状态暂不可用'
};
if (this.loginQrDialog.provider_stage === 'connection_pending') {
return '等待建立连接';
}
if (this.loginQrDialog.provider_stage === 'login_required') {
return '需要重新登录';
}
return toneMap[this.loginQrDialog.status] || '等待登录流程';
},
loginQrSourceText() {
const providerName = String(this.loginQrDialog.provider_name || '').toLowerCase();
if (providerName === 'server_864' || providerName === '864') {
if (this.loginQrDialog.provider_stage === 'connection_pending') {
return '864 服务端准备中';
}
return '864 服务端登录';
}
const source = this.loginQrCurrent.login_source;
if (source === 'awaken') {
return '缓存唤醒登录';
@@ -652,6 +684,12 @@
if (this.loginQrDialog.status === 'expired') {
return '已过期,等待刷新';
}
if (this.loginQrDialog.provider_stage === 'connection_pending') {
return '等待服务端准备';
}
if (this.loginQrDialog.provider_stage === 'login_required' && !this.loginQrCurrent.uuid) {
return '等待新二维码';
}
if (!this.loginQrCurrent.uuid) {
return '等待生成';
}
@@ -866,6 +904,10 @@
this.loginQrCountdownSeconds = 0;
return;
}
if (this.loginQrDialog.provider_stage === 'connection_pending') {
this.loginQrCountdownSeconds = 0;
return;
}
if (this.loginQrCountdownSeconds > 0) {
this.loginQrCountdownSeconds -= 1;
return;

View File

@@ -33,12 +33,14 @@
- 已在 [robot.py](/d:/learn/abot/robot.py:1) 中为 864 增加登录态硬隔离,默认不再回读 855 的历史 `config.toml` 与动态字段
- 已基于真实 864 服务联调修正首批路由差异:二维码返回结构、联系人详情路由、群公告路由、二维码有效期倒计时同步
- 已确认 864 的消息发送类接口在“尚未建立连接对象”时会优先返回“该链接不存在”,这与登录态接口返回“需要重新登录”属于不同阶段
- 已开始把 864 的“等待服务端准备 / 需要重新登录 / 等待扫码”三态显式回传给 Dashboard避免首页把不同问题混成一类未登录提示
当前尚未完成的关键项:
- 855 provider 仍需完成一轮“当前项目实际依赖接口”的可上线回归验证
- 855 provider 仍需继续梳理“项目真实使用到的接口覆盖面”,确认是否还有遗漏能力未纳入 provider 目录
- 864 provider 仍需完成真机联调,尤其是消息字段归一化、视频发送、名片发送等增强能力
- Dashboard 仍需继续做少量多 provider 登录文案收敛,但登录状态结构已具备扩展空间
因此,当前状态可以定义为:
@@ -440,6 +442,7 @@ wechat_ipad/
- 已实现 HTTP 轮询消息同步
- 已实现联系人、群资料、当前账号资料、朋友圈基础接口
- 已保留与 855 尽量一致的对外方法名,便于 `Robot` 无感切换
- 已补一层登录阶段探测,首页可以直接区分“等待服务端准备”“需要重新登录”“等待扫码”
当前仍待补强的范围:

View File

@@ -446,6 +446,10 @@ class Robot:
"logged_in": False,
"active": False,
"status": "idle",
"provider_name": "",
"provider_stage": "bootstrap",
"connection_ready": False,
"login_required": False,
"status_text": "尚未进入扫码登录流程",
"current": {},
"history": [],
@@ -485,10 +489,21 @@ class Robot:
with self._ipad_login_qr_lock:
login_state_flag = bool(self.ipad_login_qr_state.get("logged_in", False))
qr_status = str(self.ipad_login_qr_state.get("status", "idle") or "idle")
provider_name = str(
self.ipad_login_qr_state.get("provider_name", self.ipad_config.get("server_type", "") if self.ipad_config else "")
or ""
).strip()
provider_stage = str(self.ipad_login_qr_state.get("provider_stage", "bootstrap") or "bootstrap").strip()
connection_ready = bool(self.ipad_login_qr_state.get("connection_ready", False))
login_required = bool(self.ipad_login_qr_state.get("login_required", False))
state = {
"logged_in": bool(self.wxid) or login_state_flag or qr_status in {"confirmed", "logged_in"},
"active": bool(self.ipad_login_qr_state.get("active", False)),
"status": qr_status,
"provider_name": provider_name,
"provider_stage": provider_stage,
"connection_ready": connection_ready,
"login_required": login_required,
"status_text": str(
self.ipad_login_qr_state.get("status_text", "尚未进入扫码登录流程") or "尚未进入扫码登录流程"
),
@@ -520,6 +535,13 @@ class Robot:
status = str((payload or {}).get("status", "waiting") or "waiting").strip() or "waiting"
status_text = str((payload or {}).get("status_text", "等待扫码登录") or "等待扫码登录").strip()
login_source = str((payload or {}).get("login_source", "fresh_qr") or "fresh_qr").strip()
provider_name = str(
(payload or {}).get("provider_name", self.ipad_config.get("server_type", "") if self.ipad_config else "")
or ""
).strip()
provider_stage = str((payload or {}).get("provider_stage", "waiting_scan") or "waiting_scan").strip()
connection_ready = bool((payload or {}).get("connection_ready", False))
login_required = bool((payload or {}).get("login_required", True))
expires_in = (payload or {}).get("expires_in")
expires_in = None if expires_in in (None, "") else max(0, int(expires_in))
current_record = {
@@ -530,6 +552,10 @@ class Robot:
"status": status,
"status_text": status_text,
"login_source": login_source,
"provider_name": provider_name,
"provider_stage": provider_stage,
"connection_ready": connection_ready,
"login_required": login_required,
"updated_at": now_ts,
"updated_at_text": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now_ts)),
}
@@ -559,6 +585,10 @@ class Robot:
"logged_in": False,
"active": status != "confirmed",
"status": status,
"provider_name": provider_name,
"provider_stage": provider_stage,
"connection_ready": connection_ready,
"login_required": login_required,
"status_text": status_text,
"current": current_record,
"history": history_records,
@@ -571,6 +601,13 @@ class Robot:
status = str((payload or {}).get("status", "idle") or "idle").strip() or "idle"
status_text = str((payload or {}).get("status_text", "登录流程已结束") or "登录流程已结束").strip()
cleared_uuid = str((payload or {}).get("uuid", "") or "").strip()
provider_name = str(
(payload or {}).get("provider_name", self.ipad_config.get("server_type", "") if self.ipad_config else "")
or ""
).strip()
provider_stage = str((payload or {}).get("provider_stage", "logged_in") or "logged_in").strip()
connection_ready = bool((payload or {}).get("connection_ready", True))
login_required = bool((payload or {}).get("login_required", False))
with self._ipad_login_qr_lock:
history_records = list(self.ipad_login_qr_state.get("history", []) or [])
@@ -586,6 +623,10 @@ class Robot:
"logged_in": status in {"confirmed", "logged_in"} or bool(self.wxid),
"active": False,
"status": status,
"provider_name": provider_name,
"provider_stage": provider_stage,
"connection_ready": connection_ready,
"login_required": login_required,
"status_text": status_text,
"current": {},
"history": history_records[:3],

View File

@@ -103,12 +103,31 @@ class Server864RuntimeMixin:
await self._refresh_identity_from_profile(logger=logger)
await self._safe_callback(
on_login_qr_cleared,
{"status": "logged_in", "status_text": "已检测到现有登录态"},
{
"status": "logged_in",
"status_text": "已检测到现有登录态",
"provider_name": "server_864",
"provider_stage": "logged_in",
"connection_ready": True,
"login_required": False,
},
logger=logger,
callback_name="on_login_qr_cleared",
)
return
# 先探测一次 864 当前阶段,让 Dashboard 能直接区分“等服务端准备”和“需要扫码”:
# 1. 864 的未登录态并不只有一种,部分场景其实是远端连接对象还没建好;
# 2. 若首页始终只显示“未登录”,运维很难判断下一步是等服务端还是去扫码;
# 3. 这里把差异压缩成轻量阶段字段,供前端直接展示,不改动核心登录流程。
login_stage_snapshot = await self._probe_login_stage()
await self._safe_callback(
on_login_qr_update,
login_stage_snapshot,
logger=logger,
callback_name="on_login_qr_update",
)
uuid, url = await self.get_qr_code(print_qr=True)
scan_url = f"http://weixin.qq.com/x/{uuid}" if uuid else ""
await self._safe_callback(
@@ -121,6 +140,10 @@ class Server864RuntimeMixin:
"status": "waiting",
"status_text": "等待扫码登录",
"login_source": "fresh_qr",
"provider_name": "server_864",
"provider_stage": "waiting_scan",
"connection_ready": False,
"login_required": True,
},
logger=logger,
callback_name="on_login_qr_update",
@@ -131,7 +154,15 @@ class Server864RuntimeMixin:
if is_logged_in:
await self._safe_callback(
on_login_qr_cleared,
{"status": "confirmed", "status_text": "扫码登录成功", "uuid": uuid},
{
"status": "confirmed",
"status_text": "扫码登录成功",
"uuid": uuid,
"provider_name": "server_864",
"provider_stage": "logged_in",
"connection_ready": True,
"login_required": False,
},
logger=logger,
callback_name="on_login_qr_cleared",
)
@@ -158,6 +189,10 @@ class Server864RuntimeMixin:
"status": "waiting",
"status_text": str(login_status.get("msg") or login_status.get("loginState") or "等待扫码登录"),
"login_source": "fresh_qr",
"provider_name": "server_864",
"provider_stage": "waiting_scan" if uuid else "login_required",
"connection_ready": False,
"login_required": True,
},
logger=logger,
callback_name="on_login_qr_update",
@@ -180,6 +215,10 @@ class Server864RuntimeMixin:
"status": "waiting",
"status_text": "二维码已刷新,等待扫码登录",
"login_source": "refresh_qr",
"provider_name": "server_864",
"provider_stage": "waiting_scan",
"connection_ready": False,
"login_required": True,
},
logger=logger,
callback_name="on_login_qr_update",
@@ -196,6 +235,64 @@ class Server864RuntimeMixin:
logger=logger,
)
async def _probe_login_stage(self) -> dict[str, Any]:
"""探测 864 当前登录阶段,供 Dashboard 展示更准确的运维状态。"""
default_payload = {
"status": "waiting",
"status_text": "等待扫码登录",
"login_source": "fresh_qr",
"provider_name": "server_864",
"provider_stage": "login_required",
"connection_ready": False,
"login_required": True,
}
try:
login_status = await self.get_login_status(auto_login=False)
except Exception as e:
error_message = str(e).strip()
if "该链接不存在" in error_message:
return {
**default_payload,
"status_text": "864 服务端连接尚未建立,等待创建登录连接",
"provider_stage": "connection_pending",
"login_required": False,
}
if "重新登录" in error_message or "未登录" in error_message:
return {
**default_payload,
"status_text": error_message or "当前登录态已失效,等待重新扫码登录",
}
return {
**default_payload,
"status": "unavailable",
"status_text": error_message or "864 登录状态暂不可用",
"provider_stage": "status_unavailable",
}
if isinstance(login_status, dict):
login_message = str(
login_status.get("msg")
or login_status.get("message")
or login_status.get("loginState")
or ""
).strip()
if "在线" in login_message or str(login_status.get("loginState", "")).strip().lower() == "online":
return {
"status": "logged_in",
"status_text": "已检测到现有登录态",
"login_source": "runtime_state",
"provider_name": "server_864",
"provider_stage": "logged_in",
"connection_ready": True,
"login_required": False,
}
if "重新登录" in login_message or "未登录" in login_message:
return {
**default_payload,
"status_text": login_message or "当前登录态已失效,等待重新扫码登录",
}
return default_payload
async def _wait_init_ready(self, *, logger) -> None:
"""等待 864 server 侧初始化完成。"""
for _ in range(30):