修复wechat_ipad启动竞态与Dashboard抢跑问题

This commit is contained in:
liuwei
2026-05-07 10:55:34 +08:00
parent 0051574a1e
commit c628afc530
3 changed files with 79 additions and 6 deletions

View File

@@ -32,6 +32,28 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '.
class DashboardServer:
"""统计看板服务器"""
@property
def client(self) -> WechatAPIClient | None:
"""动态返回当前 robot 上挂载的微信客户端。
说明:
1. Dashboard 进程通常比 wechat 登录完成更早启动,因此不能只在构造时拍平一份固定引用;
2. 这里每次访问都优先回看 `robot.ipad_bot`,可自动吃到后续 provider 初始化完成后的真实对象;
3. 同时保留 `_client` 兜底,兼容未来如果需要在测试里手工注入 mock client 的场景。
"""
robot = getattr(self, "robot", None)
if robot is not None:
dynamic_client = getattr(robot, "ipad_bot", None)
if dynamic_client is not None:
self._client = dynamic_client
return dynamic_client
return getattr(self, "_client", None)
@client.setter
def client(self, value: WechatAPIClient | None) -> None:
"""允许初始化或测试场景显式覆写当前 client。"""
self._client = value
def __init__(self, host: str = None, port: int = None,
username: str = None, password: str = None,
robot_instance=None):
@@ -93,8 +115,12 @@ class DashboardServer:
self.contact_manager = robot_instance.contact_manager
self.plugin_manager = robot_instance.plugin_manager
self.plugin_registry = robot_instance.plugin_registry
self.client: WechatAPIClient = robot_instance.ipad_bot
self.robot = robot_instance
# Dashboard 启动时不再强绑一个“拍平后的 client 引用”:
# 1. wechat 线程可能还在 provider 初始化或登录流程中;
# 2. 这里只先记录一个初始值,后续统一通过 `client` 属性动态读取 robot.ipad_bot
# 3. 这样后台页在启动竞态下不会因为抢跑而持有一个永远为 None 的旧引用。
self.client = getattr(robot_instance, "ipad_bot", None)
self.member_context_plugin = self.plugin_manager.plugins.get("成员交互摘要")
self.member_context_service = getattr(self.member_context_plugin, "service", None)