package swagger
import (
"fmt"
"gitee.com/red-future---jilin-g/common/consul"
"gitee.com/red-future---jilin-g/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().Get(r.GetCtx(), "consul.services")
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.Slice() {
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)
})
}