Files
common/swagger/swagger.go
2026-03-12 08:51:59 +08:00

131 lines
4.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package swagger
import (
"fmt"
"gitea.com/red-future/common/consul"
"gitea.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(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="SwaggerUI"/>
<title>SwaggerUI</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.10.5/swagger-ui.min.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.10.5/swagger-ui-bundle.js" crossorigin></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({
url: '/swagger',
dom_id: '#swagger-ui',
});
};
</script>
</body>
</html>
`)
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)
})
}