You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
5.3 KiB
200 lines
5.3 KiB
3 days ago
|
package service
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// SwaggerInfo Swagger注释信息
|
||
|
type SwaggerInfo struct {
|
||
|
Summary string
|
||
|
Description string
|
||
|
Tags string
|
||
|
Router string
|
||
|
Method string
|
||
|
}
|
||
|
|
||
|
// SwaggerParser Swagger注释解析器
|
||
|
type SwaggerParser struct {
|
||
|
swaggerMap map[string]SwaggerInfo
|
||
|
}
|
||
|
|
||
|
// NewSwaggerParser 创建Swagger解析器
|
||
|
func NewSwaggerParser() *SwaggerParser {
|
||
|
parser := &SwaggerParser{
|
||
|
swaggerMap: make(map[string]SwaggerInfo),
|
||
|
}
|
||
|
fmt.Println("🔧 SwaggerParser创建成功")
|
||
|
return parser
|
||
|
}
|
||
|
|
||
|
// ParseControllerDirectory 解析Controller目录下的所有文件
|
||
|
func (sp *SwaggerParser) ParseControllerDirectory(dir string) error {
|
||
|
fmt.Printf("🔍 开始解析Controller目录: %s\n", dir)
|
||
|
|
||
|
fileCount := 0
|
||
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||
|
if err != nil {
|
||
|
fmt.Printf("❌ 访问文件失败: %s, 错误: %v\n", path, err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if !info.IsDir() && strings.HasSuffix(path, ".go") {
|
||
|
fmt.Printf("📄 解析文件: %s\n", path)
|
||
|
fileCount++
|
||
|
return sp.parseControllerFile(path)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
fmt.Printf("📊 解析完成,共处理 %d 个Go文件\n", fileCount)
|
||
|
fmt.Printf("📋 解析到的Swagger信息数量: %d\n", len(sp.swaggerMap))
|
||
|
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// parseControllerFile 解析单个Controller文件
|
||
|
func (sp *SwaggerParser) parseControllerFile(filePath string) error {
|
||
|
file, err := os.Open(filePath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer file.Close()
|
||
|
|
||
|
scanner := bufio.NewScanner(file)
|
||
|
var currentComment []string
|
||
|
|
||
|
for scanner.Scan() {
|
||
|
line := scanner.Text()
|
||
|
|
||
|
// 检查是否是注释行
|
||
|
if strings.HasPrefix(strings.TrimSpace(line), "//") {
|
||
|
currentComment = append(currentComment, line)
|
||
|
} else {
|
||
|
// 如果不是注释行,处理之前收集的注释
|
||
|
if len(currentComment) > 0 {
|
||
|
swaggerInfo := sp.extractSwaggerInfo(currentComment)
|
||
|
if swaggerInfo.Router != "" {
|
||
|
// 使用Router路径作为key
|
||
|
key := fmt.Sprintf("%s %s", swaggerInfo.Method, swaggerInfo.Router)
|
||
|
sp.swaggerMap[key] = swaggerInfo
|
||
|
}
|
||
|
currentComment = nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return scanner.Err()
|
||
|
}
|
||
|
|
||
|
// extractSwaggerInfo 从注释中提取Swagger信息
|
||
|
func (sp *SwaggerParser) extractSwaggerInfo(comments []string) SwaggerInfo {
|
||
|
info := SwaggerInfo{}
|
||
|
|
||
|
for _, comment := range comments {
|
||
|
comment = strings.TrimSpace(comment)
|
||
|
|
||
|
// 提取@Summary
|
||
|
if strings.HasPrefix(comment, "// @Summary") {
|
||
|
info.Summary = strings.TrimSpace(strings.TrimPrefix(comment, "// @Summary"))
|
||
|
}
|
||
|
|
||
|
// 提取@Description
|
||
|
if strings.HasPrefix(comment, "// @Description") {
|
||
|
info.Description = strings.TrimSpace(strings.TrimPrefix(comment, "// @Description"))
|
||
|
}
|
||
|
|
||
|
// 提取@Tags
|
||
|
if strings.HasPrefix(comment, "// @Tags") {
|
||
|
info.Tags = strings.TrimSpace(strings.TrimPrefix(comment, "// @Tags"))
|
||
|
}
|
||
|
|
||
|
// 提取@Router
|
||
|
if strings.HasPrefix(comment, "// @Router") {
|
||
|
routerLine := strings.TrimSpace(strings.TrimPrefix(comment, "// @Router"))
|
||
|
// 解析格式:/path [method]
|
||
|
parts := strings.Fields(routerLine)
|
||
|
if len(parts) >= 2 {
|
||
|
info.Router = parts[0]
|
||
|
// 提取HTTP方法
|
||
|
if len(parts) > 1 {
|
||
|
methodPart := parts[1]
|
||
|
if strings.HasPrefix(methodPart, "[") && strings.HasSuffix(methodPart, "]") {
|
||
|
info.Method = strings.ToUpper(strings.Trim(methodPart, "[]"))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return info
|
||
|
}
|
||
|
|
||
|
// GetSummary 根据路径和方法获取Summary
|
||
|
func (sp *SwaggerParser) GetSummary(method, path string) string {
|
||
|
// 尝试精确匹配
|
||
|
key := fmt.Sprintf("%s %s", strings.ToUpper(method), path)
|
||
|
if info, exists := sp.swaggerMap[key]; exists {
|
||
|
return info.Summary
|
||
|
}
|
||
|
|
||
|
// 尝试匹配不带/api前缀的路径
|
||
|
if strings.HasPrefix(path, "/api/") {
|
||
|
pathWithoutAPI := strings.TrimPrefix(path, "/api")
|
||
|
keyWithoutAPI := fmt.Sprintf("%s %s", strings.ToUpper(method), pathWithoutAPI)
|
||
|
if info, exists := sp.swaggerMap[keyWithoutAPI]; exists {
|
||
|
return info.Summary
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 尝试模糊匹配(处理路径参数)
|
||
|
for swaggerKey, info := range sp.swaggerMap {
|
||
|
if sp.matchRoute(swaggerKey, fmt.Sprintf("%s %s", strings.ToUpper(method), path)) {
|
||
|
return info.Summary
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 尝试匹配转换后的路径(将:var转换为{var})
|
||
|
convertedPath := sp.convertPathFormat(path)
|
||
|
if convertedPath != path {
|
||
|
convertedKey := fmt.Sprintf("%s %s", strings.ToUpper(method), convertedPath)
|
||
|
if info, exists := sp.swaggerMap[convertedKey]; exists {
|
||
|
return info.Summary
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// convertPathFormat 转换路径格式:将:var格式改为{var}格式
|
||
|
func (sp *SwaggerParser) convertPathFormat(path string) string {
|
||
|
re := regexp.MustCompile(`:([a-zA-Z0-9_]+)`)
|
||
|
return re.ReplaceAllString(path, "{$1}")
|
||
|
}
|
||
|
|
||
|
// matchRoute 匹配路由(处理路径参数)
|
||
|
func (sp *SwaggerParser) matchRoute(pattern, route string) bool {
|
||
|
// 将路径参数转换为正则表达式
|
||
|
// 例如:/users/:id -> /users/[^/]+
|
||
|
patternRegex := regexp.MustCompile(`:[a-zA-Z0-9_]+`)
|
||
|
regexPattern := patternRegex.ReplaceAllString(pattern, `[^/]+`)
|
||
|
|
||
|
// 创建正则表达式
|
||
|
re, err := regexp.Compile("^" + regexPattern + "$")
|
||
|
if err != nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return re.MatchString(route)
|
||
|
}
|
||
|
|
||
|
// GetAllSwaggerInfo 获取所有Swagger信息
|
||
|
func (sp *SwaggerParser) GetAllSwaggerInfo() map[string]SwaggerInfo {
|
||
|
return sp.swaggerMap
|
||
|
}
|