From 09faa2874aae78065aae2424161484bb5ed88149 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 2 Aug 2026 13:32:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=88=A0=E9=99=A4=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=EF=BC=9AonRemove=20=E7=94=9F=E5=91=BD=E5=91=A8?= =?UTF-8?q?=E6=9C=9F=EF=BC=88=E4=BB=85=E5=8D=B8=E8=BD=BD=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=EF=BC=8C=E9=87=8D=E8=BD=BD=E4=B8=8D=E8=A7=A6=E5=8F=91=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 公共 SDK(third_party/homeagent-sdk):RegisterOnRemoveHandler/RunOnRemoveHandlers (后注册先执行、幂等);内部 SDK PluginManager 接口新增 RemovePlugin - registry.RemovePlugin:runStopHandlers → Stop → 清理 plugins/sdkRefs/instances → runOnRemoveHandlers → toolCleaner.UnregisterPluginTools → cfgReg.RemoveDisabledPlugin - pluginmgr.removePlugin 先调 Registry.RemovePlugin 再删目录 - 配置清理:PluginSettings.Remove(key)(内部 SettingsAPI + settingsImpl) - 演示:timer 插件 onRemove 删 max_duration 键;plugindev 模板同步 onRemove 示例; SDK example/calendar cleanupData 删 events.json;manager plugins/uninstall 先停通道 --- internal/config/registry.go | 8 +++++ internal/plugin/registry.go | 40 +++++++++++++++++++++++++ internal/plugins/pluginmgr/plugin.go | 7 +++++ internal/plugins/timer/plugin.go | 6 ++++ internal/sdk/plugin.go | 3 ++ internal/sdk/settings.go | 2 ++ internal/sdk/settings_impl.go | 4 +++ third_party/homeagent-sdk/sdk/plugin.go | 28 +++++++++++++++++ 8 files changed, 98 insertions(+) diff --git a/internal/config/registry.go b/internal/config/registry.go index 6965516..7ef61ef 100644 --- a/internal/config/registry.go +++ b/internal/config/registry.go @@ -745,6 +745,14 @@ func (p *PluginSettings) Set(key string, value interface{}) error { return err } +// Remove 删除插件配置中的单个键(用于插件删除时的自身配置清理)。 +func (p *PluginSettings) Remove(key string) error { + p.registry.mu.Lock() + defer p.registry.mu.Unlock() + _, err := p.registry.db.Exec(fmt.Sprintf(`DELETE FROM %s WHERE key = ?`, p.table), key) + return err +} + func (p *PluginSettings) List(prefix string) ([]string, error) { p.registry.mu.RLock() defer p.registry.mu.RUnlock() diff --git a/internal/plugin/registry.go b/internal/plugin/registry.go index 0e7e8db..8d75fc0 100644 --- a/internal/plugin/registry.go +++ b/internal/plugin/registry.go @@ -390,6 +390,13 @@ func (r *Registry) runStopHandlers(name string) { } } +// runOnRemoveHandlers 执行插件注册的删除清理回调(SDK 层),插件 Stop() 之后、从注册表移除前执行。 +func (r *Registry) runOnRemoveHandlers(name string) { + if sdk, ok := r.sdkRefs[name]; ok { + sdk.RunOnRemoveHandlers() + } +} + func (r *Registry) StopAll() { r.mu.Lock() defer r.mu.Unlock() @@ -580,6 +587,39 @@ func (r *Registry) DisablePlugin(name, by string) error { func (r *Registry) EnablePlugin(name string) error { return r.Enable(name) } +// RemovePlugin 卸载插件:先停止(stop handlers + Stop),再执行插件注册的 onRemove +// 回调(删除专用,重载不触发),最后从注册表移除并清理禁用/工具注册。 +// 插件目录的物理删除由调用方(pluginmgr)负责。 +func (r *Registry) RemovePlugin(name string) error { + r.mu.Lock() + p, ok := r.plugins[name] + if ok { + r.runStopHandlers(name) + if err := p.Stop(); err != nil { + log.Printf("[plugin] stop %s for remove: %v", name, err) + } + delete(r.plugins, name) + delete(r.sdkRefs, name) + for i, inst := range r.instances { + if inst.Name() == name { + r.instances = append(r.instances[:i], r.instances[i+1:]...) + break + } + } + } + r.runOnRemoveHandlers(name) + r.mu.Unlock() + + if r.toolCleaner != nil { + r.toolCleaner.UnregisterPluginTools(name) + } + if r.cfgReg != nil { + r.cfgReg.RemoveDisabledPlugin(name) + } + log.Printf("[plugin] removed: %s", name) + return nil +} + func (r *Registry) ReloadPlugins() (string, error) { return r.Reload(r.plgDir) } // ListKnown 返回所有已知插件(已加载 + 已禁用 + 已安装但未加载)。 diff --git a/internal/plugins/pluginmgr/plugin.go b/internal/plugins/pluginmgr/plugin.go index 2adc1b4..7b5bd6f 100644 --- a/internal/plugins/pluginmgr/plugin.go +++ b/internal/plugins/pluginmgr/plugin.go @@ -456,6 +456,13 @@ func (p *Plugin) removePlugin(name string) (interface{}, error) { return map[string]interface{}{"error": "plugin not found", "name": name}, nil } + // 先经内核卸载:停止插件(stop handlers + Stop)并执行插件注册的 onRemove 回调 + if p.sdk != nil && p.sdk.PluginMgr() != nil { + if err := p.sdk.PluginMgr().RemovePlugin(name); err != nil { + log.Printf("[pluginmgr] RemovePlugin %s: %v", name, err) + } + } + if err := os.RemoveAll(dir); err != nil { return map[string]interface{}{"error": err.Error()}, nil } diff --git a/internal/plugins/timer/plugin.go b/internal/plugins/timer/plugin.go index 797f1fa..bedcf25 100644 --- a/internal/plugins/timer/plugin.go +++ b/internal/plugins/timer/plugin.go @@ -44,6 +44,12 @@ func (p *Plugin) Start(s *sdk.PluginSDK) error { p.maxDur = 24 * time.Hour // 停止清理(取消倒计时)交由 stop handler:内核在调用 Stop() 之前执行。 s.RegisterStopHandler(func() { close(p.stopCh) }) + // 删除清理:移除插件自身配置(删除专用回调,重载不触发)。 + s.RegisterOnRemoveHandler(func() { + if err := s.Settings().Remove("max_duration"); err != nil { + log.Printf("[timer] onRemove cleanup: %v", err) + } + }) s.Settings().RegisterDef(sdk.ConfigDef{ Key: "max_duration", Type: "string", DisplayName: "最大定时时长", Description: "允许设置的最大定时时长,例如 24h, 7d, 1h(默认 24h)", diff --git a/internal/sdk/plugin.go b/internal/sdk/plugin.go index 83c22ab..3d22032 100644 --- a/internal/sdk/plugin.go +++ b/internal/sdk/plugin.go @@ -69,6 +69,9 @@ type PluginManager interface { IsPluginDisabled(name string) bool DisablePlugin(name, by string) error EnablePlugin(name string) error + // RemovePlugin 卸载插件:先停止(stop handlers + Stop),再执行插件注册的 + // onRemove 回调(RegisterOnRemoveHandler),最后从注册表移除。目录删除由调用方负责。 + RemovePlugin(name string) error ReloadPlugins() (string, error) PluginMetas() map[string]PluginMeta PluginDir() string diff --git a/internal/sdk/settings.go b/internal/sdk/settings.go index 29b5df2..ef81f62 100644 --- a/internal/sdk/settings.go +++ b/internal/sdk/settings.go @@ -9,6 +9,8 @@ type SettingsAPI interface { DefsCore(prefix string) []*ConfigDef // DefsPlugin 返回另一个插件的配置定义。 DefsPlugin(plugin, prefix string) []*ConfigDef + // Remove 删除本插件配置中的单个键(插件删除时清理自身配置用)。 + Remove(key string) error } type ConfigDef = pubsdk.ConfigDef diff --git a/internal/sdk/settings_impl.go b/internal/sdk/settings_impl.go index 600c5a6..8a1b919 100644 --- a/internal/sdk/settings_impl.go +++ b/internal/sdk/settings_impl.go @@ -24,6 +24,10 @@ func (s *settingsImpl) Set(key string, value interface{}) error { if s.reg == nil { return nil } return s.reg.PluginConfig(s.pluginName).Set(key, value) } +func (s *settingsImpl) Remove(key string) error { + if s.reg == nil { return nil } + return s.reg.PluginConfig(s.pluginName).Remove(key) +} func (s *settingsImpl) List(prefix string) ([]string, error) { if s.reg == nil { return nil, nil } return s.reg.PluginConfig(s.pluginName).List(prefix) diff --git a/third_party/homeagent-sdk/sdk/plugin.go b/third_party/homeagent-sdk/sdk/plugin.go index 3a446c0..1378eaf 100644 --- a/third_party/homeagent-sdk/sdk/plugin.go +++ b/third_party/homeagent-sdk/sdk/plugin.go @@ -205,6 +205,9 @@ type PluginSDK struct { stopMu sync.Mutex stopHandlers []func() + + removeMu sync.Mutex + removeHandlers []func() } // New creates a PluginSDK with the given dependencies. @@ -408,3 +411,28 @@ func (s *PluginSDK) RunStopHandlers() { handlers[i]() } } + +// RegisterOnRemoveHandler 注册插件被删除(卸载)时的清理回调。 +// 注册的 handler 会在插件目录被移除前按"后注册先执行"的顺序调用, +// 适用于清理外部资源、删除配置表、下线状态等删除后处理。 +// 可注册多个;执行后清空(一次删除只执行一次)。 +func (s *PluginSDK) RegisterOnRemoveHandler(fn func()) { + if fn == nil { + return + } + s.removeMu.Lock() + s.removeHandlers = append(s.removeHandlers, fn) + s.removeMu.Unlock() +} + +// RunOnRemoveHandlers 执行全部已注册的 onRemove handler(后注册先执行,执行后清空,幂等)。 +// 由内核在卸载插件(registry.RemovePlugin)时、插件 Stop() 之后执行。 +func (s *PluginSDK) RunOnRemoveHandlers() { + s.removeMu.Lock() + handlers := append([]func(){}, s.removeHandlers...) + s.removeHandlers = nil + s.removeMu.Unlock() + for i := len(handlers) - 1; i >= 0; i-- { + handlers[i]() + } +}