增强插件系统:添加操作参数信息获取功能

- 在插件接口中新增 GetOperationInfo 和 GetAllOperations 方法,支持获取特定操作及所有操作的参数信息。
- 更新 PluginHelper 以实现获取操作参数信息的功能,提升插件的可用性和灵活性。
- 在日志插件中实现操作信息获取,展示如何使用新方法获取操作的详细参数信息。
- 示例程序中添加新的选项,展示如何获取插件操作的参数信息。

此更新提升了插件系统的可扩展性和易用性,便于开发者更好地管理和使用插件功能。
This commit is contained in:
2025-03-14 12:32:02 +08:00
parent b6bf2c5699
commit 2e29253909
6 changed files with 538 additions and 3 deletions

View File

@@ -14,7 +14,8 @@ func main() {
fmt.Println("请选择要运行的示例:") fmt.Println("请选择要运行的示例:")
fmt.Println("1. 基本插件加载和管理") fmt.Println("1. 基本插件加载和管理")
fmt.Println("2. 动态参数传递示例") fmt.Println("2. 动态参数传递示例")
fmt.Print("请输入选项 (1-2): ") fmt.Println("3. 插件操作参数信息示例")
fmt.Print("请输入选项 (1-3): ")
var choice int var choice int
fmt.Scan(&choice) fmt.Scan(&choice)
@@ -24,6 +25,8 @@ func main() {
BasicPluginExample() BasicPluginExample()
case 2: case 2:
DynamicParamsExample() DynamicParamsExample()
case 3:
OperationInfoExample()
default: default:
fmt.Println("无效的选项") fmt.Println("无效的选项")
} }

View File

@@ -0,0 +1,182 @@
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/darkit/goproxy/examples/plugin"
)
// 格式化输出JSON
func prettyPrint(data interface{}) {
bytes, err := json.MarshalIndent(data, "", " ")
if err != nil {
fmt.Printf("错误: %v\n", err)
return
}
fmt.Println(string(bytes))
}
// 演示插件操作信息获取功能
func OperationInfoExample() {
// 创建上下文
ctx := context.Background()
// 获取插件目录
pluginsDir := filepath.Join("..", "plugins", "dist")
if _, err := os.Stat(pluginsDir); os.IsNotExist(err) {
fmt.Printf("插件目录不存在: %s\n", pluginsDir)
return
}
// 创建插件管理器
pm := plugin.NewPluginManager(pluginsDir)
// 加载插件
fmt.Println("正在加载插件...")
if err := pm.LoadPlugins(); err != nil {
fmt.Printf("加载插件失败: %v\n", err)
return
}
// 初始化所有插件
if err := pm.InitPlugins(ctx); err != nil {
fmt.Printf("初始化插件失败: %v\n", err)
return
}
// 启动所有插件
if err := pm.StartPlugins(ctx); err != nil {
fmt.Printf("启动插件失败: %v\n", err)
return
}
// 打印所有插件信息
fmt.Println("\n=== 已加载的插件 ===")
for _, info := range pm.GetPluginInfos() {
fmt.Printf("插件: %s (版本: %s, 类型: %s)\n", info.Name, info.Version, info.Type)
}
fmt.Println()
// 示例1: 获取StatsPlugin的所有操作信息
fmt.Println("=== 示例1: 获取StatsPlugin的所有操作信息 ===")
operations, err := pm.GetPluginAllOperations("StatsPlugin")
if err != nil {
fmt.Printf("获取操作信息失败: %v\n", err)
} else {
fmt.Printf("StatsPlugin支持 %d 个操作:\n", len(operations))
for opName := range operations {
fmt.Printf(" - %s\n", opName)
}
// 选择一个操作详细展示
if len(operations) > 0 {
selectedOp := ""
for opName := range operations {
if strings.Contains(strings.ToLower(opName), "record") {
selectedOp = opName
break
}
}
if selectedOp != "" {
fmt.Printf("\n%s 操作的详细信息:\n", selectedOp)
prettyPrint(operations[selectedOp])
}
}
}
// 示例2: 获取特定操作的参数信息
fmt.Println("\n=== 示例2: 获取特定操作的参数信息 ===")
recordInfo, err := pm.GetPluginOperationInfo("StatsPlugin", "recordrequest")
if err != nil {
fmt.Printf("获取recordrequest参数信息失败: %v\n", err)
} else {
fmt.Println("recordrequest操作的参数信息:")
prettyPrint(recordInfo)
// 基于参数信息构建调用参数
if paramsInfo, ok := recordInfo["parameters"].(map[string]interface{}); ok {
fmt.Println("\n基于参数信息自动构建调用参数:")
callParams := make(map[string]interface{})
for paramName, paramInfo := range paramsInfo {
if paramInfoMap, ok := paramInfo.(map[string]interface{}); ok {
if paramInfoMap["type"] == "struct" {
// 处理结构体参数
if fields, ok := paramInfoMap["fields"].(map[string]interface{}); ok {
for fieldName, fieldInfo := range fields {
if fieldInfoMap, ok := fieldInfo.(map[string]interface{}); ok {
// 根据字段类型设置默认值
fieldType, _ := fieldInfoMap["type"].(string)
switch fieldType {
case "integer":
callParams[fieldName] = int64(1000)
case "boolean":
callParams[fieldName] = false
case "string":
callParams[fieldName] = "自动生成的值"
}
}
}
}
} else {
// 处理基本类型参数
paramType, _ := paramInfoMap["type"].(string)
switch paramType {
case "integer":
callParams[paramName] = int64(42)
case "boolean":
callParams[paramName] = true
case "string":
callParams[paramName] = "自动生成的值"
}
}
}
}
// 打印构建的参数
prettyPrint(callParams)
// 使用构建的参数调用操作
fmt.Println("\n使用自动构建的参数调用操作:")
result, err := pm.ExecutePlugin(ctx, "StatsPlugin", "recordrequest", callParams)
fmt.Printf("调用结果: %v, 错误: %v\n", result, err)
}
}
// 示例3: 获取所有插件的所有操作信息
fmt.Println("\n=== 示例3: 获取所有插件的所有操作信息 ===")
allOperations := pm.GetAllPluginsOperations()
fmt.Printf("找到 %d 个插件的操作信息:\n", len(allOperations))
var totalOps int
for pluginName, ops := range allOperations {
fmt.Printf(" 插件 %s: %d 个操作\n", pluginName, len(ops))
totalOps += len(ops)
}
fmt.Printf("总计 %d 个可用操作\n", totalOps)
// 示例4: 按插件类型获取操作信息
fmt.Println("\n=== 示例4: 按插件类型获取操作信息 ===")
utilsOps := pm.GetOperationsByType(plugin.PluginTypeUtils)
fmt.Printf("工具类插件操作:\n")
for pluginName, ops := range utilsOps {
fmt.Printf(" 插件 %s: %d 个操作\n", pluginName, len(ops))
for opName := range ops {
fmt.Printf(" - %s\n", opName)
}
}
// 停止所有插件
if err := pm.StopPlugins(ctx); err != nil {
fmt.Printf("停止插件失败: %v\n", err)
return
}
fmt.Println("\n示例结束")
}

View File

@@ -8,7 +8,7 @@ cd ../example
# 编译示例程序 # 编译示例程序
echo "===== 编译示例程序 =====" echo "===== 编译示例程序 ====="
go build -o plugin_demo go build -o plugin_demo *.go
# 运行示例程序 # 运行示例程序
echo "===== 运行示例程序 =====" echo "===== 运行示例程序 ====="

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"runtime" "runtime"
"strings"
"github.com/darkit/goproxy/examples/plugin" "github.com/darkit/goproxy/examples/plugin"
) )
@@ -76,6 +77,99 @@ func (p *LogPlugin) SetEnabled(enabled bool) {
p.enabled = enabled p.enabled = enabled
} }
// GetOperationInfo 获取操作的参数信息
func (p *LogPlugin) GetOperationInfo(operation string) (map[string]interface{}, error) {
// 转换为小写以便不区分大小写匹配
operation = strings.ToLower(operation)
switch operation {
case "log":
return map[string]interface{}{
"name": "log",
"description": "记录日志消息",
"parameters": map[string]interface{}{
"message": map[string]interface{}{
"type": "string",
"description": "要记录的日志消息",
"required": true,
},
},
"returns": map[string]interface{}{
"result": map[string]interface{}{
"type": "boolean",
"description": "操作是否成功",
},
},
}, nil
case "setlevel":
return map[string]interface{}{
"name": "setLevel",
"description": "设置日志级别",
"parameters": map[string]interface{}{
"level": map[string]interface{}{
"type": "string",
"description": "日志级别info、debug、warn、error",
"required": true,
},
},
"returns": map[string]interface{}{
"result": map[string]interface{}{
"type": "boolean",
"description": "操作是否成功",
},
},
}, nil
case "getlevel":
return map[string]interface{}{
"name": "getLevel",
"description": "获取当前日志级别",
"parameters": map[string]interface{}{},
"returns": map[string]interface{}{
"result": map[string]interface{}{
"type": "string",
"description": "当前日志级别",
},
},
}, nil
case "getstatus":
return map[string]interface{}{
"name": "getStatus",
"description": "获取插件状态信息",
"parameters": map[string]interface{}{},
"returns": map[string]interface{}{
"result": map[string]interface{}{
"type": "object",
"description": "包含插件状态的对象",
"properties": map[string]interface{}{
"name": map[string]interface{}{"type": "string"},
"version": map[string]interface{}{"type": "string"},
"level": map[string]interface{}{"type": "string"},
"enabled": map[string]interface{}{"type": "boolean"},
"type": map[string]interface{}{"type": "string"},
},
},
},
}, nil
default:
return nil, fmt.Errorf("未知的操作: %s", operation)
}
}
// GetAllOperations 获取所有操作及其参数信息
func (p *LogPlugin) GetAllOperations() map[string]map[string]interface{} {
operations := make(map[string]map[string]interface{})
// 获取所有操作的信息
for _, op := range []string{"log", "setlevel", "getlevel", "getstatus"} {
info, err := p.GetOperationInfo(op)
if err == nil {
operations[op] = info
}
}
return operations
}
func (p *LogPlugin) Init(ctx context.Context, config map[string]interface{}) error { func (p *LogPlugin) Init(ctx context.Context, config map[string]interface{}) error {
if level, ok := config["level"].(string); ok { if level, ok := config["level"].(string); ok {
p.level = level p.level = level

View File

@@ -43,6 +43,10 @@ type Plugin interface {
// params: 操作所需的参数 // params: 操作所需的参数
// 返回操作结果和可能的错误 // 返回操作结果和可能的错误
Execute(ctx context.Context, action string, params map[string]interface{}) (interface{}, error) Execute(ctx context.Context, action string, params map[string]interface{}) (interface{}, error)
// GetOperationInfo 获取操作的参数信息
GetOperationInfo(operation string) (map[string]interface{}, error)
// GetAllOperations 获取所有操作及其参数信息
GetAllOperations() map[string]map[string]interface{}
} }
// PluginHelper 插件辅助器 // PluginHelper 插件辅助器
@@ -469,6 +473,169 @@ func convertParamValue(value interface{}, targetType reflect.Type) (reflect.Valu
return reflect.Value{}, fmt.Errorf("不支持将 %T 类型转换为 %s", value, targetType) return reflect.Value{}, fmt.Errorf("不支持将 %T 类型转换为 %s", value, targetType)
} }
// GetParameterInfo 获取操作的参数信息
func (h *PluginHelper) GetParameterInfo(action string) (map[string]interface{}, error) {
action = strings.ToLower(action)
method, exists := h.methods[action]
if !exists {
return nil, fmt.Errorf("未知的操作: %s", action)
}
paramTypes := h.methodParams[action]
returnTypes := h.methodRetValues[action]
result := map[string]interface{}{
"name": action,
"description": fmt.Sprintf("%s 操作", method.Name),
"parameters": make(map[string]interface{}),
"returns": make(map[string]interface{}),
}
// 处理参数信息
paramsInfo := make(map[string]interface{})
// 首先检查是否有context参数
hasContext := false
if len(paramTypes) > 0 && paramTypes[0].String() == "context.Context" {
hasContext = true
result["hasContext"] = true
paramTypes = paramTypes[1:] // 跳过context参数
}
for i, paramType := range paramTypes {
// 如果是结构体参数,展开其字段
if paramType.Kind() == reflect.Struct {
structInfo := getStructInfo(paramType)
structInfo["position"] = i + 1 // 考虑到接收者和可能的context
if hasContext {
structInfo["position"] = i + 2
}
paramsInfo[paramType.Name()] = structInfo
} else {
// 基本类型参数
paramName := fmt.Sprintf("arg%d", i)
typeInfo := getTypeInfo(paramType)
typeInfo["position"] = i + 1 // 考虑到接收者
if hasContext {
typeInfo["position"] = i + 2
}
paramsInfo[paramName] = typeInfo
}
}
result["parameters"] = paramsInfo
// 处理返回值信息
returnsInfo := make(map[string]interface{})
for i, returnType := range returnTypes {
returnName := fmt.Sprintf("return%d", i)
if i == len(returnTypes)-1 && returnType.String() == "error" {
returnName = "error"
} else if i == 0 && len(returnTypes) == 2 && returnTypes[1].String() == "error" {
returnName = "result"
}
returnsInfo[returnName] = getTypeInfo(returnType)
}
result["returns"] = returnsInfo
return result, nil
}
// getStructInfo 获取结构体类型的详细信息
func getStructInfo(t reflect.Type) map[string]interface{} {
info := map[string]interface{}{
"type": "struct",
"name": t.Name(),
"package": t.PkgPath(),
"fields": make(map[string]interface{}),
"fieldCount": t.NumField(),
}
fields := make(map[string]interface{})
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// 跳过非导出字段
if field.PkgPath != "" {
continue
}
fieldName := field.Name
jsonTag := field.Tag.Get("json")
if jsonTag != "" && jsonTag != "-" {
parts := strings.Split(jsonTag, ",")
if parts[0] != "" {
fieldName = parts[0]
}
}
fieldInfo := getTypeInfo(field.Type)
fieldInfo["name"] = field.Name
fieldInfo["jsonName"] = fieldName
fieldInfo["position"] = i
fieldInfo["tags"] = string(field.Tag)
fields[fieldName] = fieldInfo
}
info["fields"] = fields
return info
}
// getTypeInfo 获取类型的基本信息
func getTypeInfo(t reflect.Type) map[string]interface{} {
info := map[string]interface{}{
"typeName": t.String(),
"kind": t.Kind().String(),
"type": typeKindToString(t.Kind()),
}
// 为不同类型添加特定信息
switch t.Kind() {
case reflect.Slice, reflect.Array:
info["elemType"] = getTypeInfo(t.Elem())
case reflect.Map:
info["keyType"] = getTypeInfo(t.Key())
info["valueType"] = getTypeInfo(t.Elem())
case reflect.Ptr:
info["elemType"] = getTypeInfo(t.Elem())
case reflect.Struct:
// 对于结构体,提供简略信息
info["structName"] = t.Name()
info["package"] = t.PkgPath()
info["fieldCount"] = t.NumField()
}
return info
}
// typeKindToString 将类型Kind转换为字符串表示
func typeKindToString(kind reflect.Kind) string {
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return "integer"
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return "unsigned integer"
case reflect.Float32, reflect.Float64:
return "float"
case reflect.Bool:
return "boolean"
case reflect.String:
return "string"
case reflect.Slice, reflect.Array:
return "array"
case reflect.Map:
return "map"
case reflect.Struct:
return "object"
case reflect.Ptr:
return "pointer"
case reflect.Interface:
return "interface"
default:
return kind.String()
}
}
// BasePluginImpl 提供插件接口的基本实现用于适配Plugin接口 // BasePluginImpl 提供插件接口的基本实现用于适配Plugin接口
// 这个结构体包装了BasePlugin以便兼容context参数 // 这个结构体包装了BasePlugin以便兼容context参数
type BasePluginImpl struct { type BasePluginImpl struct {
@@ -531,6 +698,23 @@ func (p *BasePluginImpl) GetAvailableActions() []string {
return p.helper.GetAvailableActions() return p.helper.GetAvailableActions()
} }
// GetOperationInfo 获取操作的参数信息
func (p *BasePluginImpl) GetOperationInfo(operation string) (map[string]interface{}, error) {
return p.helper.GetParameterInfo(operation)
}
// GetAllOperations 获取所有操作及其参数信息
func (p *BasePluginImpl) GetAllOperations() map[string]map[string]interface{} {
operations := make(map[string]map[string]interface{})
for _, action := range p.helper.GetAvailableActions() {
info, err := p.helper.GetParameterInfo(action)
if err == nil {
operations[action] = info
}
}
return operations
}
// PluginInfo 插件信息 // PluginInfo 插件信息
type PluginInfo struct { type PluginInfo struct {
Name string `json:"name"` Name string `json:"name"`
@@ -1099,3 +1283,75 @@ func (pm *PluginManager) ExecuteAllPlugins(ctx context.Context, action string, p
return results return results
} }
// GetPluginOperationInfo 获取插件特定操作的参数信息
func (pm *PluginManager) GetPluginOperationInfo(name string, operation string) (map[string]interface{}, error) {
pm.mu.RLock()
defer pm.mu.RUnlock()
plugin, exists := pm.plugins[name]
if !exists {
return nil, fmt.Errorf("插件 %s 不存在", name)
}
if !plugin.IsEnabled() {
return nil, fmt.Errorf("插件 %s 已禁用", name)
}
return plugin.GetOperationInfo(operation)
}
// GetPluginAllOperations 获取插件所有操作及其参数信息
func (pm *PluginManager) GetPluginAllOperations(name string) (map[string]map[string]interface{}, error) {
pm.mu.RLock()
defer pm.mu.RUnlock()
plugin, exists := pm.plugins[name]
if !exists {
return nil, fmt.Errorf("插件 %s 不存在", name)
}
if !plugin.IsEnabled() {
return nil, fmt.Errorf("插件 %s 已禁用", name)
}
return plugin.GetAllOperations(), nil
}
// GetOperationsByType 获取指定类型插件的所有操作信息
func (pm *PluginManager) GetOperationsByType(pluginType PluginType) map[string]map[string]map[string]interface{} {
pm.mu.RLock()
defer pm.mu.RUnlock()
result := make(map[string]map[string]map[string]interface{})
if typePlugins, exists := pm.pluginsByType[pluginType]; exists {
for name, plugin := range typePlugins {
if !plugin.IsEnabled() {
continue
}
result[name] = plugin.GetAllOperations()
}
}
return result
}
// GetAllPluginsOperations 获取所有插件的所有操作信息
func (pm *PluginManager) GetAllPluginsOperations() map[string]map[string]map[string]interface{} {
pm.mu.RLock()
defer pm.mu.RUnlock()
result := make(map[string]map[string]map[string]interface{})
for name, plugin := range pm.plugins {
if !plugin.IsEnabled() {
continue
}
result[name] = plugin.GetAllOperations()
}
return result
}

View File

@@ -123,7 +123,7 @@ func main() {
// 创建测试插件 // 创建测试插件
plugin := NewTestPlugin() plugin := NewTestPlugin()
fmt.Println("===== 测试自动方法注册与调用机制 =====\n") fmt.Println("===== 测试自动方法注册与调用机制 =====")
// 获取可用的操作列表 // 获取可用的操作列表
fmt.Println("可用操作列表:") fmt.Println("可用操作列表:")