131 lines
4.3 KiB
Go
131 lines
4.3 KiB
Go
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(`
|
||
<!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)
|
||
})
|
||
}
|