api-doc/generate.go

778 lines
25 KiB
Go

// Package api_doc ...
//
// Description : api_doc ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-07-22 15:55
package api_doc
import (
"errors"
"fmt"
"git.zhangdeman.cn/gateway/api-doc/define"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/wrapper"
"net/http"
"reflect"
"strings"
)
// NewOpenapiDoc ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:56 2024/7/22
func NewOpenapiDoc(info *define.Info, servers []*define.ServerItem) *Generate {
if nil == info {
info = &define.Info{
Description: "openapi接口文档",
Title: "openapi接口文档",
TermsOfService: "",
Contact: nil,
License: nil,
Version: "0.0.1",
}
}
if len(info.Version) == 0 {
info.Version = "0.0.1"
}
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{}
}
return &Generate{
readMethodList: []string{
http.MethodGet, http.MethodHead, http.MethodConnect, http.MethodOptions, http.MethodTrace,
},
docData: &define.OpenapiDoc{
Openapi: consts.SwaggerDocVersion3,
Info: info,
Servers: servers,
Components: &define.Components{Schemas: map[string]*define.Schema{}},
Tags: make([]*define.TagItem, 0),
Paths: make(map[string]*define.PathConfig),
},
}
}
// Generate 文档生成实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:57 2024/7/22
type Generate struct {
docData *define.OpenapiDoc
readMethodList []string
}
func (g *Generate) Doc() *define.OpenapiDoc {
return g.docData
}
// 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
}
// 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,
})
}
}
// 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,
})
}
}
// AddApi 新增Api
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:04 2024/8/14
//
// baseCfg : 接口基础配置, 示例数据
//
// &define.UriBaseConfig{
// Uri: "/foo/bar",
// Method: http.MethodPost,
// ContentType: ["application/json"],
// TagList: []string{"测试标签"},
// Summary: "这是一份示例基础配置",
// Description: "这是一份示例基础配置",
// }
//
// paramList : 参数列表
//
// resultList : 返回值列表
func (g *Generate) AddApi(baseCfg *define.UriBaseConfig, paramList []*define.ParamConfig, resultList []*define.ResultConfig) error {
return nil
}
// AddApiFromInAndOut 通过请求参数的
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:22 2025/2/9
func (g *Generate) AddApiFromInAndOut(uriPrefix string, paramType reflect.Type, resultType reflect.Type) error {
if paramType.Kind() == reflect.Ptr {
paramType = paramType.Elem()
}
if resultType.Kind() == reflect.Ptr {
resultType = resultType.Elem()
}
baseCfg, err := g.parseBaseUriConfig(uriPrefix, paramType)
if nil != err {
return err
}
if _, exist := g.docData.Paths[baseCfg.Uri]; !exist {
g.docData.Paths[baseCfg.Uri] = &define.PathConfig{}
}
// 接口文档初始化
cfg := g.getApiDocBaseCfg(baseCfg, paramType)
if wrapper.ArrayType(g.readMethodList).Has(baseCfg.Method) >= 0 {
cfg.RequestBody = nil // get类请求没有request body
// 参数解析
g.ParseReadConfigParam(baseCfg, cfg, paramType)
} 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,
}
}
}
// 无论什么请求, 对于result解析逻辑一致
resultSchemaName := g.AddComponentsSchema("", resultType.PkgPath(), resultType)
for _, itemOutputType := range baseCfg.OutputContentType {
cfg.Responses[fmt.Sprintf("%v", http.StatusOK)].Content[itemOutputType] = &define.Media{
Schema: &define.Schema{
Ref: g.getSchemaRef(resultSchemaName),
},
Example: "",
Examples: nil,
Encoding: nil,
}
}
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) {
switch baseCfg.Method {
case http.MethodGet:
g.docData.Paths[baseCfg.Uri].Get = apiDocCfg
case http.MethodHead:
g.docData.Paths[baseCfg.Uri].Head = apiDocCfg
case http.MethodPost:
g.docData.Paths[baseCfg.Uri].Post = apiDocCfg
case http.MethodPut:
g.docData.Paths[baseCfg.Uri].Put = apiDocCfg
case http.MethodPatch:
g.docData.Paths[baseCfg.Uri].Patch = apiDocCfg
case http.MethodDelete:
g.docData.Paths[baseCfg.Uri].Delete = apiDocCfg
case http.MethodConnect:
g.docData.Paths[baseCfg.Uri].Connect = apiDocCfg
case http.MethodOptions:
g.docData.Paths[baseCfg.Uri].Options = apiDocCfg
case http.MethodTrace:
g.docData.Paths[baseCfg.Uri].Trace = apiDocCfg
default:
panic("unknown method: " + baseCfg.Method)
}
}
// getApiDocBaseCfg 获取接口文档的基础配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:10 2025/2/14
func (g *Generate) getApiDocBaseCfg(baseCfg *define.UriBaseConfig, paramType reflect.Type) *define.PathItemOperationConfig {
cfg := &define.PathItemOperationConfig{
Tags: baseCfg.TagList,
Summary: baseCfg.Summary,
Description: baseCfg.Description,
ExternalDocs: nil,
OperationID: baseCfg.Summary + "(" + baseCfg.Method + "-" + strings.ReplaceAll(strings.TrimLeft(baseCfg.Uri, "/"), "/", "-") + ")",
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
}
// 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")
}
if nil == baseReqCfg.Parameters {
baseReqCfg.Parameters = make([]*define.PathConfigParameter, 0)
}
for i := 0; i < inputType.NumField(); i++ {
if inputType.Field(i).Anonymous {
// 匿名字段, 直接对齐到当前的父级
g.ParseReadConfigParam(requestCfg, baseReqCfg, inputType.Field(i).Type)
}
propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i))
if propertyName == "-" {
continue
}
if inputType.Field(i).Type.Kind() == reflect.Struct && (inputType.Field(i).Type.String() == "Meta" || strings.HasSuffix(inputType.Field(i).Type.String(), ".Meta")) && inputType.Field(i).Type.NumField() == 0 {
// 空Meta字段认为是用来描述元信息的, 忽略
continue
}
convertBaseType, isBaseType := g.realBaseType2SwaggerType(inputType.Field(i).Type.String())
realInputTypeFormat := inputType.Field(i).Type.String()
fieldType := inputType.Field(i).Type
/*if inputType.Field(i).Type.Kind() == reflect.Ptr {
fieldType = inputType.Field(i).Type.Elem()
}*/
if isBaseType {
// 当做默认基础类型, 默认不会出现 *map *[]
baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{
Name: propertyName,
In: consts.SwaggerParameterInQuery,
Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)),
Required: ValidateRule.IsRequired(inputType.Field(i)),
Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)),
Schema: &define.Schema{
Type: convertBaseType,
Format: realInputTypeFormat,
},
AllowEmptyValue: false,
Style: "",
Explode: false,
AllowReserved: false,
})
continue
}
if inputType.Field(i).Type.Kind() == reflect.Ptr {
// 处理指针
if inputType.Field(i).Type.Elem().Kind() == reflect.Struct {
// 结构体指针
schemaNameNext := g.AddComponentsSchema("", propertyName, inputType.Field(i).Type.Elem())
baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{
Name: propertyName,
In: consts.SwaggerParameterInQuery,
Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)),
Required: ValidateRule.IsRequired(inputType.Field(i)),
Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)),
Schema: &define.Schema{
// Format: realInputTypeFormat,
Ref: g.getSchemaRef(schemaNameNext),
}, AllowEmptyValue: false,
Style: "",
Explode: false,
AllowReserved: false,
})
} else {
}
continue
}
if fieldType.Kind() == reflect.Struct ||
fieldType.Kind() == reflect.Map ||
fieldType.Kind() == reflect.Array ||
fieldType.Kind() == reflect.Slice {
baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{
Name: ParseStructFieldTag.GetParamName(inputType.Field(i)),
In: consts.SwaggerParameterInQuery,
Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)),
Required: ValidateRule.IsRequired(inputType.Field(i)),
Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)),
Schema: &define.Schema{
Type: convertBaseType,
Items: nil,
Ref: "",
Format: realInputTypeFormat,
},
AllowEmptyValue: false,
Style: "",
Explode: false,
AllowReserved: false,
Ref: "",
})
}
}
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
}
}
// AddComponentsSchema 添加schema
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:25 2025/2/8
func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, inputType reflect.Type) string {
if inputType.Kind() == reflect.Struct && (inputType.String() == "Meta" || strings.HasSuffix(inputType.String(), ".Meta")) && inputType.NumField() == 0 {
// 空Meta字段认为是用来描述元信息的, 忽略
return "-"
}
inputNameArr := strings.Split(inputType.Name(), ".")
inputName := inputNameArr[len(inputNameArr)-1]
schemaName := strings.ReplaceAll(pkgPath+"."+inputName, "/", ".")
if schemaName == "-" {
// 忽略的属性
return schemaName
}
if _, exist := g.docData.Components.Schemas[schemaName]; exist {
// 已存在, 无需重复生成
return schemaName
}
if _, exist := g.docData.Components.Schemas[schemaName]; !exist {
s := &define.Schema{
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),
}
if len(rootSchemaName) == 0 || inputType.Kind() == reflect.Struct {
s.Ref = ""
}
g.docData.Components.Schemas[schemaName] = s
}
if inputType.Kind() == reflect.Map {
// map, 直接添加公共
g.docData.Components.Schemas[schemaName].Type = consts.SwaggerDataTypeObject
return schemaName
}
// 数组
if inputType.Kind() == reflect.Slice || inputType.Kind() == reflect.Array {
if len(rootSchemaName) == 0 {
g.docData.Components.Schemas[schemaName].Type = consts.SwaggerDataTypeArray
sliceItemType := g.parseSliceItem(schemaName, inputType)
g.docData.Components.Schemas[schemaName].Items = &define.PropertyXOf{Ref: g.getSchemaRef(sliceItemType)}
} 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)},
}
}
return schemaName
}
// 结构体
if inputType.Kind() == reflect.Struct {
for i := 0; i < inputType.NumField(); i++ {
if inputType.Field(i).Anonymous {
// 处理匿名字段
g.handleAnonymousField(schemaName, inputType.Field(i))
continue
}
propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i))
if propertyName == "-" {
continue
}
if inputType.Field(i).Type.Kind() == reflect.Ptr {
// 处理指针
if inputType.Field(i).Type.Elem().Kind() == reflect.Struct {
// 结构体指针
schemaNameNext := g.AddComponentsSchema(schemaName, propertyName, inputType.Field(i).Type.Elem())
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Ref: g.getSchemaRef(schemaNameNext),
}
} else {
// 当做默认基础类型, 默认不会出现 *map *[]
convertBaseType, _ := g.realBaseType2SwaggerType(inputType.Field(i).Type.String())
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Type: convertBaseType,
Format: inputType.Field(i).Type.String(),
Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)),
Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)),
}
}
continue
}
if 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 {
if inputType.Field(i).Type.Kind() == reflect.Struct ||
inputType.Field(i).Type.Kind() == reflect.Map {
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Type: consts.SwaggerDataTypeObject,
Format: inputType.Field(i).Type.String(),
Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)),
Properties: map[string]*define.Property{},
}
} else if inputType.Field(i).Type.Kind() == reflect.Array ||
inputType.Field(i).Type.Kind() == reflect.Slice {
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Type: consts.SwaggerDataTypeArray,
Format: inputType.Field(i).Type.String(),
Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)),
Items: &define.PropertyXOf{
Ref: g.getSchemaRef(g.parseSliceItem(schemaName, inputType.Field(i).Type)),
},
Properties: map[string]*define.Property{},
}
} else {
g.AddComponentsSchema(schemaName, propertyName, inputType.Field(i).Type)
}
} else {
convertBaseType, _ := g.realBaseType2SwaggerType(inputType.Field(i).Type.String())
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Type: convertBaseType,
Format: inputType.Field(i).Type.String(),
Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)),
Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)),
}
}
// 设置参数各种属性
g.setStructFieldProperty(schemaName, inputType.Field(i))
}
return schemaName
}
// 指针
if inputType.Kind() == reflect.Ptr {
if inputType.Elem().Kind() == reflect.Struct {
// 非基础数据类型
return g.AddComponentsSchema(schemaName, inputType.Elem().String(), inputType.Elem())
} else {
convertType, _ := g.realBaseType2SwaggerType(inputType.String())
g.docData.Components.Schemas[schemaName].Properties[schemaName] = &define.Property{
Type: convertType,
Format: inputType.String(),
Properties: map[string]*define.Property{},
}
}
}
return schemaName
}
// handleAnonymousField 处理匿名字段
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:43 2025/2/17
func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructField) {
if !field.Anonymous {
// 不是匿名字段
return
}
handleType := field.Type
if handleType.Kind() == reflect.Ptr {
handleType = handleType.Elem()
}
for i := 0; i < handleType.NumField(); i++ {
if handleType.Field(i).Anonymous {
// 地柜处理多层嵌套匿名字段
g.handleAnonymousField(schemaName, handleType.Field(i))
continue
} else {
itemField := handleType.Field(i)
if itemField.Type.Kind() == reflect.Map || itemField.Type.Kind() == reflect.Slice || itemField.Type.Kind() == reflect.Array || itemField.Type.Kind() == reflect.Struct || itemField.Type.Kind() == reflect.Ptr {
g.AddComponentsSchema(schemaName, handleType.Field(i).Type.PkgPath(), handleType.Field(i).Type)
continue
} else {
convertBaseType, _ := g.realBaseType2SwaggerType(handleType.Field(i).Type.String())
paramName := ParseStructFieldTag.GetParamName(itemField)
g.docData.Components.Schemas[schemaName].Properties[paramName] = &define.Property{
Type: convertBaseType,
Format: handleType.Field(i).Type.String(),
Enum: ValidateRule.Enum(handleType.Field(i)),
Default: ParseStructFieldTag.GetDefaultValue(handleType.Field(i)),
Description: ParseStructFieldTag.GetParamDesc(handleType.Field(i)),
}
if ValidateRule.IsRequired(itemField) {
g.docData.Components.Schemas[schemaName].Required = append(g.docData.Components.Schemas[schemaName].Required, paramName)
}
}
}
}
}
// parseSliceItem 解析数组每一项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:33 2025/2/8
func (g *Generate) parseSliceItem(rootSchemaName string, inputType reflect.Type) string {
if inputType.Kind() != reflect.Slice && inputType.Kind() != reflect.Array {
// 不是数组
return ""
}
sliceValue := reflect.MakeSlice(inputType, 1, 1)
sliceItemType := sliceValue.Index(0).Type()
if sliceItemType.Kind() == reflect.Ptr {
sliceItemType = sliceItemType.Elem()
}
g.AddComponentsSchema(rootSchemaName, sliceItemType.PkgPath(), sliceItemType)
if len(sliceItemType.PkgPath()) == 0 {
return sliceItemType.String()
}
return sliceItemType.PkgPath() + "." + sliceItemType.Name()
}
// getSchemaRef 获取引用的类型
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:25 2025/2/9
func (g *Generate) getSchemaRef(schemaName string) string {
if "" == schemaName {
return ""
}
schemaName = strings.ReplaceAll(schemaName, "*", "") // 去除指针类型 *
return "#/components/schemas/" + strings.ReplaceAll(schemaName, "/", ".")
}
// realType2SwaggerType golang 真实数据类型转换为golang数据类型
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:25 2025/2/11
func (g *Generate) realBaseType2SwaggerType(realType string) (string, bool) {
switch realType {
case "bool", "*bool":
return consts.SwaggerDataTypeBoolean, true
case "string", "*string":
return consts.SwaggerDataTypeString, true
case "byte", "*byte":
return consts.SwaggerDataTypeByte, true
case "float32", "*float32", "float64", "*float64":
return consts.SwaggerDataTypeDouble, true
case "int", "*int", "uint", "*uint", "int64", "*int64", "uint64", "*uint64":
return consts.SwaggerDataTypeInteger, true
case "int8", "*int8", "uint8", "*uint8", "int16", "*int16", "uint16", "*uint16", "int32", "*int32", "uint32", "*uint32":
return consts.SwaggerDataTypeInteger, true
default:
if strings.HasPrefix(realType, "[]") {
return consts.SwaggerDataTypeArray, true
}
return realType, false
}
}
// setStructFieldProperty 添加struct_field各种属性
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:13 2025/2/13
func (g *Generate) setStructFieldProperty(schemaName string, structField reflect.StructField) {
paramName := ParseStructFieldTag.GetParamName(structField)
if ValidateRule.IsRequired(structField) {
g.docData.Components.Schemas[schemaName].Required = append(g.docData.Components.Schemas[schemaName].Required, paramName)
}
g.docData.Components.Schemas[schemaName].Properties[ParseStructFieldTag.GetParamName(structField)].Enum = ValidateRule.Enum(structField)
}
// parseBaseUriConfig 通过Meta字段解析Uri基础配置信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:13 2025/2/14
func (g *Generate) parseBaseUriConfig(uriPrefix string, paramType reflect.Type) (*define.UriBaseConfig, error) {
// 解析meta信息
metaField, metaFieldExist := paramType.FieldByName("Meta")
if !metaFieldExist {
return nil, errors.New("Meta field not found")
}
res := &define.UriBaseConfig{
Uri: "",
Method: "",
ContentType: nil,
OutputContentType: nil,
TagList: nil,
Summary: "",
Description: "",
ParamList: nil,
ResultList: nil,
Deprecated: false,
}
res.Uri = metaField.Tag.Get(define.TagPath)
if len(uriPrefix) > 0 {
res.Uri = strings.TrimRight(uriPrefix, "/") + "/" + strings.TrimLeft(res.Uri, "/")
}
// 保证接口路由以 /开头
res.Uri = "/" + strings.TrimLeft(res.Uri, "/")
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), ",")
// 解析第一个返回值, 要求必须是结构体或者是map
outputStrictModel := metaField.Tag.Get(define.TagOutputStrict)
res.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true"
deprecated := metaField.Tag.Get(define.TagDeprecated)
res.Deprecated = deprecated == "1" || deprecated == "true"
requestContentType := strings.TrimSpace(metaField.Tag.Get(define.TagContentType))
if len(requestContentType) == 0 {
if wrapper.ArrayType(g.readMethodList).Has(res.Method) >= 0 {
// get类请求
requestContentType = consts.MimeTypeXWWWFormUrlencoded
} else {
requestContentType = consts.MimeTypeJson
}
}
res.ContentType = strings.Split(requestContentType, ",")
responseContentType := strings.TrimSpace(metaField.Tag.Get(define.TagOutputContentType))
if len(responseContentType) == 0 {
// 未设置响应类型默认JSON数据
responseContentType = consts.MimeTypeJson
}
res.OutputContentType = strings.Split(responseContentType, ",")
res.Summary = ParseStructFieldTag.Summary(metaField)
if len(res.Summary) == 0 {
res.Summary = wrapper.String(strings.ReplaceAll(strings.TrimLeft(res.Uri, "/"), "/", "_")).SnakeCaseToCamel()
}
if res.Method == "" {
return nil, errors.New("baseCfg.Method is empty")
}
if res.Uri == "" {
return nil, errors.New("baseCfg.Uri is empty")
}
if nil == g.docData.Tags {
g.docData.Tags = make([]*define.TagItem, 0)
}
// 增加tag
for _, itemTag := range res.TagList {
exist := false
for _, t := range g.docData.Tags {
if itemTag == t.Name {
exist = true
break
}
}
if !exist {
g.docData.Tags = append(g.docData.Tags, &define.TagItem{
Name: itemTag,
Description: itemTag,
})
}
}
return res, nil
}