package plugin import ( "fmt" "os" "path/filepath" "strings" "time" ) // DocGenerator 文档生成器 // 用于生成插件系统的API文档 type DocGenerator struct { pm *PluginManager // 插件管理器 outputPath string // 输出路径 } // NewDocGenerator 创建一个新的文档生成器 func NewDocGenerator(pm *PluginManager, outputPath string) *DocGenerator { return &DocGenerator{ pm: pm, outputPath: outputPath, } } // GenerateAllDocs 为所有插件生成文档 func (g *DocGenerator) GenerateAllDocs() error { // 确保输出目录存在 if err := os.MkdirAll(g.outputPath, 0755); err != nil { return fmt.Errorf("创建输出目录失败: %v", err) } // 生成索引文档 if err := g.generateIndexDoc(); err != nil { return err } // 为每个插件生成文档 plugins := g.pm.GetAllPlugins() for _, p := range plugins { if err := g.generatePluginDoc(p); err != nil { return err } } // 生成按类型分组的文档 if err := g.generatePluginsByTypeDoc(); err != nil { return err } return nil } // generateIndexDoc 生成索引文档 func (g *DocGenerator) generateIndexDoc() error { indexPath := filepath.Join(g.outputPath, "index.md") file, err := os.Create(indexPath) if err != nil { return fmt.Errorf("创建索引文档失败: %v", err) } defer file.Close() // 获取所有插件信息 plugins := g.pm.GetPluginInfos() // 写入标题 file.WriteString("# 插件系统API文档\n\n") file.WriteString(fmt.Sprintf("生成时间: %s\n\n", time.Now().Format("2006-01-02 15:04:05"))) // 插件计数 file.WriteString(fmt.Sprintf("## 插件总览\n\n总共发现 %d 个插件\n\n", len(plugins))) // 写入插件列表 file.WriteString("## 插件列表\n\n") file.WriteString("| 插件名称 | 版本 | 类型 | 描述 | 作者 | 状态 |\n") file.WriteString("|---------|------|------|------|------|------|\n") for _, info := range plugins { status := "启用" if !info.Enabled { status = "禁用" } // 使用相对链接 pluginLink := fmt.Sprintf("[%s](%s.md)", info.Name, info.Name) file.WriteString(fmt.Sprintf("| %s | %s | %s | %s | %s | %s |\n", pluginLink, info.Version, info.Type, info.Description, info.Author, status)) } // 按类型分组的链接 file.WriteString("\n## 按类型查看插件\n\n") file.WriteString("- [按类型分组查看所有插件](plugins_by_type.md)\n\n") return nil } // generatePluginDoc 为单个插件生成文档 func (g *DocGenerator) generatePluginDoc(p Plugin) error { pluginName := p.Name() docPath := filepath.Join(g.outputPath, pluginName+".md") file, err := os.Create(docPath) if err != nil { return fmt.Errorf("创建插件文档失败: %v", err) } defer file.Close() // 写入插件标题和基本信息 file.WriteString(fmt.Sprintf("# %s\n\n", pluginName)) file.WriteString(fmt.Sprintf("- **版本**: %s\n", p.Version())) file.WriteString(fmt.Sprintf("- **描述**: %s\n", p.Description())) file.WriteString(fmt.Sprintf("- **作者**: %s\n", p.Author())) file.WriteString(fmt.Sprintf("- **类型**: %s\n", p.Type())) file.WriteString(fmt.Sprintf("- **状态**: %s\n\n", boolToStatus(p.IsEnabled()))) // 获取插件的所有操作 operations := p.GetAllOperations() file.WriteString(fmt.Sprintf("## 支持的操作\n\n此插件支持 %d 个操作:\n\n", len(operations))) // 操作列表 for opName := range operations { file.WriteString(fmt.Sprintf("- [%s](#%s)\n", opName, strings.ToLower(opName))) } // 详细操作文档 file.WriteString("\n## 操作详情\n\n") for opName, opInfo := range operations { file.WriteString(fmt.Sprintf("### %s\n\n", opName)) // 操作描述 if desc, ok := opInfo["description"].(string); ok { file.WriteString(fmt.Sprintf("%s\n\n", desc)) } // 参数表格 file.WriteString("#### 参数\n\n") if params, ok := opInfo["parameters"].(map[string]interface{}); ok && len(params) > 0 { file.WriteString("| 参数名 | 类型 | 必填 | 描述 |\n") file.WriteString("|--------|------|------|------|\n") for paramName, paramInfo := range params { if info, ok := paramInfo.(map[string]interface{}); ok { paramType := getMapString(info, "type", "未知") required := getMapBool(info, "required", false) requiredStr := "否" if required { requiredStr = "是" } description := getMapString(info, "description", "") file.WriteString(fmt.Sprintf("| %s | %s | %s | %s |\n", paramName, paramType, requiredStr, description)) // 如果是结构体,则显示其字段 if paramType == "struct" || paramType == "object" { if fields, ok := info["fields"].(map[string]interface{}); ok { file.WriteString("\n**字段详情:**\n\n") file.WriteString("| 字段名 | 类型 | 必填 | 描述 |\n") file.WriteString("|--------|------|------|------|\n") for fieldName, fieldInfo := range fields { if fi, ok := fieldInfo.(map[string]interface{}); ok { fieldType := getMapString(fi, "type", "未知") fieldRequired := getMapBool(fi, "required", false) fieldRequiredStr := "否" if fieldRequired { fieldRequiredStr = "是" } fieldDesc := getMapString(fi, "description", "") file.WriteString(fmt.Sprintf("| %s | %s | %s | %s |\n", fieldName, fieldType, fieldRequiredStr, fieldDesc)) } } file.WriteString("\n") } } } } } else { file.WriteString("此操作不需要参数\n\n") } // 返回值 file.WriteString("#### 返回值\n\n") if returns, ok := opInfo["returns"].(map[string]interface{}); ok && len(returns) > 0 { file.WriteString("| 名称 | 类型 | 描述 |\n") file.WriteString("|------|------|------|\n") for retName, retInfo := range returns { if info, ok := retInfo.(map[string]interface{}); ok { retType := getMapString(info, "type", "未知") description := getMapString(info, "description", "") file.WriteString(fmt.Sprintf("| %s | %s | %s |\n", retName, retType, description)) // 如果返回值是对象,显示其属性 if retType == "object" && info["properties"] != nil { if props, ok := info["properties"].(map[string]interface{}); ok { file.WriteString("\n**属性详情:**\n\n") file.WriteString("| 属性名 | 类型 | 描述 |\n") file.WriteString("|--------|------|------|\n") for propName, propInfo := range props { if pi, ok := propInfo.(map[string]interface{}); ok { propType := getMapString(pi, "type", "未知") propDesc := getMapString(pi, "description", "") file.WriteString(fmt.Sprintf("| %s | %s | %s |\n", propName, propType, propDesc)) } } file.WriteString("\n") } } } } } else { file.WriteString("此操作没有返回值\n\n") } // 示例(如果有) if examples, ok := opInfo["examples"].([]interface{}); ok && len(examples) > 0 { file.WriteString("#### 示例\n\n") for i, example := range examples { if ex, ok := example.(map[string]interface{}); ok { file.WriteString(fmt.Sprintf("**示例 %d:**\n\n", i+1)) if desc, ok := ex["description"].(string); ok { file.WriteString(fmt.Sprintf("%s\n\n", desc)) } if req, ok := ex["request"].(string); ok { file.WriteString("请求:\n```json\n") file.WriteString(req) file.WriteString("\n```\n\n") } if resp, ok := ex["response"].(string); ok { file.WriteString("响应:\n```json\n") file.WriteString(resp) file.WriteString("\n```\n\n") } } } } file.WriteString("\n---\n\n") } // 链接回索引 file.WriteString("\n[返回索引](index.md)\n") return nil } // generatePluginsByTypeDoc 生成按类型分组的插件文档 func (g *DocGenerator) generatePluginsByTypeDoc() error { filePath := filepath.Join(g.outputPath, "plugins_by_type.md") file, err := os.Create(filePath) if err != nil { return fmt.Errorf("创建类型分组文档失败: %v", err) } defer file.Close() // 写入标题 file.WriteString("# 按类型分组的插件\n\n") // 获取所有插件类型 allPlugins := g.pm.GetAllPlugins() typeMap := make(map[PluginType][]Plugin) for _, p := range allPlugins { pluginType := p.Type() typeMap[pluginType] = append(typeMap[pluginType], p) } // 目录 file.WriteString("## 目录\n\n") for pType := range typeMap { file.WriteString(fmt.Sprintf("- [%s](#%s)\n", pType, strings.ToLower(string(pType)))) } file.WriteString("\n") // 按类型分组列出插件 for pType, plugins := range typeMap { file.WriteString(fmt.Sprintf("## %s\n\n", pType)) file.WriteString(fmt.Sprintf("此类型共有 %d 个插件\n\n", len(plugins))) file.WriteString("| 插件名称 | 版本 | 描述 | 作者 | 状态 |\n") file.WriteString("|---------|------|------|------|------|\n") for _, p := range plugins { status := "启用" if !p.IsEnabled() { status = "禁用" } // 使用相对链接 pluginLink := fmt.Sprintf("[%s](%s.md)", p.Name(), p.Name()) file.WriteString(fmt.Sprintf("| %s | %s | %s | %s | %s |\n", pluginLink, p.Version(), p.Description(), p.Author(), status)) } file.WriteString("\n") } // 链接回索引 file.WriteString("\n[返回索引](index.md)\n") return nil } // Utility functions // boolToStatus 将布尔值转换为状态字符串 func boolToStatus(enabled bool) string { if enabled { return "启用" } return "禁用" } // getMapString 安全地从map中获取字符串值 func getMapString(m map[string]interface{}, key, defaultVal string) string { if val, ok := m[key].(string); ok { return val } return defaultVal } // getMapBool 安全地从map中获取布尔值 func getMapBool(m map[string]interface{}, key string, defaultVal bool) bool { if val, ok := m[key].(bool); ok { return val } return defaultVal }