增强插件系统:添加参数验证功能
- 在插件助手中新增 ValidateParams 函数,用于验证传入参数是否符合定义,确保参数的完整性和正确性。 - 更新 ExecuteAction 方法,集成参数验证逻辑,提升插件执行的安全性和可靠性。 - 示例程序中添加新的选项,展示如何生成插件API文档和OpenAPI/Swagger文档,增强用户体验。 此更新提升了插件系统的健壮性,便于开发者更好地管理和使用插件功能。
This commit is contained in:
853
examples/plugin/client_generator.go
Normal file
853
examples/plugin/client_generator.go
Normal file
@@ -0,0 +1,853 @@
|
||||
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\"`" + `
|
||||
}
|
||||
|
||||
// PluginType 插件类型
|
||||
type PluginType string
|
||||
|
||||
const (
|
||||
PluginTypeGeneral PluginType = "General"
|
||||
PluginTypeStorage PluginType = "Storage"
|
||||
PluginTypeUtils PluginType = "Utils"
|
||||
PluginTypeNetwork PluginType = "Network"
|
||||
)
|
||||
|
||||
// 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{}"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user