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{}" } }