From f6810bafbd9e2cabc34e387da4ff1049fe4edbe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Mon, 17 Feb 2025 18:56:48 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E5=8C=BF=E5=90=8D=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=A4=84=E7=90=86,=20=E5=BE=85=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- generate.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/generate.go b/generate.go index 625553a..77f5f0f 100644 --- a/generate.go +++ b/generate.go @@ -326,6 +326,10 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe 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 := ParseStructField.GetParamName(inputType.Field(i)) if propertyName == "-" { continue @@ -342,6 +346,7 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe 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{ @@ -500,6 +505,11 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in // 结构体 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 := ParseStructField.GetParamName(inputType.Field(i)) if propertyName == "-" { continue @@ -580,6 +590,42 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in 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 { + if handleType.Kind() == reflect.Map || handleType.Kind() == reflect.Slice || handleType.Kind() == reflect.Array || handleType.Kind() == reflect.Struct || handleType.Kind() == reflect.Ptr { + g.AddComponentsSchema(schemaName, handleType.Field(i).Type.PkgPath(), handleType.Field(i).Type) + continue + } else { + g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(handleType.Field(i))] = &define.Property{ + Type: g.realBaseType2SwaggerType(handleType.Field(i).Type.String()), + Format: handleType.Field(i).Type.String(), + Enum: ValidateRule.Enum(handleType.Field(i)), + Default: ParseStructField.GetDefaultValue(handleType.Field(i)), + Description: ParseStructField.GetParamDesc(handleType.Field(i)), + } + } + } + } +} + // parseSliceItem 解析数组每一项 // // Author : go_developer@163.com<白茶清欢> From 0fc52fe51bf5a5fb0a528537c46f9b6695383850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Mon, 17 Feb 2025 22:04:23 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BD=A0=E5=90=8D?= =?UTF-8?q?=E5=AD=97=E7=AB=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- generate.go | 9 +++++++-- parser_test.go | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/generate.go b/generate.go index 77f5f0f..8433d85 100644 --- a/generate.go +++ b/generate.go @@ -610,17 +610,22 @@ func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructF g.handleAnonymousField(schemaName, handleType.Field(i)) continue } else { - if handleType.Kind() == reflect.Map || handleType.Kind() == reflect.Slice || handleType.Kind() == reflect.Array || handleType.Kind() == reflect.Struct || handleType.Kind() == reflect.Ptr { + 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 { - g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(handleType.Field(i))] = &define.Property{ + paramName := ParseStructField.GetParamName(itemField) + g.docData.Components.Schemas[schemaName].Properties[paramName] = &define.Property{ Type: g.realBaseType2SwaggerType(handleType.Field(i).Type.String()), Format: handleType.Field(i).Type.String(), Enum: ValidateRule.Enum(handleType.Field(i)), Default: ParseStructField.GetDefaultValue(handleType.Field(i)), Description: ParseStructField.GetParamDesc(handleType.Field(i)), } + if ValidateRule.IsRequired(itemField) { + g.docData.Components.Schemas[schemaName].Required = append(g.docData.Components.Schemas[schemaName].Required, paramName) + } } } } diff --git a/parser_test.go b/parser_test.go index a9fd398..feb4558 100644 --- a/parser_test.go +++ b/parser_test.go @@ -25,10 +25,15 @@ type Meta struct { // // Date : 17:55 2024/7/19 func Test_parser_Openapi3(t *testing.T) { + type UserExt struct { + Job string `json:"job" dc:"job" binding:"required"` + Height string `json:"height" dc:"height" binding:"required"` + } type User struct { Meta `json:"-" deprecated:"false" 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"` + UserExt } type UserDelete struct { Meta `json:"-" deprecated:"false" path:"/user/detail" method:"DELETE" desc:"测试接口" tag:"用户,搜索" content_type:"application/json" output_content_type:"application/json"` From 1ff465b95359118ada607fb397e8c6ad73986204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 18 Feb 2025 10:08:45 +0800 Subject: [PATCH 3/7] code upgrade --- generate.go | 48 ++++++++++++++++++++++++------------------------ struct_field.go | 16 ++++++++-------- validate_rule.go | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/generate.go b/generate.go index 8433d85..12348d2 100644 --- a/generate.go +++ b/generate.go @@ -330,7 +330,7 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe // 匿名字段, 直接对齐到当前的父级 g.ParseReadConfigParam(requestCfg, baseReqCfg, inputType.Field(i).Type) } - propertyName := ParseStructField.GetParamName(inputType.Field(i)) + propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i)) if propertyName == "-" { continue } @@ -352,9 +352,9 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{ Name: propertyName, In: consts.SwaggerParameterInQuery, - Description: ParseStructField.GetParamDesc(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), Required: ValidateRule.IsRequired(inputType.Field(i)), - Deprecated: ParseStructField.Deprecated(inputType.Field(i)), + Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)), Schema: &define.Schema{ // Format: realInputTypeFormat, Ref: g.getSchemaRef(schemaNameNext), @@ -368,9 +368,9 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{ Name: propertyName, In: consts.SwaggerParameterInQuery, - Description: ParseStructField.GetParamDesc(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), Required: ValidateRule.IsRequired(inputType.Field(i)), - Deprecated: ParseStructField.Deprecated(inputType.Field(i)), + Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)), Schema: &define.Schema{ Type: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()), Format: realInputTypeFormat, @@ -390,11 +390,11 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe if convertType := g.realBaseType2SwaggerType(fieldType.String()); !strings.HasPrefix(convertType, "[]") && convertType != inputType.Field(i).Type.Kind().String() { // 针对基础类型指针 baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{ - Name: ParseStructField.GetParamName(inputType.Field(i)), + Name: ParseStructFieldTag.GetParamName(inputType.Field(i)), In: consts.SwaggerParameterInQuery, - Description: ParseStructField.GetParamDesc(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), Required: ValidateRule.IsRequired(inputType.Field(i)), - Deprecated: ParseStructField.Deprecated(inputType.Field(i)), + Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)), Schema: &define.Schema{ Type: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()), Items: nil, @@ -411,11 +411,11 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe } } else { baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{ - Name: ParseStructField.GetParamName(inputType.Field(i)), + Name: ParseStructFieldTag.GetParamName(inputType.Field(i)), In: consts.SwaggerParameterInQuery, - Description: ParseStructField.GetParamDesc(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), Required: ValidateRule.IsRequired(inputType.Field(i)), - Deprecated: ParseStructField.Deprecated(inputType.Field(i)), + Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)), Schema: &define.Schema{ Type: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()), Items: nil, @@ -510,7 +510,7 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in g.handleAnonymousField(schemaName, inputType.Field(i)) continue } - propertyName := ParseStructField.GetParamName(inputType.Field(i)) + propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i)) if propertyName == "-" { continue } @@ -527,8 +527,8 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{ Type: g.realBaseType2SwaggerType(g.realBaseType2SwaggerType(inputType.Field(i).Type.String())), Format: inputType.Field(i).Type.String(), - Default: ParseStructField.GetDefaultValue(inputType.Field(i)), - Description: ParseStructField.GetParamDesc(inputType.Field(i)), + Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), } } continue @@ -542,7 +542,7 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{ Type: consts.SwaggerDataTypeObject, Format: inputType.Field(i).Type.String(), - Description: ParseStructField.GetParamDesc(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), Properties: map[string]*define.Property{}, } } else if inputType.Field(i).Type.Kind() == reflect.Array || @@ -550,7 +550,7 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{ Type: consts.SwaggerDataTypeArray, Format: inputType.Field(i).Type.String(), - Description: ParseStructField.GetParamDesc(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), Items: &define.PropertyXOf{ Ref: g.getSchemaRef(g.parseSliceItem(schemaName, inputType.Field(i).Type)), }, @@ -564,8 +564,8 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in 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)), - Description: ParseStructField.GetParamDesc(inputType.Field(i)), + Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), } } // 设置参数各种属性 @@ -615,13 +615,13 @@ func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructF g.AddComponentsSchema(schemaName, handleType.Field(i).Type.PkgPath(), handleType.Field(i).Type) continue } else { - paramName := ParseStructField.GetParamName(itemField) + paramName := ParseStructFieldTag.GetParamName(itemField) g.docData.Components.Schemas[schemaName].Properties[paramName] = &define.Property{ Type: g.realBaseType2SwaggerType(handleType.Field(i).Type.String()), Format: handleType.Field(i).Type.String(), Enum: ValidateRule.Enum(handleType.Field(i)), - Default: ParseStructField.GetDefaultValue(handleType.Field(i)), - Description: ParseStructField.GetParamDesc(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) @@ -708,11 +708,11 @@ func (g *Generate) realType2SwaggerType(realType string) string { // // Date : 16:13 2025/2/13 func (g *Generate) setStructFieldProperty(schemaName string, structField reflect.StructField) { - paramName := ParseStructField.GetParamName(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[ParseStructField.GetParamName(structField)].Enum = ValidateRule.Enum(structField) + g.docData.Components.Schemas[schemaName].Properties[ParseStructFieldTag.GetParamName(structField)].Enum = ValidateRule.Enum(structField) } // parseBaseUriConfig 通过Meta字段解析Uri基础配置信息 @@ -769,7 +769,7 @@ func (g *Generate) parseBaseUriConfig(uriPrefix string, paramType reflect.Type) responseContentType = consts.MimeTypeJson } res.OutputContentType = strings.Split(responseContentType, ",") - res.Summary = ParseStructField.Summary(metaField) + res.Summary = ParseStructFieldTag.Summary(metaField) if res.Method == "" { return nil, errors.New("baseCfg.Method is empty") } diff --git a/struct_field.go b/struct_field.go index 18a165d..f7f3561 100644 --- a/struct_field.go +++ b/struct_field.go @@ -15,10 +15,10 @@ import ( ) var ( - ParseStructField = parseStructField{} + ParseStructFieldTag = parseStructFieldTag{} ) -type parseStructField struct { +type parseStructFieldTag struct { } // GetParamName 获取参数名称 @@ -26,7 +26,7 @@ type parseStructField struct { // Author : go_developer@163.com<白茶清欢> // // Date : 21:58 2025/2/11 -func (psf parseStructField) GetParamName(structField reflect.StructField) string { +func (psf parseStructFieldTag) GetParamName(structField reflect.StructField) string { paramNameTagList := []string{ define.TagJson, define.TagForm, define.TagXml, define.TagYaml, @@ -49,7 +49,7 @@ func (psf parseStructField) GetParamName(structField reflect.StructField) string // Author : go_developer@163.com<白茶清欢> // // Date : 22:01 2025/2/11 -func (psf parseStructField) GetParamDesc(structField reflect.StructField) string { +func (psf parseStructFieldTag) GetParamDesc(structField reflect.StructField) string { descTagList := []string{define.TagDesc, define.TagDescription} for _, tag := range descTagList { tagVal := structField.Tag.Get(tag) @@ -66,7 +66,7 @@ func (psf parseStructField) GetParamDesc(structField reflect.StructField) string // Author : go_developer@163.com<白茶清欢> // // Date : 22:05 2025/2/11 -func (psf parseStructField) GetDefaultValue(structField reflect.StructField) any { +func (psf parseStructFieldTag) GetDefaultValue(structField reflect.StructField) any { defaultTagList := []string{define.TagD, define.TagDefault} fieldType := structField.Type.Kind().String() for _, tag := range defaultTagList { @@ -104,7 +104,7 @@ func (psf parseStructField) GetDefaultValue(structField reflect.StructField) any // Author : go_developer@163.com<白茶清欢> // // Date : 15:30 2025/2/13 -func (psf parseStructField) GetValidateRule(structField reflect.StructField) string { +func (psf parseStructFieldTag) GetValidateRule(structField reflect.StructField) string { defaultTagList := []string{define.TagValidate, define.TagBinding} for _, tag := range defaultTagList { if tagVal, exist := structField.Tag.Lookup(tag); exist && len(tagVal) > 0 { @@ -119,7 +119,7 @@ func (psf parseStructField) GetValidateRule(structField reflect.StructField) str // Author : go_developer@163.com<白茶清欢> // // Date : 21:12 2025/2/13 -func (psf parseStructField) Deprecated(structField reflect.StructField) bool { +func (psf parseStructFieldTag) 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") { @@ -134,7 +134,7 @@ func (psf parseStructField) Deprecated(structField reflect.StructField) bool { // Author : go_developer@163.com<白茶清欢> // // Date : 15:15 2025/2/14 -func (psf parseStructField) Summary(structField reflect.StructField) string { +func (psf parseStructFieldTag) Summary(structField reflect.StructField) string { defaultTagList := []string{define.TagSummary} for _, tag := range defaultTagList { if tagVal, exist := structField.Tag.Lookup(tag); exist && len(tagVal) > 0 { diff --git a/validate_rule.go b/validate_rule.go index a4fa52c..e33f0e9 100644 --- a/validate_rule.go +++ b/validate_rule.go @@ -85,7 +85,7 @@ func (r validateRule) Enum(structField reflect.StructField) []any { // Date : 15:29 2025/2/13 func (r validateRule) getValidateRuleTable(structField reflect.StructField) map[string]string { res := map[string]string{} - ruleStr := ParseStructField.GetValidateRule(structField) + ruleStr := ParseStructFieldTag.GetValidateRule(structField) if len(ruleStr) == 0 { return res } From f0f15a4df3dd98a9e7db267ea9f5b266b29725f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 18 Feb 2025 10:51:24 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E4=BC=98=E5=8C=96=20summary/operationId?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- generate.go | 110 +++++++++++++++++++++------------------------------- 1 file changed, 44 insertions(+), 66 deletions(-) diff --git a/generate.go b/generate.go index 12348d2..4495927 100644 --- a/generate.go +++ b/generate.go @@ -270,7 +270,7 @@ func (g *Generate) getApiDocBaseCfg(baseCfg *define.UriBaseConfig, paramType ref Summary: baseCfg.Summary, Description: baseCfg.Description, ExternalDocs: nil, - OperationID: baseCfg.Summary + "(" + strings.ReplaceAll(strings.TrimLeft(baseCfg.Uri, "/"), "/", "-") + ")", + OperationID: baseCfg.Summary + "(" + baseCfg.Method + "-" + strings.ReplaceAll(strings.TrimLeft(baseCfg.Uri, "/"), "/", "-") + ")", Parameters: make([]*define.PathConfigParameter, 0), RequestBody: &define.RequestBody{ Required: true, @@ -338,11 +338,31 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe // 空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 { @@ -364,22 +384,7 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe AllowReserved: false, }) } else { - // 当做默认基础类型, 默认不会出现 *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: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()), - Format: realInputTypeFormat, - }, - AllowEmptyValue: false, - Style: "", - Explode: false, - AllowReserved: false, - }) + } continue } @@ -387,29 +392,6 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe 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() { - // 针对基础类型指针 - 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: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()), - Items: nil, - Ref: "", - Format: realInputTypeFormat, - }, - AllowEmptyValue: false, - Style: "", - Explode: false, - AllowReserved: false, - Ref: "", - }) - continue - } - } else { baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{ Name: ParseStructFieldTag.GetParamName(inputType.Field(i)), In: consts.SwaggerParameterInQuery, @@ -417,7 +399,7 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe Required: ValidateRule.IsRequired(inputType.Field(i)), Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)), Schema: &define.Schema{ - Type: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()), + Type: convertBaseType, Items: nil, Ref: "", Format: realInputTypeFormat, @@ -524,8 +506,9 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in } } else { // 当做默认基础类型, 默认不会出现 *map *[] + convertBaseType, _ := g.realBaseType2SwaggerType(inputType.Field(i).Type.String()) g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{ - Type: g.realBaseType2SwaggerType(g.realBaseType2SwaggerType(inputType.Field(i).Type.String())), + Type: convertBaseType, Format: inputType.Field(i).Type.String(), Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)), Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), @@ -559,10 +542,10 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in } 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: g.realBaseType2SwaggerType(inputType.Field(i).Type.String()), + Type: convertBaseType, Format: inputType.Field(i).Type.String(), Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)), Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), @@ -579,7 +562,7 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in // 非基础数据类型 return g.AddComponentsSchema(schemaName, inputType.Elem().String(), inputType.Elem()) } else { - convertType := g.realBaseType2SwaggerType(inputType.String()) + convertType, _ := g.realBaseType2SwaggerType(inputType.String()) g.docData.Components.Schemas[schemaName].Properties[schemaName] = &define.Property{ Type: convertType, Format: inputType.String(), @@ -615,9 +598,10 @@ func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructF 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: g.realBaseType2SwaggerType(handleType.Field(i).Type.String()), + Type: convertBaseType, Format: handleType.Field(i).Type.String(), Enum: ValidateRule.Enum(handleType.Field(i)), Default: ParseStructFieldTag.GetDefaultValue(handleType.Field(i)), @@ -671,37 +655,28 @@ func (g *Generate) getSchemaRef(schemaName string) string { // Author : go_developer@163.com<白茶清欢> // // Date : 20:25 2025/2/11 -func (g *Generate) realBaseType2SwaggerType(realType string) string { +func (g *Generate) realBaseType2SwaggerType(realType string) (string, bool) { switch realType { case "bool", "*bool": - return consts.SwaggerDataTypeBoolean + return consts.SwaggerDataTypeBoolean, true case "string", "*string": - return consts.SwaggerDataTypeString + return consts.SwaggerDataTypeString, true case "byte", "*byte": - return consts.SwaggerDataTypeByte + return consts.SwaggerDataTypeByte, true case "float32", "*float32", "float64", "*float64": - return consts.SwaggerDataTypeDouble + return consts.SwaggerDataTypeDouble, true case "int", "*int", "uint", "*uint", "int64", "*int64", "uint64", "*uint64": - return consts.SwaggerDataTypeInteger + return consts.SwaggerDataTypeInteger, true case "int8", "*int8", "uint8", "*uint8", "int16", "*int16", "uint16", "*uint16", "int32", "*int32", "uint32", "*uint32": - return consts.SwaggerDataTypeInteger + return consts.SwaggerDataTypeInteger, true default: - return realType + if strings.HasPrefix(realType, "[]") { + return consts.SwaggerDataTypeArray, true + } + return realType, false } } -// 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) -} - // setStructFieldProperty 添加struct_field各种属性 // // Author : go_developer@163.com<白茶清欢> @@ -770,6 +745,9 @@ func (g *Generate) parseBaseUriConfig(uriPrefix string, paramType reflect.Type) } 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") } From 848adb5b292ae5147c5cb1b1630cd158476feb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 18 Feb 2025 11:48:39 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=B1=BB=E5=9E=8B=E7=9A=84=E6=95=B0=E7=BB=84?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/openapi.go | 4 +++- generate.go | 35 ++++++++++++++++++++++++++--------- parser_test.go | 8 +++++--- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/define/openapi.go b/define/openapi.go index f4f45d0..c590a60 100644 --- a/define/openapi.go +++ b/define/openapi.go @@ -162,7 +162,9 @@ type Property struct { // // Date : 17:13 2024/7/19 type PropertyXOf struct { - Ref string `json:"$ref"` // 引用的结构描述 + Type string `json:"type,omitempty"` // 基础类型 + Format string `json:"format,omitempty"` // 真实类型 + Ref string `json:"$ref,omitempty"` // 引用的结构描述 } // SchemaDiscriminator 当一个 request bodies 或 response payloads 可以是多种 schemas 时,可以使用一个 discriminator 对象来帮助序列化、反序列化和校验 diff --git a/generate.go b/generate.go index 4495927..8d5f3dd 100644 --- a/generate.go +++ b/generate.go @@ -469,16 +469,23 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in } // 数组 if inputType.Kind() == reflect.Slice || inputType.Kind() == reflect.Array { + sliceItemType := g.parseSliceItem(schemaName, inputType) + propertyXOf := &define.PropertyXOf{ + Ref: g.getSchemaRef(sliceItemType), + } + if !strings.HasPrefix(propertyXOf.Ref, "#") { + propertyXOf.Type = propertyXOf.Ref + propertyXOf.Format = sliceItemType + propertyXOf.Ref = "" + } 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)} + g.docData.Components.Schemas[schemaName].Items = propertyXOf } 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)}, + Items: propertyXOf, } } @@ -530,14 +537,20 @@ 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 { + sliceItemType := g.parseSliceItem(schemaName, inputType.Field(i).Type) + propertyXOf := &define.PropertyXOf{ + Ref: g.getSchemaRef(sliceItemType), + } + if !strings.HasPrefix(propertyXOf.Ref, "#") { + propertyXOf.Type = propertyXOf.Ref + propertyXOf.Format = sliceItemType + propertyXOf.Ref = "" + } 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{}, + Items: propertyXOf, } } else { g.AddComponentsSchema(schemaName, propertyName, inputType.Field(i).Type) @@ -647,7 +660,11 @@ func (g *Generate) getSchemaRef(schemaName string) string { return "" } schemaName = strings.ReplaceAll(schemaName, "*", "") // 去除指针类型 * - return "#/components/schemas/" + strings.ReplaceAll(schemaName, "/", ".") + convertType, isBaseType := g.realBaseType2SwaggerType(schemaName) + if isBaseType { + return convertType + } + return "#/components/schemas/" + strings.ReplaceAll(convertType, "/", ".") } // realType2SwaggerType golang 真实数据类型转换为golang数据类型 diff --git a/parser_test.go b/parser_test.go index feb4558..aa72e77 100644 --- a/parser_test.go +++ b/parser_test.go @@ -30,9 +30,11 @@ func Test_parser_Openapi3(t *testing.T) { Height string `json:"height" dc:"height" binding:"required"` } type User struct { - Meta `json:"-" deprecated:"false" 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"` + Meta `json:"-" deprecated:"false" 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"` + IDList []int64 `json:"id_list" dc:"id_list...." binding:"required"` + IDPtrList []*int64 `json:"id_ptr_list" dc:"id_ptr_list...." binding:"required"` UserExt } type UserDelete struct { From d407826f883eca45daaff2817b2189f5af4763d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 18 Feb 2025 12:23:51 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=AF=8F=E4=B8=80=E9=A1=B9=E6=95=B0=E6=8D=AE=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- generate.go | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/generate.go b/generate.go index 8d5f3dd..7544aee 100644 --- a/generate.go +++ b/generate.go @@ -469,14 +469,16 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in } // 数组 if inputType.Kind() == reflect.Slice || inputType.Kind() == reflect.Array { - sliceItemType := g.parseSliceItem(schemaName, inputType) - propertyXOf := &define.PropertyXOf{ - Ref: g.getSchemaRef(sliceItemType), - } - if !strings.HasPrefix(propertyXOf.Ref, "#") { - propertyXOf.Type = propertyXOf.Ref + sliceItemType, itemIsBaseType := g.parseSliceItem(schemaName, inputType) + propertyXOf := &define.PropertyXOf{} + if itemIsBaseType { + propertyXOf.Type, _ = g.realBaseType2SwaggerType(sliceItemType) propertyXOf.Format = sliceItemType propertyXOf.Ref = "" + } else { + propertyXOf.Type = "" + propertyXOf.Format = "" + propertyXOf.Ref = g.getSchemaRef(sliceItemType) } if len(rootSchemaName) == 0 { g.docData.Components.Schemas[schemaName].Type = consts.SwaggerDataTypeArray @@ -537,14 +539,16 @@ 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 { - sliceItemType := g.parseSliceItem(schemaName, inputType.Field(i).Type) - propertyXOf := &define.PropertyXOf{ - Ref: g.getSchemaRef(sliceItemType), - } - if !strings.HasPrefix(propertyXOf.Ref, "#") { - propertyXOf.Type = propertyXOf.Ref + sliceItemType, itemIsBaseType := g.parseSliceItem(schemaName, inputType.Field(i).Type) + propertyXOf := &define.PropertyXOf{} + if itemIsBaseType { + propertyXOf.Type, _ = g.realBaseType2SwaggerType(sliceItemType) propertyXOf.Format = sliceItemType propertyXOf.Ref = "" + } else { + propertyXOf.Type = "" + propertyXOf.Format = "" + propertyXOf.Ref = g.getSchemaRef(sliceItemType) } g.docData.Components.Schemas[schemaName].Properties[propertyName] = &define.Property{ Type: consts.SwaggerDataTypeArray, @@ -633,21 +637,26 @@ func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructF // Author : go_developer@163.com<白茶清欢> // // Date : 21:33 2025/2/8 -func (g *Generate) parseSliceItem(rootSchemaName string, inputType reflect.Type) string { +func (g *Generate) parseSliceItem(rootSchemaName string, inputType reflect.Type) (string, bool) { if inputType.Kind() != reflect.Slice && inputType.Kind() != reflect.Array { // 不是数组 - return "" + return "", false } sliceValue := reflect.MakeSlice(inputType, 1, 1) sliceItemType := sliceValue.Index(0).Type() + realSliceItemType := sliceItemType.String() if sliceItemType.Kind() == reflect.Ptr { sliceItemType = sliceItemType.Elem() } + _, isBaseType := g.realBaseType2SwaggerType(sliceItemType.String()) + if isBaseType { + return realSliceItemType, true + } g.AddComponentsSchema(rootSchemaName, sliceItemType.PkgPath(), sliceItemType) if len(sliceItemType.PkgPath()) == 0 { - return sliceItemType.String() + return realSliceItemType, false } - return sliceItemType.PkgPath() + "." + sliceItemType.Name() + return sliceItemType.PkgPath() + "." + sliceItemType.Name(), false } // getSchemaRef 获取引用的类型 From 4e54b5fe33d1fc5bf8bb1795316fd78bc3506e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 18 Feb 2025 12:36:41 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8C=BF=E5=90=8D?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=B8=AD=E5=9F=BA=E7=A1=80=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=8C=87=E9=92=88=E8=A7=A3=E6=9E=90=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- generate.go | 18 +++++++++--------- parser_test.go | 5 +++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/generate.go b/generate.go index 7544aee..b1cdf44 100644 --- a/generate.go +++ b/generate.go @@ -606,23 +606,23 @@ func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructF } 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) + baseConvertType, isBaseType := g.realBaseType2SwaggerType(itemField.Type.String()) + if !isBaseType { + g.AddComponentsSchema(schemaName, itemField.Type.PkgPath(), itemField.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)), + Type: baseConvertType, + Format: itemField.Type.String(), + Enum: ValidateRule.Enum(itemField), + Default: ParseStructFieldTag.GetDefaultValue(itemField), + Description: ParseStructFieldTag.GetParamDesc(itemField), } if ValidateRule.IsRequired(itemField) { g.docData.Components.Schemas[schemaName].Required = append(g.docData.Components.Schemas[schemaName].Required, paramName) diff --git a/parser_test.go b/parser_test.go index aa72e77..ff7cfca 100644 --- a/parser_test.go +++ b/parser_test.go @@ -26,8 +26,9 @@ type Meta struct { // Date : 17:55 2024/7/19 func Test_parser_Openapi3(t *testing.T) { type UserExt struct { - Job string `json:"job" dc:"job" binding:"required"` - Height string `json:"height" dc:"height" binding:"required"` + Job string `json:"job" dc:"job" binding:"required"` + JobPtr *string `json:"job_ptr" dc:"job_ptr" binding:"required"` + Height string `json:"height" dc:"height" binding:"required"` } type User struct { Meta `json:"-" deprecated:"false" path:"/user/detail" method:"POST" desc:"测试接口" tag:"用户,搜索" content_type:"application/json" output_content_type:"application/json"`