2024-07-22 16:04:21 +08:00
|
|
|
// Package api_doc ...
|
|
|
|
//
|
|
|
|
// Description : api_doc ...
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 2024-07-22 15:55
|
|
|
|
package api_doc
|
|
|
|
|
|
|
|
import (
|
2025-02-09 15:00:25 +08:00
|
|
|
"errors"
|
2025-02-10 11:15:29 +08:00
|
|
|
"fmt"
|
2024-07-22 16:04:21 +08:00
|
|
|
"git.zhangdeman.cn/gateway/api-doc/define"
|
|
|
|
"git.zhangdeman.cn/zhangdeman/consts"
|
2025-02-09 15:00:25 +08:00
|
|
|
"git.zhangdeman.cn/zhangdeman/wrapper"
|
|
|
|
"net/http"
|
2025-02-08 18:52:12 +08:00
|
|
|
"reflect"
|
2024-08-14 14:54:57 +08:00
|
|
|
"strings"
|
2024-07-22 16:04:21 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// NewOpenapiDoc ...
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 15:56 2024/7/22
|
2025-02-09 21:23:58 +08:00
|
|
|
func NewOpenapiDoc(info *define.Info, servers []*define.ServerItem) *Generate {
|
|
|
|
if nil == info {
|
|
|
|
info = &define.Info{
|
|
|
|
Description: "openapi接口文档",
|
|
|
|
Title: "openapi接口文档",
|
|
|
|
TermsOfService: "",
|
|
|
|
Contact: nil,
|
|
|
|
License: nil,
|
2025-02-10 11:33:10 +08:00
|
|
|
Version: "0.0.1",
|
2025-02-09 21:23:58 +08:00
|
|
|
}
|
|
|
|
}
|
2025-02-10 11:33:10 +08:00
|
|
|
if len(info.Version) == 0 {
|
|
|
|
info.Version = "0.0.1"
|
|
|
|
}
|
2025-02-09 21:23:58 +08:00
|
|
|
if nil == info.License {
|
|
|
|
info.License = &define.License{
|
|
|
|
Name: consts.LicenseApache20,
|
|
|
|
Url: consts.LicenseUrlTable[consts.LicenseApache20],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nil == info.Contact {
|
|
|
|
info.Contact = &define.Contact{
|
|
|
|
Name: "研发人员(developer)",
|
|
|
|
Url: "",
|
|
|
|
Email: "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nil == servers {
|
|
|
|
servers = []*define.ServerItem{}
|
|
|
|
}
|
2024-07-22 16:04:21 +08:00
|
|
|
return &Generate{
|
2025-02-14 21:08:19 +08:00
|
|
|
readMethodList: []string{
|
|
|
|
http.MethodGet, http.MethodHead, http.MethodConnect, http.MethodOptions, http.MethodTrace,
|
|
|
|
},
|
2024-07-22 16:04:21 +08:00
|
|
|
docData: &define.OpenapiDoc{
|
2025-02-09 21:23:58 +08:00
|
|
|
Openapi: consts.SwaggerDocVersion3,
|
|
|
|
Info: info,
|
|
|
|
Servers: servers,
|
2024-08-14 14:24:30 +08:00
|
|
|
Components: &define.Components{Schemas: map[string]*define.Schema{}},
|
|
|
|
Tags: make([]*define.TagItem, 0),
|
|
|
|
Paths: make(map[string]*define.PathConfig),
|
2024-07-22 16:04:21 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate 文档生成实例
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 15:57 2024/7/22
|
|
|
|
type Generate struct {
|
2025-02-14 21:08:19 +08:00
|
|
|
docData *define.OpenapiDoc
|
|
|
|
readMethodList []string
|
2024-07-22 16:04:21 +08:00
|
|
|
}
|
2024-08-14 14:24:30 +08:00
|
|
|
|
2024-08-14 14:59:58 +08:00
|
|
|
// SetLicense 设置文档协议
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 14:56 2024/8/14
|
|
|
|
func (g *Generate) SetLicense(name string, url string) {
|
|
|
|
g.docData.Info.License.Name = name
|
|
|
|
g.docData.Info.License.Url = url
|
|
|
|
}
|
|
|
|
|
2024-08-14 14:24:30 +08:00
|
|
|
// AddTag 新增tag
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 14:23 2024/8/14
|
|
|
|
func (g *Generate) AddTag(tagName string, tagDesc string) {
|
|
|
|
isHasTag := false
|
|
|
|
for _, item := range g.docData.Tags {
|
|
|
|
if item.Name == tagName {
|
|
|
|
if len(tagDesc) > 0 {
|
|
|
|
item.Description = tagDesc
|
|
|
|
}
|
|
|
|
isHasTag = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !isHasTag {
|
|
|
|
g.docData.Tags = append(g.docData.Tags, &define.TagItem{
|
|
|
|
Name: tagName,
|
|
|
|
Description: tagDesc,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-14 14:54:57 +08:00
|
|
|
// AddServer 添加server
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 14:45 2024/8/14
|
|
|
|
func (g *Generate) AddServer(serverDomain string, serverDesc string, serverVariable map[string]*define.ServerItemVariable) {
|
|
|
|
if nil == serverVariable {
|
|
|
|
serverVariable = make(map[string]*define.ServerItemVariable)
|
|
|
|
}
|
|
|
|
serverDomain = strings.TrimRight(serverDomain, "/")
|
|
|
|
isHasServer := false
|
|
|
|
for _, item := range g.docData.Servers {
|
|
|
|
if item.Url != serverDomain {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
isHasServer = true
|
|
|
|
if len(serverDesc) > 0 {
|
|
|
|
item.Description = serverDesc
|
|
|
|
}
|
|
|
|
for varName, varValue := range serverVariable {
|
|
|
|
item.Variables[varName] = varValue
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !isHasServer {
|
|
|
|
g.docData.Servers = append(g.docData.Servers, &define.ServerItem{
|
|
|
|
Url: serverDomain,
|
|
|
|
Description: serverDesc,
|
|
|
|
Variables: serverVariable,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-14 15:43:22 +08:00
|
|
|
// AddApi 新增Api
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 15:04 2024/8/14
|
|
|
|
//
|
|
|
|
// baseCfg : 接口基础配置, 示例数据
|
|
|
|
//
|
|
|
|
// &define.UriBaseConfig{
|
|
|
|
// Uri: "/foo/bar",
|
|
|
|
// Method: http.MethodPost,
|
2025-02-08 18:52:12 +08:00
|
|
|
// ContentType: ["application/json"],
|
2024-08-14 15:43:22 +08:00
|
|
|
// TagList: []string{"测试标签"},
|
|
|
|
// Summary: "这是一份示例基础配置",
|
|
|
|
// Description: "这是一份示例基础配置",
|
|
|
|
// }
|
|
|
|
//
|
2024-08-19 16:32:51 +08:00
|
|
|
// paramList : 参数列表
|
2024-08-14 15:43:22 +08:00
|
|
|
//
|
2024-08-19 16:32:51 +08:00
|
|
|
// resultList : 返回值列表
|
|
|
|
func (g *Generate) AddApi(baseCfg *define.UriBaseConfig, paramList []*define.ParamConfig, resultList []*define.ResultConfig) error {
|
2024-08-14 15:43:22 +08:00
|
|
|
return nil
|
2024-08-14 14:24:30 +08:00
|
|
|
}
|
2025-02-08 18:52:12 +08:00
|
|
|
|
2025-02-09 15:00:25 +08:00
|
|
|
// AddApiFromInAndOut 通过请求参数的
|
2025-02-08 18:52:12 +08:00
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
2025-02-09 15:00:25 +08:00
|
|
|
// Date : 14:22 2025/2/9
|
2025-02-14 21:33:46 +08:00
|
|
|
func (g *Generate) AddApiFromInAndOut(uriPrefix string, paramType reflect.Type, resultType reflect.Type) error {
|
2025-02-14 12:29:28 +08:00
|
|
|
if paramType.Kind() == reflect.Ptr {
|
|
|
|
paramType = paramType.Elem()
|
|
|
|
}
|
|
|
|
if resultType.Kind() == reflect.Ptr {
|
|
|
|
resultType = resultType.Elem()
|
|
|
|
}
|
2025-02-14 21:33:46 +08:00
|
|
|
baseCfg, err := g.parseBaseUriConfig(uriPrefix, paramType)
|
2025-02-14 15:31:53 +08:00
|
|
|
if nil != err {
|
|
|
|
return err
|
2025-02-09 15:00:25 +08:00
|
|
|
}
|
|
|
|
if _, exist := g.docData.Paths[baseCfg.Uri]; !exist {
|
|
|
|
g.docData.Paths[baseCfg.Uri] = &define.PathConfig{}
|
2025-02-08 22:04:25 +08:00
|
|
|
}
|
2025-02-14 18:22:53 +08:00
|
|
|
// 接口文档初始化
|
|
|
|
cfg := g.getApiDocBaseCfg(baseCfg, paramType)
|
2025-02-14 21:08:19 +08:00
|
|
|
|
|
|
|
if wrapper.ArrayType(g.readMethodList).Has(baseCfg.Method) >= 0 {
|
2025-02-14 15:31:53 +08:00
|
|
|
cfg.RequestBody = nil // get类请求没有request body
|
2025-02-14 12:29:28 +08:00
|
|
|
// 参数解析
|
|
|
|
g.ParseReadConfigParam(baseCfg, cfg, paramType)
|
2025-02-14 15:31:53 +08:00
|
|
|
} else {
|
|
|
|
// post类解析
|
|
|
|
paramSchemaName := g.AddComponentsSchema("", paramType.PkgPath(), paramType)
|
|
|
|
for _, itemType := range baseCfg.ContentType {
|
|
|
|
cfg.RequestBody.Content[itemType] = &define.Media{
|
|
|
|
Schema: &define.Schema{
|
|
|
|
Ref: g.getSchemaRef(paramSchemaName),
|
|
|
|
},
|
|
|
|
Example: "",
|
|
|
|
Examples: nil,
|
|
|
|
Encoding: nil,
|
|
|
|
}
|
2025-02-10 11:15:29 +08:00
|
|
|
}
|
|
|
|
}
|
2025-02-14 15:31:53 +08:00
|
|
|
// 无论什么请求, 对于result解析逻辑一致
|
|
|
|
resultSchemaName := g.AddComponentsSchema("", resultType.PkgPath(), resultType)
|
2025-02-09 15:00:25 +08:00
|
|
|
for _, itemOutputType := range baseCfg.OutputContentType {
|
2025-02-10 11:15:29 +08:00
|
|
|
cfg.Responses[fmt.Sprintf("%v", http.StatusOK)].Content[itemOutputType] = &define.Media{
|
2025-02-09 15:00:25 +08:00
|
|
|
Schema: &define.Schema{
|
2025-02-12 22:15:14 +08:00
|
|
|
Ref: g.getSchemaRef(resultSchemaName),
|
2025-02-09 15:00:25 +08:00
|
|
|
},
|
|
|
|
Example: "",
|
|
|
|
Examples: nil,
|
|
|
|
Encoding: nil,
|
2025-02-08 18:52:12 +08:00
|
|
|
}
|
|
|
|
}
|
2025-02-14 18:22:53 +08:00
|
|
|
g.setApiDoc(baseCfg, cfg)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// setApiDoc 设置文档配置
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 16:13 2025/2/14
|
|
|
|
func (g *Generate) setApiDoc(baseCfg *define.UriBaseConfig, apiDocCfg *define.PathItemOperationConfig) {
|
2025-02-09 15:00:25 +08:00
|
|
|
switch baseCfg.Method {
|
|
|
|
case http.MethodGet:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Get = apiDocCfg
|
2025-02-09 15:00:25 +08:00
|
|
|
case http.MethodHead:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Head = apiDocCfg
|
2025-02-09 15:00:25 +08:00
|
|
|
case http.MethodPost:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Post = apiDocCfg
|
2025-02-09 15:00:25 +08:00
|
|
|
case http.MethodPut:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Put = apiDocCfg
|
2025-02-09 15:00:25 +08:00
|
|
|
case http.MethodPatch:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Patch = apiDocCfg
|
2025-02-09 15:00:25 +08:00
|
|
|
case http.MethodDelete:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Delete = apiDocCfg
|
2025-02-09 15:00:25 +08:00
|
|
|
case http.MethodConnect:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Connect = apiDocCfg
|
2025-02-09 15:00:25 +08:00
|
|
|
case http.MethodOptions:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Options = apiDocCfg
|
2025-02-09 15:00:25 +08:00
|
|
|
case http.MethodTrace:
|
2025-02-14 18:22:53 +08:00
|
|
|
g.docData.Paths[baseCfg.Uri].Trace = apiDocCfg
|
2025-02-14 11:30:12 +08:00
|
|
|
default:
|
|
|
|
panic("unknown method: " + baseCfg.Method)
|
2025-02-08 18:52:12 +08:00
|
|
|
}
|
2025-02-14 18:22:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// getApiDocBaseCfg 获取接口文档的基础配置
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 16:10 2025/2/14
|
|
|
|
func (g *Generate) getApiDocBaseCfg(baseCfg *define.UriBaseConfig, paramType reflect.Type) *define.PathItemOperationConfig {
|
|
|
|
defaultPkgPath := wrapper.String(strings.TrimLeft(baseCfg.Uri, "/")).SnakeCaseToCamel()
|
|
|
|
cfg := &define.PathItemOperationConfig{
|
|
|
|
Tags: baseCfg.TagList,
|
|
|
|
Summary: baseCfg.Summary,
|
|
|
|
Description: baseCfg.Description,
|
|
|
|
ExternalDocs: nil,
|
|
|
|
OperationID: baseCfg.Method + "-" + defaultPkgPath,
|
|
|
|
Parameters: make([]*define.PathConfigParameter, 0),
|
|
|
|
RequestBody: &define.RequestBody{
|
|
|
|
Required: true,
|
|
|
|
Description: "",
|
|
|
|
Content: map[string]*define.Media{},
|
|
|
|
Ref: "",
|
|
|
|
},
|
|
|
|
Responses: map[string]*define.Response{
|
|
|
|
fmt.Sprintf("%v", http.StatusOK): &define.Response{
|
|
|
|
Content: map[string]*define.Media{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Callbacks: nil,
|
|
|
|
Deprecated: baseCfg.Deprecated,
|
|
|
|
Security: nil,
|
|
|
|
Servers: nil,
|
|
|
|
}
|
|
|
|
// 解析绑定在url中的参数
|
|
|
|
if paramList := define.UriParamRegexp.FindAllString(baseCfg.Uri, -1); len(paramList) > 0 {
|
|
|
|
for _, param := range paramList {
|
|
|
|
param = strings.TrimPrefix(param, "{")
|
|
|
|
param = strings.TrimSuffix(param, "}")
|
|
|
|
cfg.Parameters = append(cfg.Parameters, &define.PathConfigParameter{
|
|
|
|
Name: param,
|
|
|
|
In: consts.SwaggerParameterInPath,
|
|
|
|
Description: param,
|
|
|
|
Required: true,
|
|
|
|
Deprecated: false,
|
|
|
|
Schema: &define.Schema{
|
|
|
|
Type: consts.SwaggerDataTypeString,
|
|
|
|
Format: consts.DataTypeString.String(),
|
|
|
|
},
|
|
|
|
AllowEmptyValue: false,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cfg
|
2025-02-09 15:00:25 +08:00
|
|
|
}
|
|
|
|
|
2025-02-14 12:29:28 +08:00
|
|
|
// ParseReadConfigParam 解析get类请求参数
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 11:55 2025/2/14
|
|
|
|
func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseReqCfg *define.PathItemOperationConfig, inputType reflect.Type) {
|
|
|
|
if inputType.Kind() == reflect.Ptr {
|
|
|
|
inputType = inputType.Elem()
|
|
|
|
}
|
|
|
|
if inputType.Kind() != reflect.Struct {
|
|
|
|
panic(requestCfg.Uri + " : request param not struct")
|
|
|
|
}
|
2025-02-14 18:22:53 +08:00
|
|
|
if nil == baseReqCfg.Parameters {
|
|
|
|
baseReqCfg.Parameters = make([]*define.PathConfigParameter, 0)
|
|
|
|
}
|
2025-02-14 12:29:28 +08:00
|
|
|
for i := 0; i < inputType.NumField(); i++ {
|
|
|
|
propertyName := ParseStructField.GetParamName(inputType.Field(i))
|
|
|
|
if propertyName == "-" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
realInputTypeFormat := inputType.Field(i).Type.Kind().String()
|
|
|
|
fieldType := inputType.Field(i).Type
|
|
|
|
if inputType.Field(i).Type.Kind() == reflect.Ptr {
|
|
|
|
fieldType = inputType.Field(i).Type.Elem()
|
|
|
|
}
|
|
|
|
if fieldType.Kind() == reflect.Ptr ||
|
|
|
|
fieldType.Kind() == reflect.Struct ||
|
|
|
|
fieldType.Kind() == reflect.Map ||
|
|
|
|
fieldType.Kind() == reflect.Array ||
|
|
|
|
fieldType.Kind() == reflect.Slice {
|
|
|
|
if convertType := g.realBaseType2SwaggerType(fieldType.String()); !strings.HasPrefix(convertType, "[]") && convertType != inputType.Field(i).Type.Kind().String() {
|
|
|
|
// 针对基础类型指针
|
2025-02-14 18:22:53 +08:00
|
|
|
baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{
|
|
|
|
Name: ParseStructField.GetParamName(inputType.Field(i)),
|
|
|
|
In: consts.SwaggerParameterInQuery,
|
|
|
|
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
|
|
|
Required: ValidateRule.IsRequired(inputType.Field(i)),
|
|
|
|
Deprecated: ParseStructField.Deprecated(inputType.Field(i)),
|
|
|
|
Schema: &define.Schema{
|
|
|
|
Type: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()),
|
|
|
|
Items: nil,
|
|
|
|
Ref: "",
|
|
|
|
Format: realInputTypeFormat,
|
|
|
|
},
|
|
|
|
AllowEmptyValue: false,
|
|
|
|
Style: "",
|
|
|
|
Explode: false,
|
|
|
|
AllowReserved: false,
|
|
|
|
Ref: "",
|
|
|
|
})
|
2025-02-14 12:29:28 +08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
2025-02-14 18:22:53 +08:00
|
|
|
baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{
|
2025-02-14 12:29:28 +08:00
|
|
|
Name: ParseStructField.GetParamName(inputType.Field(i)),
|
|
|
|
In: consts.SwaggerParameterInQuery,
|
|
|
|
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
|
|
|
Required: ValidateRule.IsRequired(inputType.Field(i)),
|
|
|
|
Deprecated: ParseStructField.Deprecated(inputType.Field(i)),
|
|
|
|
Schema: &define.Schema{
|
|
|
|
Type: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()),
|
|
|
|
Items: nil,
|
|
|
|
Ref: "",
|
|
|
|
Format: realInputTypeFormat,
|
|
|
|
},
|
|
|
|
AllowEmptyValue: false,
|
|
|
|
Style: "",
|
|
|
|
Explode: false,
|
|
|
|
AllowReserved: false,
|
|
|
|
Ref: "",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2025-02-14 18:22:53 +08:00
|
|
|
switch requestCfg.Method {
|
|
|
|
case http.MethodGet:
|
|
|
|
g.docData.Paths[requestCfg.Uri].Get = baseReqCfg
|
|
|
|
case http.MethodHead:
|
|
|
|
g.docData.Paths[requestCfg.Uri].Head = baseReqCfg
|
|
|
|
case http.MethodConnect:
|
|
|
|
g.docData.Paths[requestCfg.Uri].Connect = baseReqCfg
|
|
|
|
case http.MethodOptions:
|
|
|
|
g.docData.Paths[requestCfg.Uri].Options = baseReqCfg
|
|
|
|
case http.MethodTrace:
|
|
|
|
g.docData.Paths[requestCfg.Uri].Trace = baseReqCfg
|
|
|
|
}
|
2025-02-14 12:29:28 +08:00
|
|
|
}
|
|
|
|
|
2025-02-09 15:00:25 +08:00
|
|
|
// AddComponentsSchema 添加schema
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 15:25 2025/2/8
|
2025-02-12 22:15:14 +08:00
|
|
|
func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, inputType reflect.Type) string {
|
2025-02-13 11:44:00 +08:00
|
|
|
inputNameArr := strings.Split(inputType.Name(), ".")
|
|
|
|
inputName := inputNameArr[len(inputNameArr)-1]
|
|
|
|
schemaName := strings.ReplaceAll(pkgPath+"."+inputName, "/", "-")
|
2025-02-13 21:57:18 +08:00
|
|
|
if schemaName == "-" {
|
|
|
|
// 忽略的属性
|
|
|
|
return schemaName
|
|
|
|
}
|
2025-02-13 16:18:42 +08:00
|
|
|
if _, exist := g.docData.Components.Schemas[schemaName]; exist {
|
|
|
|
// 已存在, 无需重复生成
|
|
|
|
return schemaName
|
|
|
|
}
|
2025-02-09 21:23:58 +08:00
|
|
|
if _, exist := g.docData.Components.Schemas[schemaName]; !exist {
|
2025-02-12 22:46:48 +08:00
|
|
|
s := &define.Schema{
|
2025-02-13 16:03:31 +08:00
|
|
|
Nullable: false,
|
|
|
|
Deprecated: false,
|
|
|
|
Properties: make(map[string]*define.Property),
|
|
|
|
Required: make([]string, 0),
|
|
|
|
Enum: make([]any, 0),
|
|
|
|
Type: consts.SwaggerDataTypeObject, // TODO : 区分数组
|
|
|
|
Ref: g.getSchemaRef(schemaName),
|
2025-02-09 21:23:58 +08:00
|
|
|
}
|
2025-02-12 22:46:48 +08:00
|
|
|
if len(rootSchemaName) == 0 || inputType.Kind() == reflect.Struct {
|
|
|
|
s.Ref = ""
|
|
|
|
}
|
|
|
|
g.docData.Components.Schemas[schemaName] = s
|
2025-02-08 18:52:12 +08:00
|
|
|
}
|
|
|
|
if inputType.Kind() == reflect.Map {
|
|
|
|
// map, 直接添加公共
|
2025-02-09 21:23:58 +08:00
|
|
|
g.docData.Components.Schemas[schemaName].Type = consts.SwaggerDataTypeObject
|
2025-02-09 15:00:25 +08:00
|
|
|
return schemaName
|
2025-02-08 18:52:12 +08:00
|
|
|
}
|
|
|
|
// 数组
|
2025-02-08 21:27:40 +08:00
|
|
|
if inputType.Kind() == reflect.Slice || inputType.Kind() == reflect.Array {
|
2025-02-12 22:15:14 +08:00
|
|
|
if len(rootSchemaName) == 0 {
|
|
|
|
g.docData.Components.Schemas[schemaName].Type = consts.SwaggerDataTypeArray
|
|
|
|
sliceItemType := g.parseSliceItem(schemaName, inputType)
|
2025-02-13 11:37:48 +08:00
|
|
|
g.docData.Components.Schemas[schemaName].Items = &define.PropertyXOf{Ref: g.getSchemaRef(sliceItemType)}
|
2025-02-12 22:15:14 +08:00
|
|
|
} else {
|
|
|
|
sliceItemType := g.parseSliceItem(schemaName, inputType)
|
|
|
|
g.docData.Components.Schemas[rootSchemaName].Properties[schemaName] = &define.Property{
|
|
|
|
Type: consts.SwaggerDataTypeArray,
|
|
|
|
Format: inputType.String(),
|
|
|
|
Items: &define.PropertyXOf{Ref: g.getSchemaRef(sliceItemType)},
|
|
|
|
}
|
2025-02-09 21:23:58 +08:00
|
|
|
}
|
2025-02-12 22:15:14 +08:00
|
|
|
|
2025-02-09 15:00:25 +08:00
|
|
|
return schemaName
|
2025-02-08 18:52:12 +08:00
|
|
|
}
|
|
|
|
// 结构体
|
|
|
|
if inputType.Kind() == reflect.Struct {
|
2025-02-12 22:46:48 +08:00
|
|
|
if len(rootSchemaName) == 0 {
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// g.docData.Components.Schemas[schemaName].Properties[""] = schemaName
|
|
|
|
}
|
|
|
|
// g.docData.Components.Schemas[schemaName].Ref = consts.SwaggerDataTypeObject
|
2025-02-08 22:04:25 +08:00
|
|
|
for i := 0; i < inputType.NumField(); i++ {
|
2025-02-13 21:57:18 +08:00
|
|
|
propertyName := ParseStructField.GetParamName(inputType.Field(i))
|
|
|
|
if propertyName == "-" {
|
|
|
|
continue
|
|
|
|
}
|
2025-02-08 22:04:25 +08:00
|
|
|
if inputType.Field(i).Type.Kind() == reflect.Ptr ||
|
|
|
|
inputType.Field(i).Type.Kind() == reflect.Struct ||
|
|
|
|
inputType.Field(i).Type.Kind() == reflect.Map ||
|
|
|
|
inputType.Field(i).Type.Kind() == reflect.Array ||
|
|
|
|
inputType.Field(i).Type.Kind() == reflect.Slice {
|
2025-02-11 21:28:28 +08:00
|
|
|
if convertType := g.realBaseType2SwaggerType(inputType.Field(i).Type.String()); !strings.HasPrefix(convertType, "[]") && convertType != inputType.Field(i).Type.Kind().String() {
|
2025-02-11 21:09:42 +08:00
|
|
|
// 针对基础类型指针
|
2025-02-13 21:57:18 +08:00
|
|
|
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
|
2025-02-11 22:28:45 +08:00
|
|
|
Type: g.realBaseType2SwaggerType(convertType),
|
|
|
|
Format: inputType.Field(i).Type.String(),
|
2025-02-12 22:20:50 +08:00
|
|
|
Default: ParseStructField.GetDefaultValue(inputType.Field(i)),
|
|
|
|
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
2025-02-11 21:09:42 +08:00
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2025-02-11 21:28:28 +08:00
|
|
|
if inputType.Field(i).Type.Kind() == reflect.Struct ||
|
|
|
|
inputType.Field(i).Type.Kind() == reflect.Map {
|
2025-02-13 21:57:18 +08:00
|
|
|
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
|
2025-02-11 22:28:45 +08:00
|
|
|
Type: consts.SwaggerDataTypeObject,
|
|
|
|
Format: inputType.Field(i).Type.String(),
|
2025-02-12 22:20:50 +08:00
|
|
|
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
2025-02-11 22:28:45 +08:00
|
|
|
Properties: map[string]*define.Property{},
|
2025-02-11 21:28:28 +08:00
|
|
|
}
|
|
|
|
} else if inputType.Field(i).Type.Kind() == reflect.Array ||
|
|
|
|
inputType.Field(i).Type.Kind() == reflect.Slice {
|
2025-02-13 21:57:18 +08:00
|
|
|
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
|
2025-02-11 22:28:45 +08:00
|
|
|
Type: consts.SwaggerDataTypeArray,
|
|
|
|
Format: inputType.Field(i).Type.String(),
|
2025-02-12 22:20:50 +08:00
|
|
|
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
2025-02-11 21:28:28 +08:00
|
|
|
Items: &define.PropertyXOf{
|
2025-02-13 11:37:48 +08:00
|
|
|
Ref: g.getSchemaRef(g.parseSliceItem(schemaName, inputType.Field(i).Type)),
|
2025-02-11 21:28:28 +08:00
|
|
|
},
|
|
|
|
Properties: map[string]*define.Property{},
|
|
|
|
}
|
|
|
|
} else if inputType.Field(i).Type.Kind() == reflect.Ptr {
|
|
|
|
|
2025-02-12 21:55:35 +08:00
|
|
|
} else {
|
2025-02-13 21:57:18 +08:00
|
|
|
g.AddComponentsSchema(schemaName, propertyName, inputType.Field(i).Type)
|
2025-02-10 14:38:37 +08:00
|
|
|
}
|
2025-02-11 21:28:28 +08:00
|
|
|
|
2025-02-09 21:23:58 +08:00
|
|
|
} else {
|
2025-02-13 21:57:18 +08:00
|
|
|
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
|
2025-02-11 22:28:45 +08:00
|
|
|
Type: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()),
|
|
|
|
Format: inputType.Field(i).Type.String(),
|
2025-02-12 22:20:50 +08:00
|
|
|
Default: ParseStructField.GetDefaultValue(inputType.Field(i)),
|
|
|
|
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
2025-02-09 21:23:58 +08:00
|
|
|
}
|
2025-02-08 22:04:25 +08:00
|
|
|
}
|
2025-02-13 18:27:31 +08:00
|
|
|
// 设置参数各种属性
|
|
|
|
g.setStructFieldProperty(schemaName, inputType.Field(i))
|
2025-02-08 22:04:25 +08:00
|
|
|
}
|
2025-02-11 21:09:42 +08:00
|
|
|
return schemaName
|
|
|
|
}
|
|
|
|
// 指针
|
|
|
|
if inputType.Kind() == reflect.Ptr {
|
|
|
|
convertType := g.realBaseType2SwaggerType(inputType.String())
|
|
|
|
if convertType == inputType.String() {
|
|
|
|
// 非基础数据类型
|
2025-02-12 21:55:35 +08:00
|
|
|
return g.AddComponentsSchema(schemaName, inputType.Elem().String(), inputType.Elem())
|
2025-02-11 21:09:42 +08:00
|
|
|
} else {
|
|
|
|
g.docData.Components.Schemas[schemaName].Properties[schemaName] = &define.Property{
|
|
|
|
Type: convertType,
|
|
|
|
Format: inputType.String(),
|
|
|
|
Properties: map[string]*define.Property{},
|
|
|
|
}
|
|
|
|
}
|
2025-02-08 22:04:25 +08:00
|
|
|
}
|
2025-02-09 15:00:25 +08:00
|
|
|
return schemaName
|
2025-02-08 22:04:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// parseSliceItem 解析数组每一项
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 21:33 2025/2/8
|
2025-02-10 14:38:37 +08:00
|
|
|
func (g *Generate) parseSliceItem(rootSchemaName string, inputType reflect.Type) string {
|
2025-02-08 22:04:25 +08:00
|
|
|
if inputType.Kind() != reflect.Slice && inputType.Kind() != reflect.Array {
|
|
|
|
// 不是数组
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
sliceValue := reflect.MakeSlice(inputType, 1, 1)
|
|
|
|
sliceItemType := sliceValue.Index(0).Type()
|
2025-02-10 14:38:37 +08:00
|
|
|
g.AddComponentsSchema(rootSchemaName, sliceItemType.PkgPath(), sliceItemType)
|
2025-02-08 22:04:25 +08:00
|
|
|
if len(sliceItemType.PkgPath()) == 0 {
|
|
|
|
return sliceItemType.String()
|
2025-02-08 18:52:12 +08:00
|
|
|
}
|
2025-02-13 11:44:00 +08:00
|
|
|
return sliceItemType.PkgPath() + "." + sliceItemType.Name()
|
2025-02-08 18:52:12 +08:00
|
|
|
}
|
2025-02-09 15:00:25 +08:00
|
|
|
|
2025-02-12 22:15:14 +08:00
|
|
|
// getSchemaRef 获取引用的类型
|
2025-02-09 15:00:25 +08:00
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 14:25 2025/2/9
|
2025-02-12 22:15:14 +08:00
|
|
|
func (g *Generate) getSchemaRef(schemaName string) string {
|
2025-02-09 15:00:25 +08:00
|
|
|
if "" == schemaName {
|
|
|
|
return ""
|
|
|
|
}
|
2025-02-12 22:46:48 +08:00
|
|
|
return "#/components/schemas/" + strings.ReplaceAll(schemaName, "/", "-")
|
2025-02-09 15:00:25 +08:00
|
|
|
}
|
2025-02-11 21:09:42 +08:00
|
|
|
|
|
|
|
// realType2SwaggerType golang 真实数据类型转换为golang数据类型
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 20:25 2025/2/11
|
|
|
|
func (g *Generate) realBaseType2SwaggerType(realType string) string {
|
|
|
|
switch realType {
|
|
|
|
case "bool", "*bool":
|
|
|
|
return consts.SwaggerDataTypeBoolean
|
|
|
|
case "string", "*string":
|
|
|
|
return consts.SwaggerDataTypeString
|
|
|
|
case "byte", "*byte":
|
|
|
|
return consts.SwaggerDataTypeByte
|
|
|
|
case "float32", "*float32", "float64", "*float64":
|
2025-02-13 11:37:48 +08:00
|
|
|
return consts.SwaggerDataTypeDouble
|
2025-02-11 21:09:42 +08:00
|
|
|
case "int", "*int", "uint", "*uint", "int64", "*int64", "uint64", "*uint64":
|
2025-02-13 11:37:48 +08:00
|
|
|
return consts.SwaggerDataTypeInteger
|
2025-02-11 21:09:42 +08:00
|
|
|
case "int8", "*int8", "uint8", "*uint8", "int16", "*int16", "uint16", "*uint16", "int32", "*int32", "uint32", "*uint32":
|
|
|
|
return consts.SwaggerDataTypeInteger
|
|
|
|
default:
|
|
|
|
return realType
|
|
|
|
}
|
|
|
|
}
|
2025-02-11 21:28:28 +08:00
|
|
|
|
|
|
|
// realType2SwaggerType ...
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 21:20 2025/2/11
|
|
|
|
func (g *Generate) realType2SwaggerType(realType string) string {
|
|
|
|
if strings.HasPrefix(realType, "[]") {
|
|
|
|
return consts.SwaggerDataTypeArray
|
|
|
|
}
|
|
|
|
return g.realBaseType2SwaggerType(realType)
|
|
|
|
}
|
2025-02-13 16:18:42 +08:00
|
|
|
|
|
|
|
// setStructFieldProperty 添加struct_field各种属性
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 16:13 2025/2/13
|
|
|
|
func (g *Generate) setStructFieldProperty(schemaName string, structField reflect.StructField) {
|
|
|
|
paramName := ParseStructField.GetParamName(structField)
|
|
|
|
if ValidateRule.IsRequired(structField) {
|
|
|
|
g.docData.Components.Schemas[schemaName].Required = append(g.docData.Components.Schemas[schemaName].Required, paramName)
|
|
|
|
}
|
2025-02-13 18:27:31 +08:00
|
|
|
g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(structField)].Enum = ValidateRule.Enum(structField)
|
2025-02-13 16:18:42 +08:00
|
|
|
}
|
2025-02-13 21:57:18 +08:00
|
|
|
|
2025-02-14 15:31:53 +08:00
|
|
|
// parseBaseUriConfig 通过Meta字段解析Uri基础配置信息
|
|
|
|
//
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
//
|
|
|
|
// Date : 15:13 2025/2/14
|
2025-02-14 21:33:46 +08:00
|
|
|
func (g *Generate) parseBaseUriConfig(uriPrefix string, paramType reflect.Type) (*define.UriBaseConfig, error) {
|
2025-02-13 21:57:18 +08:00
|
|
|
// 解析meta信息
|
|
|
|
metaField, metaFieldExist := paramType.FieldByName("Meta")
|
|
|
|
if !metaFieldExist {
|
2025-02-14 15:31:53 +08:00
|
|
|
return nil, errors.New("Meta field not found")
|
2025-02-13 21:57:18 +08:00
|
|
|
}
|
|
|
|
res := &define.UriBaseConfig{
|
|
|
|
Uri: "",
|
|
|
|
Method: "",
|
|
|
|
ContentType: nil,
|
|
|
|
OutputContentType: nil,
|
|
|
|
TagList: nil,
|
|
|
|
Summary: "",
|
|
|
|
Description: "",
|
|
|
|
ParamList: nil,
|
|
|
|
ResultList: nil,
|
|
|
|
Deprecated: false,
|
|
|
|
}
|
2025-02-14 15:31:53 +08:00
|
|
|
res.Uri = metaField.Tag.Get(define.TagPath)
|
2025-02-14 21:33:46 +08:00
|
|
|
if len(uriPrefix) > 0 {
|
|
|
|
res.Uri = strings.TrimRight(uriPrefix, "/") + strings.TrimLeft(res.Uri, "/")
|
|
|
|
}
|
2025-02-14 15:31:53 +08:00
|
|
|
res.Method = strings.ToUpper(metaField.Tag.Get(define.TagMethod))
|
|
|
|
res.Description = metaField.Tag.Get(define.TagDesc)
|
|
|
|
res.TagList = strings.Split(metaField.Tag.Get(define.TagUriTag), ",")
|
2025-02-13 21:57:18 +08:00
|
|
|
// 解析第一个返回值, 要求必须是结构体或者是map
|
2025-02-14 15:31:53 +08:00
|
|
|
outputStrictModel := metaField.Tag.Get(define.TagOutputStrict)
|
2025-02-13 21:57:18 +08:00
|
|
|
res.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true"
|
|
|
|
deprecated := metaField.Tag.Get(define.TagDeprecated)
|
|
|
|
res.Deprecated = deprecated == "1" || deprecated == "true"
|
2025-02-14 15:31:53 +08:00
|
|
|
res.ContentType = strings.Split(metaField.Tag.Get(define.TagContentType), ",")
|
2025-02-14 21:08:19 +08:00
|
|
|
if len(res.ContentType) == 0 {
|
|
|
|
if wrapper.ArrayType(g.readMethodList).Has(res.Method) >= 0 {
|
|
|
|
// get类请求
|
|
|
|
res.ContentType = []string{consts.MimeTypeXWWWFormUrlencoded}
|
|
|
|
} else {
|
|
|
|
res.ContentType = []string{consts.MimeTypeJson}
|
|
|
|
}
|
|
|
|
}
|
2025-02-14 15:31:53 +08:00
|
|
|
res.OutputContentType = strings.Split(metaField.Tag.Get(define.TagOutputContentType), ",")
|
2025-02-14 21:08:19 +08:00
|
|
|
if len(res.OutputContentType) == 0 {
|
|
|
|
// 未设置响应类型默认JSON数据
|
|
|
|
res.OutputContentType = []string{consts.MimeTypeJson}
|
|
|
|
}
|
2025-02-14 15:31:53 +08:00
|
|
|
res.Summary = ParseStructField.Summary(metaField)
|
|
|
|
if res.Method == "" {
|
|
|
|
return nil, errors.New("baseCfg.Method is empty")
|
|
|
|
}
|
|
|
|
if res.Uri == "" {
|
|
|
|
return nil, errors.New("baseCfg.Uri is empty")
|
|
|
|
}
|
|
|
|
return res, nil
|
2025-02-13 21:57:18 +08:00
|
|
|
}
|