优化接口基础信息解析 + 支持设置接口是否弃用

This commit is contained in:
白茶清欢 2025-02-13 21:57:18 +08:00
parent 45a25e0018
commit e2da9231a1
6 changed files with 107 additions and 22 deletions

View File

@ -37,6 +37,8 @@ type UriBaseConfig struct {
Description string `json:"description"` // 接口详细描述
ParamList []*ParamConfig `json:"param_list"` // 参数列表
ResultList []*ResultConfig `json:"result_list"` // 返回值列表
Deprecated bool `json:"deprecated"` // 是否弃用
OutputStrict bool `json:"output_strict"` // 严格模式
}
// ParamConfig 参数配置

View File

@ -21,4 +21,18 @@ const (
TagDescription = "description"
TagD = "d"
TagDefault = "default"
TagDeprecated = "deprecated"
)
const (
TagNamePath = "path" // 接口的请求路径
TagNameMethod = "method" // 接口的请求方法
TagNameUriTag = "tag" // 接口的tag
TagNameDesc = "desc" // 接口的描述
TagNameOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map非严格模式返回任意值
TagNameBinding = "binding" // gin 内置的验证规则tag
TagNameValidate = "validate" // validator v10 默认的验证规则tag
TagNameErrMsg = "err" // 验证失败错误信息tag
TagNameContentType = "content_type"
TagNameOutputContentType = "output_content_type"
)

20
define/uri.go Normal file
View File

@ -0,0 +1,20 @@
// Package define ...
//
// Description : define ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-13 21:29
package define
// UriConfig 接口基础配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:29 2025/2/13
type UriConfig struct {
Path string `json:"path"` // 接口路由, 必须配置
RequestMethod string `json:"request_method"` // 接口请求方法, 必须配置
TagList []string `json:"tag_list"` // 接口分组
Desc string `json:"desc"` // 接口描述
}

View File

@ -170,7 +170,8 @@ func (g *Generate) AddApi(baseCfg *define.UriBaseConfig, paramList []*define.Par
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:22 2025/2/9
func (g *Generate) AddApiFromInAndOut(baseCfg *define.UriBaseConfig, paramType reflect.Type, resultType reflect.Type) error {
func (g *Generate) AddApiFromInAndOut(paramType reflect.Type, resultType reflect.Type) error {
baseCfg := g.parseBaseUriConfig(paramType)
if nil == baseCfg {
return errors.New("baseCfg is nil")
}
@ -220,7 +221,7 @@ func (g *Generate) AddApiFromInAndOut(baseCfg *define.UriBaseConfig, paramType r
},
},
Callbacks: nil,
Deprecated: false,
Deprecated: baseCfg.Deprecated,
Security: nil,
Servers: nil,
}
@ -276,6 +277,10 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
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
@ -326,6 +331,10 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
}
// g.docData.Components.Schemas[schemaName].Ref = consts.SwaggerDataTypeObject
for i := 0; i < inputType.NumField(); i++ {
propertyName := ParseStructField.GetParamName(inputType.Field(i))
if propertyName == "-" {
continue
}
if inputType.Field(i).Type.Kind() == reflect.Ptr ||
inputType.Field(i).Type.Kind() == reflect.Struct ||
inputType.Field(i).Type.Kind() == reflect.Map ||
@ -333,7 +342,7 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
inputType.Field(i).Type.Kind() == reflect.Slice {
if convertType := g.realBaseType2SwaggerType(inputType.Field(i).Type.String()); !strings.HasPrefix(convertType, "[]") && convertType != inputType.Field(i).Type.Kind().String() {
// 针对基础类型指针
g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(inputType.Field(i))] = &define.Property{
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Type: g.realBaseType2SwaggerType(convertType),
Format: inputType.Field(i).Type.String(),
Default: ParseStructField.GetDefaultValue(inputType.Field(i)),
@ -343,7 +352,7 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
}
if inputType.Field(i).Type.Kind() == reflect.Struct ||
inputType.Field(i).Type.Kind() == reflect.Map {
g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(inputType.Field(i))] = &define.Property{
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Type: consts.SwaggerDataTypeObject,
Format: inputType.Field(i).Type.String(),
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
@ -351,7 +360,7 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
}
} else if inputType.Field(i).Type.Kind() == reflect.Array ||
inputType.Field(i).Type.Kind() == reflect.Slice {
g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(inputType.Field(i))] = &define.Property{
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Type: consts.SwaggerDataTypeArray,
Format: inputType.Field(i).Type.String(),
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
@ -363,11 +372,11 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
} else if inputType.Field(i).Type.Kind() == reflect.Ptr {
} else {
g.AddComponentsSchema(schemaName, ParseStructField.GetParamName(inputType.Field(i)), inputType.Field(i).Type)
g.AddComponentsSchema(schemaName, propertyName, inputType.Field(i).Type)
}
} else {
g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(inputType.Field(i))] = &define.Property{
g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{
Type: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()),
Format: inputType.Field(i).Type.String(),
Default: ParseStructField.GetDefaultValue(inputType.Field(i)),
@ -475,3 +484,35 @@ func (g *Generate) setStructFieldProperty(schemaName string, structField reflect
}
g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(structField)].Enum = ValidateRule.Enum(structField)
}
func (g *Generate) parseBaseUriConfig(paramType reflect.Type) *define.UriBaseConfig {
// 解析meta信息
metaField, metaFieldExist := paramType.FieldByName("Meta")
if !metaFieldExist {
return nil
}
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.TagNamePath)
res.Method = metaField.Tag.Get(define.TagNameMethod)
res.Description = metaField.Tag.Get(define.TagNameDesc)
res.TagList = strings.Split(metaField.Tag.Get(define.TagNameUriTag), ",")
// 解析第一个返回值, 要求必须是结构体或者是map
outputStrictModel := metaField.Tag.Get(define.TagNameOutputStrict)
res.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true"
deprecated := metaField.Tag.Get(define.TagDeprecated)
res.Deprecated = deprecated == "1" || deprecated == "true"
res.ContentType = strings.Split(metaField.Tag.Get(define.TagNameContentType), ",")
res.OutputContentType = strings.Split(metaField.Tag.Get(define.TagNameOutputContentType), ",")
return res
}

View File

@ -10,13 +10,14 @@ package api_doc
import (
"encoding/json"
"fmt"
"git.zhangdeman.cn/gateway/api-doc/define"
"git.zhangdeman.cn/zhangdeman/serialize"
"net/http"
"reflect"
"testing"
)
type Meta struct {
}
// Test_parser_Openapi3 测试数据结构定义正确性
//
// Author : go_developer@163.com<白茶清欢>
@ -24,6 +25,7 @@ import (
// Date : 17:55 2024/7/19
func Test_parser_Openapi3(t *testing.T) {
type User struct {
Meta `json:"-" deprecated:"true" path:"/user/detail" method:"POST" desc:"测试接口" tag:"用户,搜索" content_type:"application/json" output_content_type:"application/json"`
Name string `json:"name" d:"zhang" desc:"用户姓名" binding:"required"`
Age string `json:"age" d:"18" desc:"年龄" binding:"required,oneof=12 13 18 90"`
}
@ -31,22 +33,13 @@ func Test_parser_Openapi3(t *testing.T) {
Total int64 `json:"total" binding:"required"`
UserList []User `json:"user_list"`
}
var l List
var o List
var f User
g := NewOpenapiDoc(nil, nil)
g.AddApiFromInAndOut(&define.UriBaseConfig{
Uri: "/ent/user/detail",
Method: http.MethodPost,
ContentType: []string{"application/x-www-form-urlencoded"},
OutputContentType: []string{"application/yml", "application/json", "application/xml"},
TagList: []string{"测试文档生成"},
Summary: "测试接口",
Description: "",
ParamList: nil,
ResultList: nil,
}, reflect.TypeOf(l), reflect.TypeOf(l))
g.AddApiFromInAndOut(reflect.TypeOf(f), reflect.TypeOf(o))
byteData, _ := json.Marshal(g.docData)
fmt.Println(string(byteData))
fmt.Println(g.parseSliceItem("", reflect.TypeOf(l)))
fmt.Println(g.parseSliceItem("", reflect.TypeOf(o)))
}
func TestParseForSwagger(t *testing.T) {

View File

@ -111,3 +111,18 @@ func (psf parseStructField) GetValidateRule(structField reflect.StructField) str
}
return ""
}
// Deprecated 是否弃用
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:12 2025/2/13
func (psf parseStructField) Deprecated(structField reflect.StructField) bool {
defaultTagList := []string{define.TagDeprecated}
for _, tag := range defaultTagList {
if tagVal, exist := structField.Tag.Lookup(tag); exist && (tagVal == "1" || strings.ToLower(tagVal) == "true") {
return true
}
}
return false
}