- 在插件管理器中引入 DynamicPlugin 结构体,支持动态加载和管理插件,提升插件的灵活性和可扩展性。 - 更新插件接口,添加插件名称、版本、描述、作者、类型和启用状态的获取和设置方法,增强插件信息的管理能力。 - 修改现有插件实现,确保兼容新的动态插件结构,提升插件的统一性和可维护性。 - 更新示例程序,展示如何使用新的动态插件功能,提升用户体验。 此更新提升了插件系统的灵活性和可扩展性,便于开发者更好地管理和使用插件功能。
844 lines
22 KiB
Go
844 lines
22 KiB
Go
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{}"
|
||
}
|
||
}
|