增强插件系统:优化插件管理和动态插件实现
- 在插件管理器中引入 DynamicPlugin 结构体,支持动态加载和管理插件,提升插件的灵活性和可扩展性。 - 更新插件接口,添加插件名称、版本、描述、作者、类型和启用状态的获取和设置方法,增强插件信息的管理能力。 - 修改现有插件实现,确保兼容新的动态插件结构,提升插件的统一性和可维护性。 - 更新示例程序,展示如何使用新的动态插件功能,提升用户体验。 此更新提升了插件系统的灵活性和可扩展性,便于开发者更好地管理和使用插件功能。
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
*.so
|
1
examples/plugin/admin/plugins/plugins.json
Normal file
1
examples/plugin/admin/plugins/plugins.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
@@ -405,6 +405,19 @@ func APIPluginsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
plugins := pm.GetPluginInfos()
|
plugins := pm.GetPluginInfos()
|
||||||
|
fmt.Printf("API请求: 找到 %d 个插件\n", len(plugins))
|
||||||
|
|
||||||
|
// 打印每个插件的信息
|
||||||
|
for i, p := range plugins {
|
||||||
|
fmt.Printf("插件 %d: 名称=%s, 版本=%s, 作者=%s, 类型=%s, 启用=%v\n",
|
||||||
|
i+1, p.Name, p.Version, p.Author, p.Type, p.Enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有插件,返回一个空数组而不是 null
|
||||||
|
if plugins == nil {
|
||||||
|
plugins = []plugin.PluginInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(plugins); err != nil {
|
if err := json.NewEncoder(w).Encode(plugins); err != nil {
|
||||||
http.Error(w, fmt.Sprintf("编码JSON失败: %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("编码JSON失败: %v", err), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
@@ -427,7 +440,7 @@ func getPluginInfo(name string) (*plugin.PluginInfo, map[string]interface{}) {
|
|||||||
// 插件目录
|
// 插件目录
|
||||||
var pluginsDir = "./plugins"
|
var pluginsDir = "./plugins"
|
||||||
|
|
||||||
func main3() {
|
func main() {
|
||||||
// 创建插件管理器
|
// 创建插件管理器
|
||||||
pm = plugin.NewPluginManager(pluginsDir)
|
pm = plugin.NewPluginManager(pluginsDir)
|
||||||
|
|
||||||
|
@@ -134,16 +134,6 @@ type PluginInfo struct {
|
|||||||
Config map[string]interface{} ` + "`json:\"config,omitempty\"`" + `
|
Config map[string]interface{} ` + "`json:\"config,omitempty\"`" + `
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginType 插件类型
|
|
||||||
type PluginType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
PluginTypeGeneral PluginType = "General"
|
|
||||||
PluginTypeStorage PluginType = "Storage"
|
|
||||||
PluginTypeUtils PluginType = "Utils"
|
|
||||||
PluginTypeNetwork PluginType = "Network"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OperationInfo 操作信息
|
// OperationInfo 操作信息
|
||||||
type OperationInfo struct {
|
type OperationInfo struct {
|
||||||
Name string ` + "`json:\"name\"`" + `
|
Name string ` + "`json:\"name\"`" + `
|
||||||
|
@@ -236,7 +236,7 @@ func (p *LogPlugin) Log(message string) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// 创建插件管理器
|
// 创建插件管理器
|
||||||
pluginsDir := "./plugins"
|
pluginsDir := "../admin/plugins"
|
||||||
pm := plugin.NewPluginManager(pluginsDir)
|
pm := plugin.NewPluginManager(pluginsDir)
|
||||||
|
|
||||||
// 检查当前系统是否支持动态加载插件
|
// 检查当前系统是否支持动态加载插件
|
||||||
|
@@ -3,7 +3,6 @@ package plugin
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
@@ -978,36 +977,445 @@ func (pm *PluginManager) loadPlugin(path string) error {
|
|||||||
return fmt.Errorf("查找Plugin变量失败: %v", err)
|
return fmt.Errorf("查找Plugin变量失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 类型断言
|
// 使用反射检查插件对象
|
||||||
plugin, ok := symPlugin.(Plugin)
|
pluginValue := reflect.ValueOf(symPlugin)
|
||||||
if !ok {
|
fmt.Printf("加载插件: %s, 类型: %s\n", path, pluginValue.Type())
|
||||||
return errors.New("插件类型错误")
|
|
||||||
|
// 查找基本字段或方法
|
||||||
|
var pluginName, pluginVersion, pluginAuthor, pluginDesc string
|
||||||
|
var pluginType PluginType = PluginTypeGeneral
|
||||||
|
var enabled = true
|
||||||
|
|
||||||
|
// 直接检查方法
|
||||||
|
// 尝试通过方法获取信息
|
||||||
|
nameFound := false
|
||||||
|
if nameMethod := pluginValue.MethodByName("Name"); nameMethod.IsValid() {
|
||||||
|
results := nameMethod.Call(nil)
|
||||||
|
if len(results) > 0 && results[0].Kind() == reflect.String {
|
||||||
|
pluginName = results[0].String()
|
||||||
|
nameFound = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if versionMethod := pluginValue.MethodByName("Version"); versionMethod.IsValid() {
|
||||||
|
results := versionMethod.Call(nil)
|
||||||
|
if len(results) > 0 && results[0].Kind() == reflect.String {
|
||||||
|
pluginVersion = results[0].String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if descMethod := pluginValue.MethodByName("Description"); descMethod.IsValid() {
|
||||||
|
results := descMethod.Call(nil)
|
||||||
|
if len(results) > 0 && results[0].Kind() == reflect.String {
|
||||||
|
pluginDesc = results[0].String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if authorMethod := pluginValue.MethodByName("Author"); authorMethod.IsValid() {
|
||||||
|
results := authorMethod.Call(nil)
|
||||||
|
if len(results) > 0 && results[0].Kind() == reflect.String {
|
||||||
|
pluginAuthor = results[0].String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeMethod := pluginValue.MethodByName("Type"); typeMethod.IsValid() {
|
||||||
|
results := typeMethod.Call(nil)
|
||||||
|
if len(results) > 0 {
|
||||||
|
// 尝试匹配插件类型
|
||||||
|
typeStr := fmt.Sprint(results[0].Interface())
|
||||||
|
switch typeStr {
|
||||||
|
case "storage":
|
||||||
|
pluginType = PluginTypeStorage
|
||||||
|
case "security":
|
||||||
|
pluginType = PluginTypeSecurity
|
||||||
|
case "network":
|
||||||
|
pluginType = PluginTypeNetwork
|
||||||
|
case "utils":
|
||||||
|
pluginType = PluginTypeUtils
|
||||||
|
case "hardware":
|
||||||
|
pluginType = PluginTypeHardware
|
||||||
|
case "ui":
|
||||||
|
pluginType = PluginTypeUI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接检查字段(仅在方法不可用时)
|
||||||
|
if !nameFound && pluginValue.Kind() == reflect.Ptr && !pluginValue.IsNil() {
|
||||||
|
elemValue := pluginValue.Elem()
|
||||||
|
|
||||||
|
// 检查elemValue是否为结构体
|
||||||
|
if elemValue.Kind() == reflect.Struct {
|
||||||
|
// 检查是否有name字段
|
||||||
|
if nameField := elemValue.FieldByName("name"); nameField.IsValid() && nameField.Kind() == reflect.String {
|
||||||
|
pluginName = nameField.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有version字段
|
||||||
|
if versionField := elemValue.FieldByName("version"); versionField.IsValid() && versionField.Kind() == reflect.String {
|
||||||
|
pluginVersion = versionField.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有description字段
|
||||||
|
if descField := elemValue.FieldByName("description"); descField.IsValid() && descField.Kind() == reflect.String {
|
||||||
|
pluginDesc = descField.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有author字段
|
||||||
|
if authorField := elemValue.FieldByName("author"); authorField.IsValid() && authorField.Kind() == reflect.String {
|
||||||
|
pluginAuthor = authorField.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有pluginType字段
|
||||||
|
if typeField := elemValue.FieldByName("pluginType"); typeField.IsValid() {
|
||||||
|
// 转换为字符串并尝试匹配已知类型
|
||||||
|
typeStr := fmt.Sprint(typeField.Interface())
|
||||||
|
switch typeStr {
|
||||||
|
case "storage":
|
||||||
|
pluginType = PluginTypeStorage
|
||||||
|
case "security":
|
||||||
|
pluginType = PluginTypeSecurity
|
||||||
|
case "network":
|
||||||
|
pluginType = PluginTypeNetwork
|
||||||
|
case "utils":
|
||||||
|
pluginType = PluginTypeUtils
|
||||||
|
case "hardware":
|
||||||
|
pluginType = PluginTypeHardware
|
||||||
|
case "ui":
|
||||||
|
pluginType = PluginTypeUI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有enabled字段
|
||||||
|
if enabledField := elemValue.FieldByName("enabled"); enabledField.IsValid() && enabledField.Kind() == reflect.Bool {
|
||||||
|
enabled = enabledField.Bool()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果名称仍为空,则使用文件名
|
||||||
|
if pluginName == "" {
|
||||||
|
// 从路径中提取文件名并去掉扩展名
|
||||||
|
fileName := filepath.Base(path)
|
||||||
|
pluginName = strings.TrimSuffix(fileName, filepath.Ext(fileName))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("创建适配器插件: 名称=%s, 版本=%s, 作者=%s, 类型=%s\n",
|
||||||
|
pluginName, pluginVersion, pluginAuthor, pluginType)
|
||||||
|
|
||||||
|
// 创建一个自定义插件结构体
|
||||||
|
customPlugin := &DynamicPlugin{
|
||||||
|
original: symPlugin,
|
||||||
|
name: pluginName,
|
||||||
|
version: pluginVersion,
|
||||||
|
description: pluginDesc,
|
||||||
|
author: pluginAuthor,
|
||||||
|
pluginType: pluginType,
|
||||||
|
enabled: enabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查插件名称是否已存在
|
// 检查插件名称是否已存在
|
||||||
pm.mu.Lock()
|
pm.mu.Lock()
|
||||||
defer pm.mu.Unlock()
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
if _, exists := pm.plugins[plugin.Name()]; exists {
|
if _, exists := pm.plugins[customPlugin.Name()]; exists {
|
||||||
return fmt.Errorf("插件 %s 已存在", plugin.Name())
|
return fmt.Errorf("插件 %s 已存在", customPlugin.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置插件启用状态
|
// 设置插件启用状态
|
||||||
if config, exists := pm.configs[plugin.Name()]; exists {
|
if config, exists := pm.configs[customPlugin.Name()]; exists {
|
||||||
if enabled, ok := config["enabled"].(bool); ok {
|
if enabled, ok := config["enabled"].(bool); ok {
|
||||||
plugin.SetEnabled(enabled)
|
customPlugin.enabled = enabled
|
||||||
} else {
|
|
||||||
plugin.SetEnabled(true) // 默认启用
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
plugin.SetEnabled(true) // 默认启用
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册插件
|
// 注册插件
|
||||||
pm.registerPlugin(plugin)
|
pm.registerPlugin(customPlugin)
|
||||||
|
fmt.Printf("已成功注册插件: %s\n", customPlugin.Name())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DynamicPlugin 完全动态的插件实现
|
||||||
|
type DynamicPlugin struct {
|
||||||
|
original interface{}
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
description string
|
||||||
|
author string
|
||||||
|
pluginType PluginType
|
||||||
|
enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name 返回插件名称
|
||||||
|
func (p *DynamicPlugin) Name() string {
|
||||||
|
return p.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version 返回插件版本
|
||||||
|
func (p *DynamicPlugin) Version() string {
|
||||||
|
return p.version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description 返回插件描述
|
||||||
|
func (p *DynamicPlugin) Description() string {
|
||||||
|
return p.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author 返回插件作者
|
||||||
|
func (p *DynamicPlugin) Author() string {
|
||||||
|
return p.author
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 返回插件类型
|
||||||
|
func (p *DynamicPlugin) Type() PluginType {
|
||||||
|
return p.pluginType
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled 返回插件是否启用
|
||||||
|
func (p *DynamicPlugin) IsEnabled() bool {
|
||||||
|
return p.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnabled 设置插件启用状态
|
||||||
|
func (p *DynamicPlugin) SetEnabled(enabled bool) {
|
||||||
|
p.enabled = enabled
|
||||||
|
|
||||||
|
// 尝试调用原始插件的SetEnabled方法
|
||||||
|
method := reflect.ValueOf(p.original).MethodByName("SetEnabled")
|
||||||
|
if method.IsValid() {
|
||||||
|
method.Call([]reflect.Value{reflect.ValueOf(enabled)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init 初始化插件
|
||||||
|
func (p *DynamicPlugin) Init(ctx context.Context, config map[string]interface{}) error {
|
||||||
|
method := reflect.ValueOf(p.original).MethodByName("Init")
|
||||||
|
if !method.IsValid() {
|
||||||
|
return nil // 如果方法不存在,就不做任何事情
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定方法参数数量和类型
|
||||||
|
methodType := method.Type()
|
||||||
|
numParams := methodType.NumIn()
|
||||||
|
|
||||||
|
// 准备参数
|
||||||
|
var args []reflect.Value
|
||||||
|
|
||||||
|
// 对于不同参数数量的处理
|
||||||
|
switch numParams {
|
||||||
|
case 0:
|
||||||
|
// 无参数
|
||||||
|
case 1:
|
||||||
|
// 只有一个参数,可能是配置
|
||||||
|
args = append(args, reflect.ValueOf(config))
|
||||||
|
case 2:
|
||||||
|
// 两个参数,可能是上下文和配置
|
||||||
|
args = append(args, reflect.ValueOf(ctx), reflect.ValueOf(config))
|
||||||
|
default:
|
||||||
|
// 不支持更多参数
|
||||||
|
return fmt.Errorf("不支持的Init方法参数数量: %d", numParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用方法
|
||||||
|
results := method.Call(args)
|
||||||
|
|
||||||
|
// 处理返回值
|
||||||
|
if len(results) > 0 && results[0].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
||||||
|
if !results[0].IsNil() {
|
||||||
|
return results[0].Interface().(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start 启动插件
|
||||||
|
func (p *DynamicPlugin) Start(ctx context.Context) error {
|
||||||
|
method := reflect.ValueOf(p.original).MethodByName("Start")
|
||||||
|
if !method.IsValid() {
|
||||||
|
return nil // 如果方法不存在,就不做任何事情
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定方法参数数量和类型
|
||||||
|
methodType := method.Type()
|
||||||
|
numParams := methodType.NumIn()
|
||||||
|
|
||||||
|
// 准备参数
|
||||||
|
var args []reflect.Value
|
||||||
|
|
||||||
|
// 对于不同参数数量的处理
|
||||||
|
switch numParams {
|
||||||
|
case 0:
|
||||||
|
// 无参数
|
||||||
|
case 1:
|
||||||
|
// 一个参数,可能是上下文
|
||||||
|
args = append(args, reflect.ValueOf(ctx))
|
||||||
|
default:
|
||||||
|
// 不支持更多参数
|
||||||
|
return fmt.Errorf("不支持的Start方法参数数量: %d", numParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用方法
|
||||||
|
results := method.Call(args)
|
||||||
|
|
||||||
|
// 处理返回值
|
||||||
|
if len(results) > 0 && results[0].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
||||||
|
if !results[0].IsNil() {
|
||||||
|
return results[0].Interface().(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop 停止插件
|
||||||
|
func (p *DynamicPlugin) Stop(ctx context.Context) error {
|
||||||
|
method := reflect.ValueOf(p.original).MethodByName("Stop")
|
||||||
|
if !method.IsValid() {
|
||||||
|
return nil // 如果方法不存在,就不做任何事情
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定方法参数数量和类型
|
||||||
|
methodType := method.Type()
|
||||||
|
numParams := methodType.NumIn()
|
||||||
|
|
||||||
|
// 准备参数
|
||||||
|
var args []reflect.Value
|
||||||
|
|
||||||
|
// 对于不同参数数量的处理
|
||||||
|
switch numParams {
|
||||||
|
case 0:
|
||||||
|
// 无参数
|
||||||
|
case 1:
|
||||||
|
// 一个参数,可能是上下文
|
||||||
|
args = append(args, reflect.ValueOf(ctx))
|
||||||
|
default:
|
||||||
|
// 不支持更多参数
|
||||||
|
return fmt.Errorf("不支持的Stop方法参数数量: %d", numParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用方法
|
||||||
|
results := method.Call(args)
|
||||||
|
|
||||||
|
// 处理返回值
|
||||||
|
if len(results) > 0 && results[0].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
||||||
|
if !results[0].IsNil() {
|
||||||
|
return results[0].Interface().(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute 执行插件功能
|
||||||
|
func (p *DynamicPlugin) Execute(ctx context.Context, action string, params map[string]interface{}) (interface{}, error) {
|
||||||
|
method := reflect.ValueOf(p.original).MethodByName("Execute")
|
||||||
|
if !method.IsValid() {
|
||||||
|
return nil, fmt.Errorf("插件不支持Execute方法")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定方法参数数量和类型
|
||||||
|
methodType := method.Type()
|
||||||
|
numParams := methodType.NumIn()
|
||||||
|
|
||||||
|
// 准备参数
|
||||||
|
var args []reflect.Value
|
||||||
|
|
||||||
|
// 对于不同参数数量的处理
|
||||||
|
switch numParams {
|
||||||
|
case 2:
|
||||||
|
// 两个参数,可能是action和params
|
||||||
|
args = append(args, reflect.ValueOf(action), reflect.ValueOf(params))
|
||||||
|
case 3:
|
||||||
|
// 三个参数,可能是ctx、action和params
|
||||||
|
args = append(args, reflect.ValueOf(ctx), reflect.ValueOf(action), reflect.ValueOf(params))
|
||||||
|
default:
|
||||||
|
// 不支持其他参数数量
|
||||||
|
return nil, fmt.Errorf("不支持的Execute方法参数数量: %d", numParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用方法
|
||||||
|
results := method.Call(args)
|
||||||
|
|
||||||
|
// 处理返回值
|
||||||
|
var result interface{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch len(results) {
|
||||||
|
case 1:
|
||||||
|
// 只有一个返回值,检查是否为错误
|
||||||
|
if results[0].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
||||||
|
if !results[0].IsNil() {
|
||||||
|
err = results[0].Interface().(error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = results[0].Interface()
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
// 两个返回值,第一个是结果,第二个是错误
|
||||||
|
if !results[0].IsNil() {
|
||||||
|
result = results[0].Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
if results[1].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
||||||
|
if !results[1].IsNil() {
|
||||||
|
err = results[1].Interface().(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOperationInfo 获取操作的参数信息
|
||||||
|
func (p *DynamicPlugin) GetOperationInfo(operation string) (map[string]interface{}, error) {
|
||||||
|
method := reflect.ValueOf(p.original).MethodByName("GetOperationInfo")
|
||||||
|
if !method.IsValid() {
|
||||||
|
return map[string]interface{}{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
results := method.Call([]reflect.Value{reflect.ValueOf(operation)})
|
||||||
|
|
||||||
|
var result map[string]interface{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(results) > 0 && !results[0].IsNil() {
|
||||||
|
if m, ok := results[0].Interface().(map[string]interface{}); ok {
|
||||||
|
result = m
|
||||||
|
} else {
|
||||||
|
result = map[string]interface{}{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(results) > 1 && results[1].Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
||||||
|
if !results[1].IsNil() {
|
||||||
|
err = results[1].Interface().(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllOperations 获取所有操作及其参数信息
|
||||||
|
func (p *DynamicPlugin) GetAllOperations() map[string]map[string]interface{} {
|
||||||
|
method := reflect.ValueOf(p.original).MethodByName("GetAllOperations")
|
||||||
|
if !method.IsValid() {
|
||||||
|
return map[string]map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
results := method.Call(nil)
|
||||||
|
|
||||||
|
if len(results) > 0 && !results[0].IsNil() {
|
||||||
|
if m, ok := results[0].Interface().(map[string]map[string]interface{}); ok {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
// registerPlugin 注册插件到插件管理器
|
// registerPlugin 注册插件到插件管理器
|
||||||
func (pm *PluginManager) registerPlugin(plugin Plugin) {
|
func (pm *PluginManager) registerPlugin(plugin Plugin) {
|
||||||
pluginType := plugin.Type()
|
pluginType := plugin.Type()
|
||||||
@@ -1460,4 +1868,4 @@ func (pm *PluginManager) GetAllPluginsOperations() map[string]map[string]map[str
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
12
examples/plugin/plugins/build.sh
Normal file → Executable file
12
examples/plugin/plugins/build.sh
Normal file → Executable file
@@ -3,14 +3,14 @@
|
|||||||
# 构建脚本,用于编译所有插件
|
# 构建脚本,用于编译所有插件
|
||||||
|
|
||||||
# 确保输出目录存在
|
# 确保输出目录存在
|
||||||
mkdir -p dist
|
mkdir -p ../../admin/plugins
|
||||||
|
|
||||||
echo "===== 开始编译插件 ====="
|
echo "===== 开始编译插件 ====="
|
||||||
|
|
||||||
# 编译带有明确类型的日志插件
|
# 编译带有明确类型的日志插件
|
||||||
echo "编译日志插件 (明确类型)..."
|
echo "编译日志插件 (明确类型)..."
|
||||||
cd logger
|
cd logger
|
||||||
go build -buildmode=plugin -o ../dist/logger.so
|
go build -buildmode=plugin -ldflags "-s -w" -o ../../admin/plugins/logger.so
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "日志插件编译成功!"
|
echo "日志插件编译成功!"
|
||||||
else
|
else
|
||||||
@@ -22,7 +22,7 @@ cd ..
|
|||||||
# 编译使用默认类型的日志插件
|
# 编译使用默认类型的日志插件
|
||||||
echo "编译默认日志插件 (默认类型)..."
|
echo "编译默认日志插件 (默认类型)..."
|
||||||
cd defaultlogger
|
cd defaultlogger
|
||||||
go build -buildmode=plugin -o ../dist/defaultlogger.so
|
go build -buildmode=plugin -ldflags "-s -w" -o ../../admin/plugins/defaultlogger.so
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "默认日志插件编译成功!"
|
echo "默认日志插件编译成功!"
|
||||||
else
|
else
|
||||||
@@ -34,7 +34,7 @@ cd ..
|
|||||||
# 编译统计插件
|
# 编译统计插件
|
||||||
echo "编译统计插件..."
|
echo "编译统计插件..."
|
||||||
cd stats
|
cd stats
|
||||||
go build -buildmode=plugin -o ../dist/stats.so
|
go build -buildmode=plugin -ldflags "-s -w" -o ../../admin/plugins/stats.so
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "统计插件编译成功!"
|
echo "统计插件编译成功!"
|
||||||
else
|
else
|
||||||
@@ -46,7 +46,7 @@ cd ..
|
|||||||
# 编译存储插件
|
# 编译存储插件
|
||||||
echo "编译存储插件..."
|
echo "编译存储插件..."
|
||||||
cd storage
|
cd storage
|
||||||
go build -buildmode=plugin -o ../dist/storage.so
|
go build -buildmode=plugin -ldflags "-s -w" -o ../../admin/plugins/storage.so
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "存储插件编译成功!"
|
echo "存储插件编译成功!"
|
||||||
else
|
else
|
||||||
@@ -56,8 +56,6 @@ fi
|
|||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
echo "===== 所有插件编译完成 ====="
|
echo "===== 所有插件编译完成 ====="
|
||||||
echo "插件文件保存在 dist 目录中:"
|
|
||||||
ls -la dist/
|
|
||||||
|
|
||||||
# 对于Windows系统,添加.exe后缀
|
# 对于Windows系统,添加.exe后缀
|
||||||
# go build -buildmode=plugin -o ../dist/plugin_name.dll
|
# go build -buildmode=plugin -o ../dist/plugin_name.dll
|
@@ -14,22 +14,73 @@ import (
|
|||||||
// DefaultLoggerPlugin 默认日志插件
|
// DefaultLoggerPlugin 默认日志插件
|
||||||
// 提供文件日志和控制台日志功能,使用默认插件类型
|
// 提供文件日志和控制台日志功能,使用默认插件类型
|
||||||
type DefaultLoggerPlugin struct {
|
type DefaultLoggerPlugin struct {
|
||||||
*plugin.BasePlugin // 嵌入基本插件结构
|
plugin.Plugin // 嵌入接口,确保类型检查
|
||||||
logFile *os.File // 日志文件
|
name string // 插件名称
|
||||||
logger *log.Logger // 日志记录器
|
version string // 插件版本
|
||||||
config map[string]interface{} // 配置
|
description string // 插件描述
|
||||||
|
author string // 插件作者
|
||||||
|
pluginType plugin.PluginType // 插件类型
|
||||||
|
enabled bool // 是否启用
|
||||||
|
logFile *os.File // 日志文件
|
||||||
|
logger *log.Logger // 日志记录器
|
||||||
|
config map[string]interface{} // 配置
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugin 导出的插件变量
|
// Plugin 导出的插件变量
|
||||||
// 注意:变量名必须是Plugin,大小写敏感
|
// 注意:变量名必须是Plugin,大小写敏感
|
||||||
// 这个插件使用默认的通用插件类型(PluginTypeGeneral)
|
// 这个插件使用默认的通用插件类型(PluginTypeGeneral)
|
||||||
var Plugin = &DefaultLoggerPlugin{
|
var Plugin = &DefaultLoggerPlugin{
|
||||||
BasePlugin: plugin.NewBasePluginWithDefaultType(
|
name: "DefaultLoggerPlugin",
|
||||||
"DefaultLoggerPlugin",
|
version: "1.0.0",
|
||||||
"1.0.0",
|
description: "使用默认通用类型的日志记录插件",
|
||||||
"使用默认通用类型的日志记录插件",
|
author: "开发者",
|
||||||
"开发者",
|
pluginType: plugin.PluginTypeGeneral, // 使用通用插件类型
|
||||||
), // 将自动使用 PluginTypeGeneral 类型
|
enabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name 返回插件名称
|
||||||
|
func (p *DefaultLoggerPlugin) Name() string {
|
||||||
|
return p.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version 返回插件版本
|
||||||
|
func (p *DefaultLoggerPlugin) Version() string {
|
||||||
|
return p.version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description 返回插件描述
|
||||||
|
func (p *DefaultLoggerPlugin) Description() string {
|
||||||
|
return p.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author 返回插件作者
|
||||||
|
func (p *DefaultLoggerPlugin) Author() string {
|
||||||
|
return p.author
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 返回插件类型
|
||||||
|
func (p *DefaultLoggerPlugin) Type() plugin.PluginType {
|
||||||
|
return p.pluginType
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled 返回插件是否启用
|
||||||
|
func (p *DefaultLoggerPlugin) IsEnabled() bool {
|
||||||
|
return p.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnabled 设置插件启用状态
|
||||||
|
func (p *DefaultLoggerPlugin) SetEnabled(enabled bool) {
|
||||||
|
p.enabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOperationInfo 获取操作的参数信息
|
||||||
|
func (p *DefaultLoggerPlugin) GetOperationInfo(operation string) (map[string]interface{}, error) {
|
||||||
|
return map[string]interface{}{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllOperations 获取所有操作及其参数信息
|
||||||
|
func (p *DefaultLoggerPlugin) GetAllOperations() map[string]map[string]interface{} {
|
||||||
|
return map[string]map[string]interface{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init 初始化插件
|
// Init 初始化插件
|
||||||
|
@@ -14,36 +14,73 @@ import (
|
|||||||
// LoggerPlugin 日志插件
|
// LoggerPlugin 日志插件
|
||||||
// 提供文件日志和控制台日志功能
|
// 提供文件日志和控制台日志功能
|
||||||
type LoggerPlugin struct {
|
type LoggerPlugin struct {
|
||||||
*plugin.BasePlugin // 嵌入基本插件结构
|
plugin.Plugin // 嵌入接口,确保类型检查
|
||||||
logFile *os.File // 日志文件
|
name string // 插件名称
|
||||||
logger *log.Logger // 日志记录器
|
version string // 插件版本
|
||||||
config map[string]interface{} // 配置
|
description string // 插件描述
|
||||||
|
author string // 插件作者
|
||||||
|
pluginType plugin.PluginType // 插件类型
|
||||||
|
enabled bool // 是否启用
|
||||||
|
logFile *os.File // 日志文件
|
||||||
|
logger *log.Logger // 日志记录器
|
||||||
|
config map[string]interface{} // 配置
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugin 导出的插件变量
|
// Plugin 导出的插件变量
|
||||||
// 注意:变量名必须是Plugin,大小写敏感
|
// 注意:变量名必须是Plugin,大小写敏感
|
||||||
// 使用方式1: 明确指定插件类型
|
|
||||||
var Plugin = &LoggerPlugin{
|
var Plugin = &LoggerPlugin{
|
||||||
BasePlugin: plugin.NewBasePlugin(
|
name: "LoggerPlugin",
|
||||||
"LoggerPlugin",
|
version: "1.0.0",
|
||||||
"1.0.0",
|
description: "简单的日志记录插件",
|
||||||
"简单的日志记录插件",
|
author: "开发者",
|
||||||
"开发者",
|
pluginType: plugin.PluginTypeUtils, // 明确指定为工具类插件
|
||||||
plugin.PluginTypeUtils, // 明确指定为工具类插件
|
enabled: true,
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用方式2: 使用默认插件类型(通用插件)
|
// Name 返回插件名称
|
||||||
// 如果您不关心插件类型或想使用默认的通用插件类型,可以使用以下方式:
|
func (p *LoggerPlugin) Name() string {
|
||||||
//
|
return p.name
|
||||||
// var Plugin = &LoggerPlugin{
|
}
|
||||||
// BasePlugin: plugin.NewBasePluginWithDefaultType(
|
|
||||||
// "LoggerPlugin",
|
// Version 返回插件版本
|
||||||
// "1.0.0",
|
func (p *LoggerPlugin) Version() string {
|
||||||
// "简单的日志记录插件",
|
return p.version
|
||||||
// "开发者",
|
}
|
||||||
// ), // 将自动使用 PluginTypeGeneral 类型
|
|
||||||
// }
|
// Description 返回插件描述
|
||||||
|
func (p *LoggerPlugin) Description() string {
|
||||||
|
return p.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author 返回插件作者
|
||||||
|
func (p *LoggerPlugin) Author() string {
|
||||||
|
return p.author
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 返回插件类型
|
||||||
|
func (p *LoggerPlugin) Type() plugin.PluginType {
|
||||||
|
return p.pluginType
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled 返回插件是否启用
|
||||||
|
func (p *LoggerPlugin) IsEnabled() bool {
|
||||||
|
return p.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnabled 设置插件启用状态
|
||||||
|
func (p *LoggerPlugin) SetEnabled(enabled bool) {
|
||||||
|
p.enabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOperationInfo 获取操作的参数信息
|
||||||
|
func (p *LoggerPlugin) GetOperationInfo(operation string) (map[string]interface{}, error) {
|
||||||
|
return map[string]interface{}{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllOperations 获取所有操作及其参数信息
|
||||||
|
func (p *LoggerPlugin) GetAllOperations() map[string]map[string]interface{} {
|
||||||
|
return map[string]map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
// Init 初始化插件
|
// Init 初始化插件
|
||||||
func (p *LoggerPlugin) Init(ctx context.Context, config map[string]interface{}) error {
|
func (p *LoggerPlugin) Init(ctx context.Context, config map[string]interface{}) error {
|
||||||
|
@@ -12,13 +12,19 @@ import (
|
|||||||
// StatsPlugin 统计插件
|
// StatsPlugin 统计插件
|
||||||
// 用于收集和记录系统运行时统计数据
|
// 用于收集和记录系统运行时统计数据
|
||||||
type StatsPlugin struct {
|
type StatsPlugin struct {
|
||||||
*plugin.BasePluginImpl // 改为使用BasePluginImpl
|
plugin.Plugin // 嵌入接口,确保类型检查
|
||||||
stats map[string]int64
|
name string // 插件名称
|
||||||
startTime time.Time
|
version string // 插件版本
|
||||||
mu sync.RWMutex
|
description string // 插件描述
|
||||||
tickerStop chan bool
|
author string // 插件作者
|
||||||
ticker *time.Ticker
|
pluginType plugin.PluginType // 插件类型
|
||||||
config map[string]interface{}
|
enabled bool // 是否启用
|
||||||
|
stats map[string]int64
|
||||||
|
startTime time.Time
|
||||||
|
mu sync.RWMutex
|
||||||
|
tickerStop chan bool
|
||||||
|
ticker *time.Ticker
|
||||||
|
config map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatsParams 统计请求参数结构体
|
// StatsParams 统计请求参数结构体
|
||||||
@@ -33,29 +39,60 @@ type StatsParams struct {
|
|||||||
|
|
||||||
// Plugin 导出的插件变量
|
// Plugin 导出的插件变量
|
||||||
var Plugin = &StatsPlugin{
|
var Plugin = &StatsPlugin{
|
||||||
// 使用默认构造函数,不指定插件类型,将默认为通用插件
|
name: "StatsPlugin",
|
||||||
BasePluginImpl: plugin.NewPluginWithDefaultType(
|
version: "1.0.0",
|
||||||
"StatsPlugin",
|
description: "系统运行时统计插件",
|
||||||
"1.0.0",
|
author: "开发者",
|
||||||
"系统运行时统计插件",
|
pluginType: plugin.PluginTypeUtils, // 指定为工具类插件
|
||||||
"开发者",
|
enabled: true,
|
||||||
),
|
stats: make(map[string]int64),
|
||||||
stats: make(map[string]int64),
|
tickerStop: make(chan bool),
|
||||||
tickerStop: make(chan bool),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为展示如何指定类型,我们也可以显式设置插件类型
|
// Name 返回插件名称
|
||||||
// var Plugin = &StatsPlugin{
|
func (p *StatsPlugin) Name() string {
|
||||||
// BasePlugin: plugin.NewBasePlugin(
|
return p.name
|
||||||
// "StatsPlugin",
|
}
|
||||||
// "1.0.0",
|
|
||||||
// "系统运行时统计插件",
|
// Version 返回插件版本
|
||||||
// "开发者",
|
func (p *StatsPlugin) Version() string {
|
||||||
// plugin.PluginTypeUtils, // 明确指定为工具类插件
|
return p.version
|
||||||
// ),
|
}
|
||||||
// stats: make(map[string]int64),
|
|
||||||
// tickerStop: make(chan bool),
|
// Description 返回插件描述
|
||||||
// }
|
func (p *StatsPlugin) Description() string {
|
||||||
|
return p.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author 返回插件作者
|
||||||
|
func (p *StatsPlugin) Author() string {
|
||||||
|
return p.author
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 返回插件类型
|
||||||
|
func (p *StatsPlugin) Type() plugin.PluginType {
|
||||||
|
return p.pluginType
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled 返回插件是否启用
|
||||||
|
func (p *StatsPlugin) IsEnabled() bool {
|
||||||
|
return p.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnabled 设置插件启用状态
|
||||||
|
func (p *StatsPlugin) SetEnabled(enabled bool) {
|
||||||
|
p.enabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOperationInfo 获取操作的参数信息
|
||||||
|
func (p *StatsPlugin) GetOperationInfo(operation string) (map[string]interface{}, error) {
|
||||||
|
return map[string]interface{}{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllOperations 获取所有操作及其参数信息
|
||||||
|
func (p *StatsPlugin) GetAllOperations() map[string]map[string]interface{} {
|
||||||
|
return map[string]map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
// Init 初始化插件
|
// Init 初始化插件
|
||||||
func (p *StatsPlugin) Init(ctx context.Context, config map[string]interface{}) error {
|
func (p *StatsPlugin) Init(ctx context.Context, config map[string]interface{}) error {
|
||||||
@@ -242,7 +279,15 @@ func (p *StatsPlugin) logStats() {
|
|||||||
// GetAvailableOperations 获取可用操作列表
|
// GetAvailableOperations 获取可用操作列表
|
||||||
// 这是一个帮助方法,列出所有可通过Execute调用的操作
|
// 这是一个帮助方法,列出所有可通过Execute调用的操作
|
||||||
func (p *StatsPlugin) GetAvailableOperations() []string {
|
func (p *StatsPlugin) GetAvailableOperations() []string {
|
||||||
return p.GetAvailableActions()
|
// 返回硬编码的操作列表,而不是调用不存在的方法
|
||||||
|
return []string{
|
||||||
|
"incrementstat",
|
||||||
|
"getstat",
|
||||||
|
"recordrequest",
|
||||||
|
"resetstats",
|
||||||
|
"generatestatsreport",
|
||||||
|
"getallstats",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// main 函数是必须的,但不会被调用
|
// main 函数是必须的,但不会被调用
|
||||||
|
@@ -14,22 +14,72 @@ import (
|
|||||||
// StoragePlugin 存储插件
|
// StoragePlugin 存储插件
|
||||||
// 提供简单的文件存储功能
|
// 提供简单的文件存储功能
|
||||||
type StoragePlugin struct {
|
type StoragePlugin struct {
|
||||||
*plugin.BasePlugin // 嵌入基本插件结构
|
plugin.Plugin // 嵌入接口,确保类型检查
|
||||||
storageDir string // 存储目录
|
name string // 插件名称
|
||||||
config map[string]interface{} // 配置
|
version string // 插件版本
|
||||||
mu sync.RWMutex // 读写锁
|
description string // 插件描述
|
||||||
|
author string // 插件作者
|
||||||
|
pluginType plugin.PluginType // 插件类型
|
||||||
|
enabled bool // 是否启用
|
||||||
|
storageDir string // 存储目录
|
||||||
|
config map[string]interface{} // 配置
|
||||||
|
mu sync.RWMutex // 读写锁
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugin 导出的插件变量
|
// Plugin 导出的插件变量
|
||||||
// 注意:变量名必须是Plugin,大小写敏感
|
// 注意:变量名必须是Plugin,大小写敏感
|
||||||
var Plugin = &StoragePlugin{
|
var Plugin = &StoragePlugin{
|
||||||
BasePlugin: plugin.NewBasePlugin(
|
name: "StoragePlugin",
|
||||||
"StoragePlugin",
|
version: "1.0.0",
|
||||||
"1.0.0",
|
description: "简单的文件存储插件",
|
||||||
"简单的文件存储插件",
|
author: "开发者",
|
||||||
"开发者",
|
pluginType: plugin.PluginTypeStorage, // 设置插件类型为存储插件
|
||||||
plugin.PluginTypeStorage, // 设置插件类型为存储插件
|
enabled: true,
|
||||||
),
|
}
|
||||||
|
|
||||||
|
// Name 返回插件名称
|
||||||
|
func (p *StoragePlugin) Name() string {
|
||||||
|
return p.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version 返回插件版本
|
||||||
|
func (p *StoragePlugin) Version() string {
|
||||||
|
return p.version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description 返回插件描述
|
||||||
|
func (p *StoragePlugin) Description() string {
|
||||||
|
return p.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author 返回插件作者
|
||||||
|
func (p *StoragePlugin) Author() string {
|
||||||
|
return p.author
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 返回插件类型
|
||||||
|
func (p *StoragePlugin) Type() plugin.PluginType {
|
||||||
|
return p.pluginType
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled 返回插件是否启用
|
||||||
|
func (p *StoragePlugin) IsEnabled() bool {
|
||||||
|
return p.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnabled 设置插件启用状态
|
||||||
|
func (p *StoragePlugin) SetEnabled(enabled bool) {
|
||||||
|
p.enabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOperationInfo 获取操作的参数信息
|
||||||
|
func (p *StoragePlugin) GetOperationInfo(operation string) (map[string]interface{}, error) {
|
||||||
|
return map[string]interface{}{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllOperations 获取所有操作及其参数信息
|
||||||
|
func (p *StoragePlugin) GetAllOperations() map[string]map[string]interface{} {
|
||||||
|
return map[string]map[string]interface{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init 初始化插件
|
// Init 初始化插件
|
||||||
|
Reference in New Issue
Block a user