收敛864登录态切换异常与日志噪音

This commit is contained in:
liuwei
2026-05-07 15:47:14 +08:00
parent 41a2bd9358
commit 0b59bc4a0a
3 changed files with 79 additions and 4 deletions

View File

@@ -64,7 +64,19 @@ class Server864APIClientBase:
params=merged_params,
json=json_body,
) as response:
payload = await response.json(content_type=None)
try:
payload = await response.json(content_type=None)
except Exception:
raw_text = await response.text()
# 864 少数接口会直接返回纯文本而不是标准 DTO
# 1. 当前已在 `LogOut` 链路里碰到这种情况,旧逻辑会先炸 JSON 解析,再把真正状态信息吞掉;
# 2. 对 2xx 响应来说,这通常只是“接口风格不统一”,不应该直接视为致命协议错误;
# 3. 因此这里统一包成兼容 DTO保留原始文本给上层按需处理。
payload = {
"Code": 200 if response.status < 400 else response.status,
"Data": None,
"Text": str(raw_text or "").strip(),
}
return self._validate_payload(payload)
async def _request_data(
@@ -96,6 +108,12 @@ class Server864APIClientBase:
return payload
message = str(payload.get("Text") or payload.get("Message") or "server_864 请求失败").strip()
# 864 某些登录接口会用非 200 编码表达“当前 key 已经在线”:
# 1. 对二维码申请链路来说,这更接近一种状态回执,而不是硬失败;
# 2. 如果这里直接抛异常,上层就会把“已经在线”误判成致命错误并退出线程;
# 3. 因此先保留 payload 原样放行,让 runtime 再决定是复用现有登录态还是继续走扫码。
if "该链接已绑定微信号" in message and "在线状态良好" in message:
return payload
lowered_message = message.lower()
if any(keyword in lowered_message for keyword in ("重新登录", "已退出登录", "离线", "账号需要重新登录")):
raise UserLoggedOut(message)

View File

@@ -195,7 +195,55 @@ class Server864RuntimeMixin:
callback_name="on_login_qr_update",
)
uuid, url = await self.get_qr_code(print_qr=True, login_qr_api=login_qr_api, login_way=login_way)
try:
uuid, url = await self.get_qr_code(print_qr=True, login_qr_api=login_qr_api, login_way=login_way)
except Exception as e:
qr_error_message = str(e).strip()
if "该链接已绑定微信号" in qr_error_message and "在线状态良好" in qr_error_message:
# 当 864 服务端明确提示“当前 key 已在线”时,优先尝试直接复用现有登录态:
# 1. 这通常出现在切换二维码模式过快、旧连接尚未完全退出的瞬间;
# 2. 若此时直接抛错,线程会把“已经在线”误判成获取二维码失败;
# 3. 因此这里先转成“尝试接管现有会话”,成功后直接收口,不再强制继续申请新二维码。
identity_ready = await self._refresh_identity_from_profile(logger=logger)
if identity_ready:
ipad_config["wxid"] = self.wxid
ipad_config["login_time"] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
self._save_runtime_state(
state_path=state_path,
state_payload={"wxid": self.wxid, "login_time": ipad_config["login_time"]},
logger=logger,
)
await self._safe_callback(
on_login_qr_cleared,
{
"status": "logged_in",
"status_text": "864 服务端已存在可用登录态,已自动复用当前会话",
"provider_name": "server_864",
"provider_stage": "logged_in",
"connection_ready": True,
"login_required": False,
},
logger=logger,
callback_name="on_login_qr_cleared",
)
return
await self._safe_callback(
on_login_qr_update,
{
"status": "unavailable",
"status_text": self._normalize_login_runtime_message(qr_error_message),
"login_source": "fresh_qr",
"provider_name": "server_864",
"provider_stage": "status_unavailable",
"connection_ready": False,
"login_required": True,
"login_qr_api": login_qr_api,
"login_way": login_way,
},
logger=logger,
callback_name="on_login_qr_update",
)
raise
scan_url = f"http://weixin.qq.com/x/{uuid}" if uuid else ""
effective_time = 0
raw_state = 0

View File

@@ -1,5 +1,7 @@
from loguru import logger
from wechat_ipad.errors import UserLoggedOut
from wechat_ipad.providers.server_864.base import Server864APIClientBase
@@ -59,6 +61,13 @@ class UserMixin(Server864APIClientBase):
return True
await self.get_profile()
return True
except Exception as e:
logger.error("server_864 is_logged_in:{}", e)
except UserLoggedOut as e:
# “未登录 / 需要重新登录”是 864 登录引导中的常规状态,不应该长期污染 error 日志:
# 1. 当前首页会主动引导扫码,这类返回本质上只是“当前还没登录完成”;
# 2. 若仍按 error 记录,运维排查时很难分清真正异常和正常登录态切换;
# 3. 因此这里降级为 info只保留可读状态文本。
logger.info("server_864 is_logged_in:{}", e)
return False
except Exception as e:
logger.warning("server_864 is_logged_in:{}", e)
return False