swagger聚合封装
This commit is contained in:
@@ -2,10 +2,12 @@ package consul
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"github.com/gogf/gf/contrib/registry/consul/v2"
|
"github.com/gogf/gf/contrib/registry/consul/v2"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"github.com/gogf/gf/v2/net/gsel"
|
"github.com/gogf/gf/v2/net/gsel"
|
||||||
"github.com/gogf/gf/v2/net/gsvc"
|
"github.com/gogf/gf/v2/net/gsvc"
|
||||||
|
"github.com/gogf/gf/v2/util/grand"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -21,3 +23,15 @@ func init() {
|
|||||||
gsvc.SetRegistry(registry)
|
gsvc.SetRegistry(registry)
|
||||||
gsel.SetBuilder(gsel.NewBuilderRoundRobin())
|
gsel.SetBuilder(gsel.NewBuilderRoundRobin())
|
||||||
}
|
}
|
||||||
|
func GetInstanceAddr(ctx context.Context, name string) (addr string, err error) {
|
||||||
|
watch, err := gsvc.GetRegistry().Watch(ctx, name)
|
||||||
|
service, err := watch.Proceed()
|
||||||
|
if err != nil || service == nil {
|
||||||
|
err = errors.New("获取customerService服务实例失败!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxService := grand.N(0, len(service)-1)
|
||||||
|
maxEndpoints := grand.N(0, len(service[maxService].GetEndpoints())-1)
|
||||||
|
addr = service[maxService].GetEndpoints()[maxEndpoints].String()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func init() {
|
|||||||
<script>
|
<script>
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
window.ui = SwaggerUIBundle({
|
window.ui = SwaggerUIBundle({
|
||||||
url: '{SwaggerUIDocUrl}',
|
url: '/swagger',
|
||||||
dom_id: '#swagger-ui',
|
dom_id: '#swagger-ui',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -97,7 +97,7 @@ func doRequest(ctx context.Context, method string, url string, target any, data
|
|||||||
}()
|
}()
|
||||||
result := response.ReadAll()
|
result := response.ReadAll()
|
||||||
resultStrut := &ghttp.DefaultHandlerResponse{}
|
resultStrut := &ghttp.DefaultHandlerResponse{}
|
||||||
if gconv.Struct(result, resultStrut); resultStrut.Code != 200 {
|
if gconv.Struct(result, &resultStrut); resultStrut.Code != 200 {
|
||||||
err = errors.New(resultStrut.Message)
|
err = errors.New(resultStrut.Message)
|
||||||
} else {
|
} else {
|
||||||
gconv.Struct(resultStrut.Data, target)
|
gconv.Struct(resultStrut.Data, target)
|
||||||
|
|||||||
105
swagger/swagger.go
Normal file
105
swagger/swagger.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
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 Aggregated() {
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user