指定外层响应结构

This commit is contained in:
Hsy 2024-12-18 12:08:19 +08:00
parent 80a696bc8c
commit 517ff9a42e
8 changed files with 182 additions and 2933 deletions

View File

@ -3,7 +3,7 @@
### 1. 编译goctl-swagger插件 ### 1. 编译goctl-swagger插件
``` ```
GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/goctl-swagger@latest GOPROXY=https://goproxy.cn/,direct go install github.com/devttl/goctl-swagger@latest
``` ```
### 2. 配置环境 ### 2. 配置环境

View File

@ -1,9 +1,9 @@
package action package action
import ( import (
"github.com/devttl/goctl-swagger/generate"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/zeromicro/go-zero/tools/goctl/plugin" "github.com/zeromicro/go-zero/tools/goctl/plugin"
"github.com/zeromicro/goctl-swagger/generate"
) )
func Generator(ctx *cli.Context) error { func Generator(ctx *cli.Context) error {
@ -20,5 +20,7 @@ func Generator(ctx *cli.Context) error {
basepath := ctx.String("basepath") basepath := ctx.String("basepath")
host := ctx.String("host") host := ctx.String("host")
schemes := ctx.String("schemes") schemes := ctx.String("schemes")
return generate.Do(fileName, host, basepath, schemes, p) pack := ctx.String("pack")
response := ctx.String("response")
return generate.Do(fileName, host, basepath, schemes, pack, response, p)
} }

View File

@ -254,6 +254,7 @@ type swaggerSchemaObject struct {
MaxProperties uint64 `json:"maxProperties,omitempty"` MaxProperties uint64 `json:"maxProperties,omitempty"`
MinProperties uint64 `json:"minProperties,omitempty"` MinProperties uint64 `json:"minProperties,omitempty"`
Required []string `json:"required,omitempty"` Required []string `json:"required,omitempty"`
AllOf []swaggerSchemaObject `json:"allOf,omitempty"`
} }
// http://swagger.io/specification/#definitionsObject // http://swagger.io/specification/#definitionsObject
@ -269,3 +270,11 @@ type enumMap map[string]*descriptor.Enum
// Internal type to store used references. // Internal type to store used references.
type refMap map[string]struct{} type refMap map[string]struct{}
// responseField 响应字段
type responseField struct {
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
IsData bool `json:"is_data"`
}

View File

@ -4,13 +4,13 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "os"
"github.com/zeromicro/go-zero/tools/goctl/plugin" "github.com/zeromicro/go-zero/tools/goctl/plugin"
) )
func Do(filename string, host string, basePath string, schemes string, in *plugin.Plugin) error { func Do(filename, host, basePath, schemes, pack, response string, in *plugin.Plugin) error {
swagger, err := applyGenerate(in, host, basePath, schemes) swagger, err := applyGenerate(in, host, basePath, schemes, pack, response)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
@ -24,7 +24,7 @@ func Do(filename string, host string, basePath string, schemes string, in *plugi
output := in.Dir + "/" + filename output := in.Dir + "/" + filename
err = ioutil.WriteFile(output, formatted.Bytes(), 0666) err = os.WriteFile(output, formatted.Bytes(), 0o666)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }

View File

@ -3,6 +3,7 @@ package generate
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
@ -16,7 +17,10 @@ import (
"github.com/zeromicro/go-zero/tools/goctl/plugin" "github.com/zeromicro/go-zero/tools/goctl/plugin"
) )
var strColon = []byte(":") var (
strColon = []byte(":")
defaultResponse = parseDefaultResponse()
)
const ( const (
validateKey = "validate" validateKey = "validate"
@ -30,6 +34,9 @@ const (
optionSeparator = "|" optionSeparator = "|"
equalToken = "=" equalToken = "="
atRespDoc = "@respdoc-" atRespDoc = "@respdoc-"
// DefaultResponseJson default response pack json structure.
DefaultResponseJson = `[{"name":"trace_id","type":"string","description":"链路追踪id","example":"a1b2c3d4e5f6g7h8"},{"name":"code","type":"integer","description":"状态码","example":0},{"name":"msg","type":"string","description":"消息","example":"ok"},{"name":"data","type":"object","description":"数据","is_data":true}]`
) )
func parseRangeOption(option string) (float64, float64, bool) { func parseRangeOption(option string) (float64, float64, bool) {
@ -55,7 +62,7 @@ func parseRangeOption(option string) (float64, float64, bool) {
return min, max, true return min, max, true
} }
func applyGenerate(p *plugin.Plugin, host string, basePath string, schemes string) (*swaggerObject, error) { func applyGenerate(p *plugin.Plugin, host, basePath, schemes, pack, response string) (*swaggerObject, error) {
title, _ := strconv.Unquote(p.Api.Info.Properties["title"]) title, _ := strconv.Unquote(p.Api.Info.Properties["title"])
version, _ := strconv.Unquote(p.Api.Info.Properties["version"]) version, _ := strconv.Unquote(p.Api.Info.Properties["version"])
desc, _ := strconv.Unquote(p.Api.Info.Properties["desc"]) desc, _ := strconv.Unquote(p.Api.Info.Properties["desc"])
@ -104,8 +111,22 @@ func applyGenerate(p *plugin.Plugin, host string, basePath string, schemes strin
// s.Security = append(s.Security, swaggerSecurityRequirementObject{"apiKey": []string{}}) // s.Security = append(s.Security, swaggerSecurityRequirementObject{"apiKey": []string{}})
dataKey := "data"
if pack != "" {
resp := defaultResponse
if response != "" {
r, dk, err := parseResponse(response)
if err != nil {
return nil, err
}
resp = r
dataKey = dk
}
s.Definitions[pack] = resp
}
requestResponseRefs := refMap{} requestResponseRefs := refMap{}
renderServiceRoutes(p.Api.Service, p.Api.Service.Groups, s.Paths, requestResponseRefs) renderServiceRoutes(p.Api.Service, p.Api.Service.Groups, s.Paths, requestResponseRefs, pack, dataKey)
m := messageMap{} m := messageMap{}
renderReplyAsDefinition(s.Definitions, m, p.Api.Types, requestResponseRefs) renderReplyAsDefinition(s.Definitions, m, p.Api.Types, requestResponseRefs)
@ -113,7 +134,7 @@ func applyGenerate(p *plugin.Plugin, host string, basePath string, schemes strin
return &s, nil return &s, nil
} }
func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swaggerPathsObject, requestResponseRefs refMap) { func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swaggerPathsObject, requestResponseRefs refMap, pack, dataKey string) {
for _, group := range groups { for _, group := range groups {
for _, route := range group.Routes { for _, route := range group.Routes {
path := group.GetAnnotation("prefix") + route.Path path := group.GetAnnotation("prefix") + route.Path
@ -237,6 +258,17 @@ func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swagge
if value := group.GetAnnotation("swtags"); len(value) > 0 { if value := group.GetAnnotation("swtags"); len(value) > 0 {
tags = value tags = value
} }
schema := swaggerSchemaObject{
schemaCore: respSchema,
}
if pack != "" {
schema = swaggerSchemaObject{
AllOf: []swaggerSchemaObject{
{schemaCore: schemaCore{Ref: "#/definitions/" + strings.TrimPrefix(pack, "/")}},
{schemaCore: schemaCore{Type: "object"}, Properties: &swaggerSchemaObjectProperties{{Key: dataKey, Value: respSchema}}},
},
}
}
operationObject := &swaggerOperationObject{ operationObject := &swaggerOperationObject{
Tags: []string{tags}, Tags: []string{tags},
@ -244,9 +276,7 @@ func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swagge
Responses: swaggerResponsesObject{ Responses: swaggerResponsesObject{
"200": swaggerResponseObject{ "200": swaggerResponseObject{
Description: desc, Description: desc,
Schema: swaggerSchemaObject{ Schema: schema,
schemaCore: respSchema,
},
}, },
}, },
} }
@ -769,3 +799,43 @@ func parseHeader(m spec.Member, parameters []swaggerParameterObject) []swaggerPa
} }
return append(parameters, sp) return append(parameters, sp)
} }
// 解析响应参数
func parseResponse(resp string) (swaggerSchemaObject, string, error) {
var fields []responseField
err := json.Unmarshal([]byte(resp), &fields)
if err != nil {
return swaggerSchemaObject{}, "", err
}
hasData := false
dataKey := ""
for _, field := range fields {
if field.Name == "" || field.Type == "" {
return swaggerSchemaObject{}, "", errors.New("响应字段参数错误")
}
if field.IsData {
hasData = true
dataKey = field.Name
}
}
if !hasData {
return swaggerSchemaObject{}, "", errors.New("请指定包装的数据字段")
}
properties := new(swaggerSchemaObjectProperties)
response := swaggerSchemaObject{schemaCore: schemaCore{Type: "object"}}
for _, field := range fields {
*properties = append(*properties,
keyVal{
Key: field.Name,
Value: swaggerSchemaObject{schemaCore: schemaCore{Type: field.Type}, Description: field.Description},
})
}
response.Properties = properties
return response, dataKey, nil
}
// 默认返回值
func parseDefaultResponse() swaggerSchemaObject {
response, _, _ := parseResponse(DefaultResponseJson)
return response
}

63
go.mod
View File

@ -1,6 +1,8 @@
module github.com/zeromicro/goctl-swagger module github.com/devttl/goctl-swagger
go 1.16 go 1.21
toolchain go1.23.2
require ( require (
github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0
@ -9,3 +11,60 @@ require (
github.com/zeromicro/go-zero/tools/goctl v1.6.5 github.com/zeromicro/go-zero/tools/goctl v1.6.5
golang.org/x/oauth2 v0.17.0 golang.org/x/oauth2 v0.17.0
) )
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/openzipkin/zipkin-go v0.4.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/zeromicro/antlr v0.0.1 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.19.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/automaxprocs v1.5.3 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

2900
go.sum

File diff suppressed because it is too large Load Diff

11
main.go
View File

@ -5,8 +5,9 @@ import (
"os" "os"
"runtime" "runtime"
"github.com/devttl/goctl-swagger/action"
"github.com/devttl/goctl-swagger/generate"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/zeromicro/goctl-swagger/action"
) )
var ( var (
@ -33,6 +34,14 @@ var (
Name: "schemes", Name: "schemes",
Usage: "swagger support schemes: http, https, ws, wss", Usage: "swagger support schemes: http, https, ws, wss",
}, },
&cli.StringFlag{
Name: "pack", // 开启外层响应包装并指定外层响应结构名称
Usage: "use outer packaging response and specify the name, example: Response",
},
&cli.StringFlag{
Name: "response", // 指定外层响应结构
Usage: "outer packaging response structure, example: " + fmt.Sprintf("%q", generate.DefaultResponseJson),
},
}, },
}, },
} }