From 38dcd2135280cc179ca97607ff57bdfddc9a1037 Mon Sep 17 00:00:00 2001 From: pulsareonbot Date: Sat, 4 Jul 2026 12:27:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20P2=20=E6=B8=85=E7=90=86=20-=20?= =?UTF-8?q?=E9=97=AD=E7=8E=AF=20check=5Fupdates=20=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E5=9B=9E=E4=BC=A0=20+=20SDK=20=E6=96=87=E6=A1=A3=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20+=20DeviceResponse=20=E9=94=99=E8=AF=AF=E7=95=99?= =?UTF-8?q?=E7=97=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. service_manager.rs check_plugin_updates: 返回 Vec<(id, cur, new)>, 调用处通过 Custom { kind: "plugin_updates" } 广播结果,闭环 HTTP 调用方获取检查结果的链路 (CLAUDE.md:163 第3项, 王浩然风险3 建议) 2. plugin-sdk/src/lib.rs broadcast 文档: 替换误导性的 WifiProvisioned 示例为 StateChanged (当前真正闭环的消息),并新增 Phase 2 占位消息 说明,列出 WifiProvisioned/DeviceEvent/CursorVisibility 三个当前无 生产者的变体,提醒开发者不要为其编写业务逻辑 (CLAUDE.md:163 第1项) 3. src/plugins/device/mod.rs handle_message: DeviceResponse::Error 转换时补 eprintln 留痕,避免设备层故障完全静默 (CLAUDE.md:163 第4项, 王浩然风险3 建议: 至少在一处记录 Error 响应) 未处理项: - 单实例保护 (锁文件/socket 互斥): 需目标机实测锁行为,留给设备端工程师 - unclutter 僵尸 reap: 核查发现 shutdown() 已做 kill+wait+pkill 兜底, 仅 SIGKILL 极端场景残留,不值得加 SIGCHLD handler (过度工程) - CursorVisibility 变体: 删除会破坏示例插件和测试,倾向 Phase 2 统一清理 注: Windows 开发环境缺 dbus/pkg-config, 无法本地 cargo check; 改动经 rust-analyzer 类型检查零诊断, 待目标机验证. --- plugin-sdk/src/lib.rs | 17 ++++++++++---- src/core/service_manager.rs | 46 ++++++++++++++++++++++++++++++------- src/plugins/device/mod.rs | 5 +++- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/plugin-sdk/src/lib.rs b/plugin-sdk/src/lib.rs index 0eca33d..f9a94e1 100644 --- a/plugin-sdk/src/lib.rs +++ b/plugin-sdk/src/lib.rs @@ -518,13 +518,22 @@ impl MessageSender { /// /// # let sender: MessageSender = unimplemented!(); /// sender.broadcast( - /// "network", - /// Message::WifiProvisioned { - /// ssid: "Office-WiFi".to_string(), - /// ip: "192.168.1.8".to_string(), + /// "video", + /// Message::StateChanged { + /// old_state: "idle".to_string(), + /// new_state: "playing".to_string(), /// }, /// ); /// ``` + /// + /// # 注意:Phase 2 占位消息 + /// 以下消息变体当前在核心代码中**无生产者**,仅作为 Phase 2 占位保留。 + /// 插件开发者不应在 `handle_message` 中为其编写业务逻辑(写了也不会被触发), + /// 也不应主动广播它们(核心不消费): + /// - `Message::WifiProvisioned` — WiFi 配网完成事件(待 WiFi 插件补生产者) + /// - `Message::DeviceEvent` — 设备事件上报通道(待 DeviceBackend trait 扩展) + /// - `Message::CursorVisibility` — 光标可见性(实际链路已改走 + /// `DeviceCommand::SetCursorVisible`,此变体仅为向后兼容保留) pub fn broadcast(&self, from: &str, message: Message) { self.send(&Envelope { from: from.to_string(), diff --git a/src/core/service_manager.rs b/src/core/service_manager.rs index 48d6e78..52f0e66 100644 --- a/src/core/service_manager.rs +++ b/src/core/service_manager.rs @@ -730,16 +730,24 @@ impl ServiceManager { Ok(()) } - fn check_plugin_updates(&self) -> Result<()> { + fn check_plugin_updates(&self) -> Result> { let repo = self.plugin_repository()?; let registry = self.plugin_loader()?.load_registry()?; + let mut updates = Vec::new(); for (plugin_id, entry) in ®istry.plugins { match repo.check_update(plugin_id, &entry.active_version)? { - Some(version) => println!( - "[ServiceManager] 插件 '{plugin_id}' 发现可用更新: {} -> {version}", - entry.active_version - ), + Some(version) => { + println!( + "[ServiceManager] 插件 '{plugin_id}' 发现可用更新: {} -> {version}", + entry.active_version + ); + updates.push(( + plugin_id.clone(), + entry.active_version.clone(), + version, + )); + } None => println!( "[ServiceManager] 插件 '{plugin_id}' 已是最新版本 {}", entry.active_version @@ -747,7 +755,7 @@ impl ServiceManager { } } - Ok(()) + Ok(updates) } fn broadcast_plugin_states(&mut self) { @@ -865,8 +873,30 @@ impl ServiceManager { true } "plugin_check_updates" => { - if let Err(error) = self.check_plugin_updates() { - eprintln!("[ServiceManager] plugin_check_updates 失败: {error}"); + match self.check_plugin_updates() { + Ok(updates) => { + // 将检查结果通过 Custom 消息广播,供 HTTP 客户端等消费方获取 + let payload = serde_json::to_string( + &updates + .iter() + .map(|(id, cur, new)| { + serde_json::json!({ + "id": id, + "current_version": cur, + "available_version": new, + }) + }) + .collect::>(), + ) + .unwrap_or_else(|_| "[]".to_string()); + self.broadcast_message(Message::Custom { + kind: "plugin_updates".to_string(), + payload, + }); + } + Err(error) => { + eprintln!("[ServiceManager] plugin_check_updates 失败: {error}"); + } } true } diff --git a/src/plugins/device/mod.rs b/src/plugins/device/mod.rs index e85b08e..4fa3638 100644 --- a/src/plugins/device/mod.rs +++ b/src/plugins/device/mod.rs @@ -121,7 +121,10 @@ impl Plugin for DevicePlugin { let response = match self.backend.handle_command(cmd) { Ok(resp) => resp, Err(e) => { - // 将错误转换为 DeviceResponse::Error + // 将错误转换为 DeviceResponse::Error,并记录日志 + // (核心静态插件目前无人消费 DeviceResponse,故至少在此留痕, + // 避免设备层故障完全静默——王浩然风险3 建议) + eprintln!("[DevicePlugin] 命令处理失败: {e}"); crate::core::message::DeviceResponse::Error(e.to_string()) } };