package swagger import ( "fmt" "gitea.redpowerfuture.com/red-future/common/consul" "gitea.redpowerfuture.com/red-future/common/http" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/util/gconv" "strings" ) type swaggerDoc struct { OpenAPI string `json:"openapi"` Info map[string]interface{} `json:"info"` Paths map[string]interface{} `json:"paths"` Components map[string]interface{} `json:"components"` // 可以根据需要定义更多字段 } func updateRefsRecursively(data interface{}, serviceName string) { switch v := data.(type) { case map[string]interface{}: for key, value := range v { if key == "$ref" { refStr, ok := value.(string) if ok { // 处理 OpenAPI 3.0+ 的引用 if strings.HasPrefix(refStr, "#/components/schemas/") { originalSchemaName := strings.TrimPrefix(refStr, "#/components/schemas/") newRef := fmt.Sprintf("#/components/schemas/%s_%s", serviceName, originalSchemaName) v[key] = newRef } // 处理 Swagger 2.0 的引用 if strings.HasPrefix(refStr, "#/definitions/") { originalSchemaName := strings.TrimPrefix(refStr, "#/definitions/") newRef := fmt.Sprintf("#/components/schemas/%s_%s", serviceName, originalSchemaName) v[key] = newRef } } } else { updateRefsRecursively(value, serviceName) } } case []interface{}: for _, item := range v { updateRefsRecursively(item, serviceName) } } } func init() { http.Httpserver.SetSwaggerPath("/docs") //api文档访问路径 http.Httpserver.SetSwaggerUITemplate(` SwaggerUI
`) http.Httpserver.BindHandler("/swagger", func(r *ghttp.Request) { services := g.Cfg().MustGet(r.GetCtx(), "consul.services").Slice() aggregatedDoc := &swaggerDoc{ OpenAPI: "3.0.0", // 假设统一为 3.0.0 版本 Info: map[string]interface{}{ "title": "Aggregated API Documentation", "version": "1.0.0", "description": "This document aggregates Swagger docs from all microservices.", }, Paths: make(map[string]interface{}), Components: make(map[string]interface{}), } for _, v := range services { serviceName := gconv.String(gconv.Map(v)["name"]) instanceAddr, err := consul.GetInstanceAddr(r.GetCtx(), serviceName) if err != nil || instanceAddr == "" { continue } targetURL := fmt.Sprintf("%s://%s/api.json", "http", instanceAddr) // 假设文档地址是 /swagger/doc.json var svcDoc swaggerDoc result := g.Client().GetContent(r.GetCtx(), targetURL) gconv.Struct(result, &svcDoc) if err != nil { continue } // 1. 合并 Paths,为每个路径添加服务名前缀 for path, pathItem := range svcDoc.Paths { prefixedPath := fmt.Sprintf("/%s%s", serviceName, path) updateRefsRecursively(pathItem, serviceName) aggregatedDoc.Paths[prefixedPath] = pathItem } // 2. 合并 Components (Schemas, Responses, etc.) // 注意:这里需要处理命名冲突,一个简单的方式是在组件名前加上服务名前缀 if svcDoc.Components != nil { for compType, compDefs := range svcDoc.Components { compDefsMap, ok := compDefs.(map[string]interface{}) if !ok { continue } if _, exists := aggregatedDoc.Components[compType]; !exists { aggregatedDoc.Components[compType] = make(map[string]interface{}) } aggregatedCompDefsMap, _ := aggregatedDoc.Components[compType].(map[string]interface{}) for compName, compValue := range compDefsMap { prefixedCompName := fmt.Sprintf("%s_%s", serviceName, compName) aggregatedCompDefsMap[prefixedCompName] = compValue } } } } r.Response.WriteExit(aggregatedDoc) }) }