Files
goproxy/examples/plugin/client_generator.go
DarkiT 557059b2d2 增强插件系统:优化插件管理和动态插件实现
- 在插件管理器中引入 DynamicPlugin 结构体,支持动态加载和管理插件,提升插件的灵活性和可扩展性。
- 更新插件接口,添加插件名称、版本、描述、作者、类型和启用状态的获取和设置方法,增强插件信息的管理能力。
- 修改现有插件实现,确保兼容新的动态插件结构,提升插件的统一性和可维护性。
- 更新示例程序,展示如何使用新的动态插件功能,提升用户体验。

此更新提升了插件系统的灵活性和可扩展性,便于开发者更好地管理和使用插件功能。
2025-03-14 07:12:05 +00:00

844 lines
22 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package plugin
import (
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"time"
)
// ClientGenerator 客户端代码生成器
// 用于根据插件API信息生成类型安全的客户端代码
type ClientGenerator struct {
pm *PluginManager // 插件管理器
outputPath string // 输出路径
moduleName string // 生成的代码模块名称
language string // 生成的代码语言
}
// NewClientGenerator 创建一个新的客户端代码生成器
func NewClientGenerator(pm *PluginManager, outputPath, moduleName, language string) *ClientGenerator {
if language == "" {
language = "go" // 默认生成Go语言代码
}
return &ClientGenerator{
pm: pm,
outputPath: outputPath,
moduleName: moduleName,
language: language,
}
}
// GenerateAllClients 为所有插件生成客户端代码
func (g *ClientGenerator) GenerateAllClients() error {
// 确保输出目录存在
if err := os.MkdirAll(g.outputPath, 0755); err != nil {
return fmt.Errorf("创建输出目录失败: %v", err)
}
// 根据语言选择生成器
switch g.language {
case "go":
return g.generateGoClients()
default:
return fmt.Errorf("不支持的语言: %s目前仅支持 Go 语言", g.language)
}
}
// generateGoClients 生成Go语言客户端代码
func (g *ClientGenerator) generateGoClients() error {
// 创建client目录
clientDir := filepath.Join(g.outputPath, "client")
if err := os.MkdirAll(clientDir, 0755); err != nil {
return fmt.Errorf("创建client目录失败: %v", err)
}
// 创建pkg目录
pkgDir := filepath.Join(clientDir, "pkg")
if err := os.MkdirAll(pkgDir, 0755); err != nil {
return fmt.Errorf("创建pkg目录失败: %v", err)
}
// 创建models目录
modelsDir := filepath.Join(pkgDir, "models")
if err := os.MkdirAll(modelsDir, 0755); err != nil {
return fmt.Errorf("创建models目录失败: %v", err)
}
// 为每种类型创建单独的目录
plugins := g.pm.GetAllPlugins()
pluginsByType := make(map[PluginType][]Plugin)
for _, p := range plugins {
pluginType := p.Type()
pluginsByType[pluginType] = append(pluginsByType[pluginType], p)
}
// 生成模型
if err := g.generateModelCode(); err != nil {
return fmt.Errorf("生成模型代码失败: %v", err)
}
// 生成基本客户端代码
if err := g.generateBaseClientCode(); err != nil {
return fmt.Errorf("生成基本客户端代码失败: %v", err)
}
// 为每种插件类型生成客户端代码
for pluginType, plugins := range pluginsByType {
typeDirName := strings.ToLower(string(pluginType))
typeDir := filepath.Join(pkgDir, typeDirName)
if err := os.MkdirAll(typeDir, 0755); err != nil {
return fmt.Errorf("创建 %s 目录失败: %v", typeDirName, err)
}
// 为每个插件生成客户端代码
for _, p := range plugins {
if err := g.generatePluginClientCode(p, typeDir); err != nil {
return fmt.Errorf("生成 %s 插件客户端代码失败: %v", p.Name(), err)
}
}
// 生成类型包装代码
if err := g.generateTypeWrapperCode(pluginType, plugins, typeDir); err != nil {
return fmt.Errorf("生成 %s 类型包装代码失败: %v", pluginType, err)
}
}
// 生成示例代码
if err := g.generateExampleCode(pluginsByType); err != nil {
return fmt.Errorf("生成示例代码失败: %v", err)
}
return nil
}
// generateModelCode 生成模型代码
func (g *ClientGenerator) generateModelCode() error {
// 生成插件信息模型
pluginInfoTemplate := `// 代码由客户端生成器自动生成,请勿手动修改
package models
// PluginInfo 插件信息
type PluginInfo struct {
Name string ` + "`json:\"name\"`" + `
Version string ` + "`json:\"version\"`" + `
Description string ` + "`json:\"description\"`" + `
Author string ` + "`json:\"author\"`" + `
Type string ` + "`json:\"type\"`" + `
Enabled bool ` + "`json:\"enabled\"`" + `
Config map[string]interface{} ` + "`json:\"config,omitempty\"`" + `
}
// OperationInfo 操作信息
type OperationInfo struct {
Name string ` + "`json:\"name\"`" + `
Description string ` + "`json:\"description\"`" + `
Parameters map[string]interface{} ` + "`json:\"parameters\"`" + `
Returns map[string]interface{} ` + "`json:\"returns\"`" + `
}
// GenericResponse 通用响应
type GenericResponse struct {
Success bool ` + "`json:\"success\"`" + `
Error string ` + "`json:\"error,omitempty\"`" + `
Result interface{} ` + "`json:\"result,omitempty\"`" + `
}
`
// 保存模型代码
modelFilePath := filepath.Join(g.outputPath, "client/pkg/models/models.go")
if err := os.WriteFile(modelFilePath, []byte(pluginInfoTemplate), 0644); err != nil {
return fmt.Errorf("写入模型代码失败: %v", err)
}
return nil
}
// generateBaseClientCode 生成基本客户端代码
func (g *ClientGenerator) generateBaseClientCode() error {
baseClientTemplate := `// 代码由客户端生成器自动生成,请勿手动修改
package client
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"path"
"time"
"{{.ModuleName}}/client/pkg/models"
)
// Client 插件系统客户端
type Client struct {
baseURL string
httpClient *http.Client
}
// ClientOptions 客户端选项
type ClientOptions struct {
Timeout time.Duration
}
// NewClient 创建一个新的客户端
func NewClient(baseURL string, options *ClientOptions) *Client {
client := &Client{
baseURL: baseURL,
httpClient: &http.Client{
Timeout: 30 * time.Second, // 默认超时时间
},
}
if options != nil {
if options.Timeout > 0 {
client.httpClient.Timeout = options.Timeout
}
}
return client
}
// GetAllPlugins 获取所有插件
func (c *Client) GetAllPlugins(ctx context.Context) ([]models.PluginInfo, error) {
var plugins []models.PluginInfo
err := c.get(ctx, "/plugins", nil, &plugins)
return plugins, err
}
// GetPluginsByType 按类型获取插件
func (c *Client) GetPluginsByType(ctx context.Context, pluginType models.PluginType) ([]models.PluginInfo, error) {
var plugins []models.PluginInfo
err := c.get(ctx, fmt.Sprintf("/plugins/type/%s", pluginType), nil, &plugins)
return plugins, err
}
// GetPluginInfo 获取插件信息
func (c *Client) GetPluginInfo(ctx context.Context, pluginName string) (*models.PluginInfo, error) {
var pluginInfo models.PluginInfo
err := c.get(ctx, fmt.Sprintf("/plugins/%s/info", pluginName), nil, &pluginInfo)
return &pluginInfo, err
}
// GetPluginActions 获取插件所有操作
func (c *Client) GetPluginActions(ctx context.Context, pluginName string) (map[string]models.OperationInfo, error) {
var operations map[string]models.OperationInfo
err := c.get(ctx, fmt.Sprintf("/plugins/%s/actions", pluginName), nil, &operations)
return operations, err
}
// ExecutePluginAction 执行插件操作
func (c *Client) ExecutePluginAction(ctx context.Context, pluginName, action string, params interface{}) (*models.GenericResponse, error) {
var response models.GenericResponse
err := c.post(ctx, fmt.Sprintf("/plugins/%s/execute/%s", pluginName, action), params, &response)
return &response, err
}
// get 发送GET请求
func (c *Client) get(ctx context.Context, apiPath string, query url.Values, result interface{}) error {
return c.request(ctx, http.MethodGet, apiPath, query, nil, result)
}
// post 发送POST请求
func (c *Client) post(ctx context.Context, apiPath string, body interface{}, result interface{}) error {
return c.request(ctx, http.MethodPost, apiPath, nil, body, result)
}
// request 发送请求
func (c *Client) request(ctx context.Context, method, apiPath string, query url.Values, body interface{}, result interface{}) error {
// 构建URL
u, err := url.Parse(c.baseURL)
if err != nil {
return fmt.Errorf("解析URL失败: %v", err)
}
u.Path = path.Join(u.Path, apiPath)
if query != nil {
u.RawQuery = query.Encode()
}
// 准备请求体
var bodyReader io.Reader
if body != nil {
bodyData, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("序列化请求体失败: %v", err)
}
bodyReader = bytes.NewReader(bodyData)
}
// 创建请求
req, err := http.NewRequestWithContext(ctx, method, u.String(), bodyReader)
if err != nil {
return fmt.Errorf("创建请求失败: %v", err)
}
// 设置Content-Type
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
// 发送请求
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("发送请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应体
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("读取响应体失败: %v", err)
}
// 检查响应状态码
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
var errResp struct {
Error string ` + "`json:\"error\"`" + `
}
if err := json.Unmarshal(respBody, &errResp); err == nil && errResp.Error != "" {
return fmt.Errorf("API错误: %s", errResp.Error)
}
return fmt.Errorf("HTTP错误: %s", resp.Status)
}
// 解析响应体
if result != nil && len(respBody) > 0 {
if err := json.Unmarshal(respBody, result); err != nil {
return fmt.Errorf("解析响应体失败: %v", err)
}
}
return nil
}
`
// 准备模板数据
data := struct {
ModuleName string
}{
ModuleName: g.moduleName,
}
// 编译模板
tmpl, err := template.New("baseClient").Parse(baseClientTemplate)
if err != nil {
return fmt.Errorf("解析基本客户端模板失败: %v", err)
}
// 创建文件
baseClientPath := filepath.Join(g.outputPath, "client/client.go")
file, err := os.Create(baseClientPath)
if err != nil {
return fmt.Errorf("创建基本客户端文件失败: %v", err)
}
defer file.Close()
// 执行模板
if err := tmpl.Execute(file, data); err != nil {
return fmt.Errorf("执行基本客户端模板失败: %v", err)
}
return nil
}
// generatePluginClientCode 为特定插件生成客户端代码
func (g *ClientGenerator) generatePluginClientCode(p Plugin, outputDir string) error {
// 获取插件信息
pluginName := p.Name()
operations := p.GetAllOperations()
// 准备模板数据
type OperationData struct {
Name string
MethodName string
Description string
HasParameters bool
Parameters []map[string]interface{}
ParamsVarName string
ReturnType string
ReturnVarName string
HasContext bool
ReturnExists bool
HasComplexReturn bool
}
type PluginData struct {
PluginName string
ClassName string
Version string
ModuleName string
Description string
Author string
Type string
Operations []OperationData
ImportTime bool
ImportContext bool
GeneratedDate string
}
// 准备操作数据
operations_data := make([]OperationData, 0)
importTime := false
importContext := false
for opName, opInfo := range operations {
// 跳过内部操作
if strings.HasPrefix(opName, "_") {
continue
}
// 生成方法名
methodName := strings.Title(opName)
// 确定方法是否接受context参数
hasContext := false
if params, ok := opInfo["parameters"].(map[string]interface{}); ok {
for _, paramInfo := range params {
if info, ok := paramInfo.(map[string]interface{}); ok {
if hasCtx, ok := info["hasContext"].(bool); ok && hasCtx {
hasContext = true
importContext = true
break
}
}
}
}
// 分析参数
parameters := make([]map[string]interface{}, 0)
hasParameters := false
paramsVarName := "params"
if params, ok := opInfo["parameters"].(map[string]interface{}); ok && len(params) > 0 {
for paramName, paramInfo := range params {
if info, ok := paramInfo.(map[string]interface{}); ok {
paramType := getMapString(info, "type", "string")
desc := getMapString(info, "description", "")
required := getMapBool(info, "required", false)
// 检查是否为结构体参数
if paramType == "struct" || paramType == "object" {
// 将参数名作为变量名
paramsVarName = paramName
}
parameters = append(parameters, map[string]interface{}{
"name": paramName,
"type": convertToGoType(paramType),
"description": desc,
"required": required,
})
hasParameters = true
}
}
}
// 分析返回值
returnType := "interface{}"
returnVarName := "result"
hasComplexReturn := false
returnExists := false
if returns, ok := opInfo["returns"].(map[string]interface{}); ok && len(returns) > 0 {
for retName, retInfo := range returns {
if retName == "error" {
continue
}
returnExists = true
if info, ok := retInfo.(map[string]interface{}); ok {
retType := getMapString(info, "type", "string")
// 如果返回值类型为对象,认为是复杂返回
if retType == "object" || retType == "struct" {
hasComplexReturn = true
}
returnType = convertToGoType(retType)
returnVarName = retName
break
}
}
}
operations_data = append(operations_data, OperationData{
Name: opName,
MethodName: methodName,
Description: getMapString(opInfo, "description", ""),
HasParameters: hasParameters,
Parameters: parameters,
ParamsVarName: paramsVarName,
ReturnType: returnType,
ReturnVarName: returnVarName,
HasContext: hasContext,
ReturnExists: returnExists,
HasComplexReturn: hasComplexReturn,
})
}
// 创建插件数据
data := PluginData{
PluginName: pluginName,
ClassName: strings.Title(pluginName),
Version: p.Version(),
ModuleName: g.moduleName,
Description: p.Description(),
Author: p.Author(),
Type: string(p.Type()),
Operations: operations_data,
ImportTime: importTime,
ImportContext: importContext,
GeneratedDate: time.Now().Format("2006-01-02 15:04:05"),
}
// 定义模板
pluginClientTemplate := `// 代码由客户端生成器自动生成,请勿手动修改
// 生成时间: {{.GeneratedDate}}
package {{.Type | lower}}
import (
"context"
"encoding/json"
"fmt"{{if .ImportTime}}
"time"{{end}}
"{{.ModuleName}}/client/pkg/models"
)
// {{.ClassName}} {{.Description}}
type {{.ClassName}} struct {
client *Client
pluginName string
}
// New{{.ClassName}} 创建{{.ClassName}}客户端
func New{{.ClassName}}(client *Client) *{{.ClassName}} {
return &{{.ClassName}}{
client: client,
pluginName: "{{.PluginName}}",
}
}
// GetInfo 获取插件信息
func (c *{{.ClassName}}) GetInfo(ctx context.Context) (*models.PluginInfo, error) {
return c.client.GetPluginInfo(ctx, c.pluginName)
}
// GetAllOperations 获取所有操作信息
func (c *{{.ClassName}}) GetAllOperations(ctx context.Context) (map[string]models.OperationInfo, error) {
return c.client.GetPluginActions(ctx, c.pluginName)
}
{{range .Operations}}
// {{.MethodName}} {{.Description}}
func (c *{{$.ClassName}}) {{.MethodName}}({{if .HasContext}}ctx context.Context, {{end}}{{if .HasParameters}}{{.ParamsVarName}} {{if .HasComplexReturn}}map[string]interface{}{{else}}interface{}{{end}}{{end}}) ({{if .ReturnExists}}{{.ReturnType}}, {{end}}error) {
resp, err := c.client.ExecutePluginAction({{if .HasContext}}ctx{{else}}context.Background(){{end}}, c.pluginName, "{{.Name}}", {{if .HasParameters}}{{.ParamsVarName}}{{else}}nil{{end}})
if err != nil {
return {{if .ReturnExists}}{{if eq .ReturnType "string"}}""{{else if eq .ReturnType "int"}}0{{else if eq .ReturnType "bool"}}false{{else if eq .ReturnType "float64"}}0.0{{else}}nil{{end}}, {{end}}err
}
if !resp.Success {
return {{if .ReturnExists}}{{if eq .ReturnType "string"}}""{{else if eq .ReturnType "int"}}0{{else if eq .ReturnType "bool"}}false{{else if eq .ReturnType "float64"}}0.0{{else}}nil{{end}}, {{end}}fmt.Errorf("执行操作失败: %s", resp.Error)
}
{{if .ReturnExists}}
// 提取结果
{{if eq .ReturnType "string"}}
if str, ok := resp.Result.(string); ok {
return str, nil
}
return "", fmt.Errorf("结果类型错误")
{{else if eq .ReturnType "int"}}
if num, ok := resp.Result.(float64); ok {
return int(num), nil
}
return 0, fmt.Errorf("结果类型错误")
{{else if eq .ReturnType "bool"}}
if b, ok := resp.Result.(bool); ok {
return b, nil
}
return false, fmt.Errorf("结果类型错误")
{{else if eq .ReturnType "float64"}}
if num, ok := resp.Result.(float64); ok {
return num, nil
}
return 0.0, fmt.Errorf("结果类型错误")
{{else}}
// 处理复杂返回类型
var result {{.ReturnType}}
resultData, err := json.Marshal(resp.Result)
if err != nil {
return nil, fmt.Errorf("序列化结果失败: %v", err)
}
if err := json.Unmarshal(resultData, &result); err != nil {
return nil, fmt.Errorf("反序列化结果失败: %v", err)
}
return result, nil
{{end}}
{{else}}
return nil
{{end}}
}
{{end}}
`
// 编译模板
funcMap := template.FuncMap{
"lower": strings.ToLower,
}
tmpl, err := template.New("pluginClient").Funcs(funcMap).Parse(pluginClientTemplate)
if err != nil {
return fmt.Errorf("解析插件客户端模板失败: %v", err)
}
// 创建文件
fileName := strings.ToLower(pluginName) + ".go"
filePath := filepath.Join(outputDir, fileName)
file, err := os.Create(filePath)
if err != nil {
return fmt.Errorf("创建插件客户端文件失败: %v", err)
}
defer file.Close()
// 执行模板
if err := tmpl.Execute(file, data); err != nil {
return fmt.Errorf("执行插件客户端模板失败: %v", err)
}
return nil
}
// generateTypeWrapperCode 生成类型包装代码
func (g *ClientGenerator) generateTypeWrapperCode(pluginType PluginType, plugins []Plugin, outputDir string) error {
// 准备模板数据
type PluginData struct {
ClassName string
PluginName string
}
type TypeData struct {
TypeName string
ModuleName string
Plugins []PluginData
GeneratedDate string
}
// 构造插件数据
pluginsData := make([]PluginData, 0, len(plugins))
for _, p := range plugins {
pluginsData = append(pluginsData, PluginData{
ClassName: strings.Title(p.Name()),
PluginName: p.Name(),
})
}
data := TypeData{
TypeName: string(pluginType),
ModuleName: g.moduleName,
Plugins: pluginsData,
GeneratedDate: time.Now().Format("2006-01-02 15:04:05"),
}
// 定义模板
typeWrapperTemplate := `// 代码由客户端生成器自动生成,请勿手动修改
// 生成时间: {{.GeneratedDate}}
package {{.TypeName | lower}}
import (
"{{.ModuleName}}/client"
)
// Client {{.TypeName}}类型插件的客户端包装器
type Client struct {
client *client.Client
{{range .Plugins}}
{{.ClassName}} *{{.ClassName}}{{end}}
}
// NewClient 创建{{.TypeName}}类型插件的客户端包装器
func NewClient(baseClient *client.Client) *Client {
c := &Client{
client: baseClient,
}
{{range .Plugins}}
c.{{.ClassName}} = New{{.ClassName}}(baseClient){{end}}
return c
}
`
// 编译模板
funcMap := template.FuncMap{
"lower": strings.ToLower,
}
tmpl, err := template.New("typeWrapper").Funcs(funcMap).Parse(typeWrapperTemplate)
if err != nil {
return fmt.Errorf("解析类型包装器模板失败: %v", err)
}
// 创建文件
fileName := "client.go"
filePath := filepath.Join(outputDir, fileName)
file, err := os.Create(filePath)
if err != nil {
return fmt.Errorf("创建类型包装器文件失败: %v", err)
}
defer file.Close()
// 执行模板
if err := tmpl.Execute(file, data); err != nil {
return fmt.Errorf("执行类型包装器模板失败: %v", err)
}
return nil
}
// generateExampleCode 生成示例代码
func (g *ClientGenerator) generateExampleCode(pluginsByType map[PluginType][]Plugin) error {
// 准备模板数据
type TypeData struct {
TypeName string
HasPlugin bool
}
type ExampleData struct {
ModuleName string
TypesData []TypeData
GeneratedDate string
}
// 构造类型数据
typesData := make([]TypeData, 0)
for pType, plugins := range pluginsByType {
typesData = append(typesData, TypeData{
TypeName: string(pType),
HasPlugin: len(plugins) > 0,
})
}
data := ExampleData{
ModuleName: g.moduleName,
TypesData: typesData,
GeneratedDate: time.Now().Format("2006-01-02 15:04:05"),
}
// 定义模板
exampleTemplate := `// 代码由客户端生成器自动生成,请勿手动修改
// 生成时间: {{.GeneratedDate}}
package main
import (
"context"
"fmt"
"log"
"time"
"{{.ModuleName}}/client"
{{range .TypesData}}{{if .HasPlugin}}
"{{$.ModuleName}}/client/pkg/{{.TypeName | lower}}"{{end}}{{end}}
"{{.ModuleName}}/client/pkg/models"
)
func main() {
// 创建客户端
baseClient := client.NewClient("http://localhost:8080/api", &client.ClientOptions{
Timeout: 30 * time.Second,
})
// 创建上下文
ctx := context.Background()
// 获取所有插件
plugins, err := baseClient.GetAllPlugins(ctx)
if err != nil {
log.Fatalf("获取插件失败: %v", err)
}
fmt.Printf("发现 %d 个插件\n", len(plugins))
for _, plugin := range plugins {
fmt.Printf("- %s (版本: %s, 类型: %s)\n", plugin.Name, plugin.Version, plugin.Type)
}
// 按类型使用插件
{{range .TypesData}}{{if .HasPlugin}}
// {{.TypeName}}类型插件
{{.TypeName | lower}}Client := {{.TypeName | lower}}.NewClient(baseClient)
// 使用{{.TypeName}}客户端...{{end}}
{{end}}
// 其他示例...
fmt.Println("示例结束")
}
`
// 编译模板
funcMap := template.FuncMap{
"lower": strings.ToLower,
}
tmpl, err := template.New("example").Funcs(funcMap).Parse(exampleTemplate)
if err != nil {
return fmt.Errorf("解析示例模板失败: %v", err)
}
// 创建文件
filePath := filepath.Join(g.outputPath, "client/example/main.go")
// 确保目录存在
exampleDir := filepath.Join(g.outputPath, "client/example")
if err := os.MkdirAll(exampleDir, 0755); err != nil {
return fmt.Errorf("创建示例目录失败: %v", err)
}
file, err := os.Create(filePath)
if err != nil {
return fmt.Errorf("创建示例文件失败: %v", err)
}
defer file.Close()
// 执行模板
if err := tmpl.Execute(file, data); err != nil {
return fmt.Errorf("执行示例模板失败: %v", err)
}
return nil
}
// 辅助函数
// convertToGoType 将插件系统类型转换为Go类型
func convertToGoType(pluginType string) string {
switch pluginType {
case "string":
return "string"
case "integer":
return "int"
case "float":
return "float64"
case "boolean":
return "bool"
case "array":
return "[]interface{}"
case "object", "struct":
return "map[string]interface{}"
default:
return "interface{}"
}
}