From e2da9231a12ee6f5917e90e84b96cebacd7c780e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 13 Feb 2025 21:57:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8E=A5=E5=8F=A3=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E4=BF=A1=E6=81=AF=E8=A7=A3=E6=9E=90=20+=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=AE=BE=E7=BD=AE=E6=8E=A5=E5=8F=A3=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=BC=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/generate.go | 2 ++ define/tag.go | 14 ++++++++++++ define/uri.go | 20 +++++++++++++++++ generate.go | 55 ++++++++++++++++++++++++++++++++++++++++------ parser_test.go | 23 +++++++------------ struct_field.go | 15 +++++++++++++ 6 files changed, 107 insertions(+), 22 deletions(-) create mode 100644 define/uri.go diff --git a/define/generate.go b/define/generate.go index 66804d7..4aad3d6 100644 --- a/define/generate.go +++ b/define/generate.go @@ -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 参数配置 diff --git a/define/tag.go b/define/tag.go index 5528dcb..ac42381 100644 --- a/define/tag.go +++ b/define/tag.go @@ -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" ) diff --git a/define/uri.go b/define/uri.go new file mode 100644 index 0000000..72b15e5 --- /dev/null +++ b/define/uri.go @@ -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"` // 接口描述 +} diff --git a/generate.go b/generate.go index fa44ee6..c97f6ed 100644 --- a/generate.go +++ b/generate.go @@ -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 +} diff --git a/parser_test.go b/parser_test.go index 1d5c7f6..0d6ab83 100644 --- a/parser_test.go +++ b/parser_test.go @@ -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) { diff --git a/struct_field.go b/struct_field.go index 20bdeb0..ea67dae 100644 --- a/struct_field.go +++ b/struct_field.go @@ -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 +}