diff --git a/define/openapi.go b/define/openapi.go index ec82b92..9bf93d8 100644 --- a/define/openapi.go +++ b/define/openapi.go @@ -119,24 +119,25 @@ type PathConfigParameter struct { // // Date : 12:32 2024/7/19 type Schema struct { - Nullable bool `json:"nullable,omitempty"` // 对于定义的schema,允许发送 null 值。默认值是 false. - Discriminator *SchemaDiscriminator `json:"discriminator,omitempty"` // 说白了, 就是一个字段可能是不同的数据结构。。。 - ReadOnly bool `json:"readOnly,omitempty"` // 仅与 Schema "properties" 定义有关。 声明此属性是 "readonly" 的。这意味着它可以作为 response 的一部分但不应该作为 request 的一部分被发送。如果一个 property 的 readOnly 被标记为 true 且在 required 列表中,required 将只作用于 response。一个 property 的 readOnly 和 writeOnly 不允许同时被标记为 true。默认值是 false。 - WriteOnly bool `json:"writeOnly,omitempty"` // 仅与 Schema "properties" 定义有关。声明此 property 为 "write only"。所以它可以作为 request 的一部分而不应该作为 response 的一部分被发送。如果一个 property 的 writeOnly 被标记为 true 且在 required 列表中,required 将只作用于 request。一个 property 的 readOnly 和 writeOnly 不能同时被标记为 true。默认值是 false。 - Xml *XML `json:"xml,omitempty"` // 这只能用于 properties schemas,在root schemas 中没有效果。 - ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"` // 此 schema 附加的外部文档。 - Example string `json:"example,omitempty"` // 一个用于示范此 schema实例的示例,可以是任意格式。为了表达无法用 JSON 或 YAML 格式呈现的示例,可以使用 string 类型的值,且在必要的地方需要使用字符转义。 - Deprecated bool `json:"deprecated,omitempty"` // 表示一个 schema 是废弃的,应该逐渐被放弃使用。默认值是 false. - Properties map[string]*Property `json:"properties,omitempty"` // 数据字段 => 数据规则 - Required []string `json:"required,omitempty"` // 必传属性列表 - Enum []any `json:"enum,omitempty"` // 枚举值列表 - Type string `json:"type,omitempty"` // 类型 - Items *PropertyXOf `json:"items,omitempty"` // items 必须存在如果 type 的值是 array。 - Ref string `json:"$ref,omitempty"` // 类型引用 - Format string `json:"format,omitempty"` // 格式化类型 - Maximum *int64 `json:"maximum,omitempty"` // 最大值 - Minimum *int64 `json:"minimum,omitempty"` // 最小值 - Default any `json:"default,omitempty"` // 默认值 + Nullable bool `json:"nullable,omitempty"` // 对于定义的schema,允许发送 null 值。默认值是 false. + Discriminator *SchemaDiscriminator `json:"discriminator,omitempty"` // 说白了, 就是一个字段可能是不同的数据结构。。。 + ReadOnly bool `json:"readOnly,omitempty"` // 仅与 Schema "properties" 定义有关。 声明此属性是 "readonly" 的。这意味着它可以作为 response 的一部分但不应该作为 request 的一部分被发送。如果一个 property 的 readOnly 被标记为 true 且在 required 列表中,required 将只作用于 response。一个 property 的 readOnly 和 writeOnly 不允许同时被标记为 true。默认值是 false。 + WriteOnly bool `json:"writeOnly,omitempty"` // 仅与 Schema "properties" 定义有关。声明此 property 为 "write only"。所以它可以作为 request 的一部分而不应该作为 response 的一部分被发送。如果一个 property 的 writeOnly 被标记为 true 且在 required 列表中,required 将只作用于 request。一个 property 的 readOnly 和 writeOnly 不能同时被标记为 true。默认值是 false。 + Xml *XML `json:"xml,omitempty"` // 这只能用于 properties schemas,在root schemas 中没有效果。 + ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"` // 此 schema 附加的外部文档。 + Example string `json:"example,omitempty"` // 一个用于示范此 schema实例的示例,可以是任意格式。为了表达无法用 JSON 或 YAML 格式呈现的示例,可以使用 string 类型的值,且在必要的地方需要使用字符转义。 + Deprecated bool `json:"deprecated,omitempty"` // 表示一个 schema 是废弃的,应该逐渐被放弃使用。默认值是 false. + Properties map[string]*Property `json:"properties,omitempty"` // 数据字段 => 数据规则 + Required []string `json:"required,omitempty"` // 必传属性列表 + Enum []any `json:"enum,omitempty"` // 枚举值列表 + XEnumDescription map[string]string `json:"x-enumDescriptions,omitempty"` // 枚举值描述的扩展, redoc-free支持 + Type string `json:"type,omitempty"` // 类型 + Items *PropertyXOf `json:"items,omitempty"` // items 必须存在如果 type 的值是 array。 + Ref string `json:"$ref,omitempty"` // 类型引用 + Format string `json:"format,omitempty"` // 格式化类型 + Maximum *int64 `json:"maximum,omitempty"` // 最大值 + Minimum *int64 `json:"minimum,omitempty"` // 最小值 + Default any `json:"default,omitempty"` // 默认值 } // Property 是从 JSON Schema 提取出来的,但是做了一些调整以适应 OpenAPI Specification。 @@ -148,6 +149,7 @@ type Property struct { Type string `json:"type,omitempty"` // 数据类型, swagger本身的定义 Format string `json:"format,omitempty"` // 对应编程语言中的数据类型描述 Enum []any `json:"enum,omitempty"` // 枚举值列表 + XEnumDescription map[string]string `json:"x-enumDescriptions,omitempty"` // 枚举值描述的扩展, redoc-free支持 Default any `json:"default,omitempty"` // 默认值 : 不同于 JSON Schema,这个值必须符合定义与相同级别的 Schema 对象 中定义的类型,比如 type 是 string,那么 default 可以是 "foo" 但不能是 1。 Description string `json:"description,omitempty"` // 数据描述, CommonMark syntax可以被用来呈现富文本格式. Maximum *int64 `json:"maximum,omitempty"` // 最大值 diff --git a/define/tag.go b/define/tag.go index d8463e3..9a5edf3 100644 --- a/define/tag.go +++ b/define/tag.go @@ -8,28 +8,29 @@ package define const ( - TagJson = "json" - TagXml = "xml" - TagYaml = "yaml" - TagYml = "yml" - TagForm = "form" - TagBinding = "binding" - TagValidate = "validate" - TagErr = "err" - TagMsg = "msg" - TagDc = "dc" - TagDesc = "desc" - TagDescription = "description" - TagD = "d" - TagDefault = "default" - TagDeprecated = "deprecated" - TagSummary = "summary" - TagPath = "path" // 接口的请求路径 - TagMethod = "method" // 接口的请求方法 - TagUriTag = "tag" // 接口的tag - TagOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map,非严格模式返回任意值 - TagErrMsg = "err" // 验证失败错误信息tag - TagContentType = "content_type" - TagOutputContentType = "output_content_type" - TagNameOmitempty = "omitempty" + TagJson = "json" + TagXml = "xml" + TagYaml = "yaml" + TagYml = "yml" + TagForm = "form" + TagBinding = "binding" + TagValidate = "validate" + TagErr = "err" + TagMsg = "msg" + TagDc = "dc" + TagDesc = "desc" + TagDescription = "description" + TagD = "d" + TagDefault = "default" + TagDeprecated = "deprecated" + TagSummary = "summary" + TagPath = "path" // 接口的请求路径 + TagMethod = "method" // 接口的请求方法 + TagUriTag = "tag" // 接口的tag + TagOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map,非严格模式返回任意值 + TagErrMsg = "err" // 验证失败错误信息tag + TagContentType = "content_type" + TagOutputContentType = "output_content_type" + TagNameOmitempty = "omitempty" + TagNameEnumDescription = "enum-desc" // 枚举值描述: enum1:enum1-desc||enum2:enum2-desc ) diff --git a/generate.go b/generate.go index 3214430..52072a1 100644 --- a/generate.go +++ b/generate.go @@ -354,11 +354,13 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe Required: ValidateRule.IsRequired(inputType.Field(i)), Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)), Schema: &define.Schema{ - Minimum: ValidateRule.Minimum(inputType.Field(i)), - Maximum: ValidateRule.Maximum(inputType.Field(i)), - Type: convertBaseType, - Format: realInputTypeFormat, - Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)), + Minimum: ValidateRule.Minimum(inputType.Field(i)), + Maximum: ValidateRule.Maximum(inputType.Field(i)), + Type: convertBaseType, + Format: realInputTypeFormat, + Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)), + Enum: ValidateRule.Enum(inputType.Field(i)), + XEnumDescription: ParseStructFieldTag.EnumDescription(inputType.Field(i)), }, AllowEmptyValue: false, Style: "", @@ -402,10 +404,12 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe Required: ValidateRule.IsRequired(inputType.Field(i)), Deprecated: ParseStructFieldTag.Deprecated(inputType.Field(i)), Schema: &define.Schema{ - Type: convertBaseType, - Items: nil, - Ref: "", - Format: realInputTypeFormat, + Type: convertBaseType, + Items: nil, + Ref: "", + Format: realInputTypeFormat, + Enum: ValidateRule.Enum(inputType.Field(i)), + XEnumDescription: ParseStructFieldTag.EnumDescription(inputType.Field(i)), }, AllowEmptyValue: false, Style: "", @@ -521,12 +525,14 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in // 当做默认基础类型, 默认不会出现 *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(), - Maximum: ValidateRule.Maximum(inputType.Field(i)), - Minimum: ValidateRule.Minimum(inputType.Field(i)), - Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)), - Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), + Type: convertBaseType, + Format: inputType.Field(i).Type.String(), + Enum: ValidateRule.Enum(inputType.Field(i)), + XEnumDescription: ParseStructFieldTag.EnumDescription(inputType.Field(i)), + Maximum: ValidateRule.Maximum(inputType.Field(i)), + Minimum: ValidateRule.Minimum(inputType.Field(i)), + Default: ParseStructFieldTag.GetDefaultValue(inputType.Field(i)), + Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)), } } continue @@ -624,11 +630,12 @@ func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructF } else { paramName := ParseStructFieldTag.GetParamName(itemField) g.docData.Components.Schemas[schemaName].Properties[paramName] = &define.Property{ - Type: baseConvertType, - Format: itemField.Type.String(), - Enum: ValidateRule.Enum(itemField), - Default: ParseStructFieldTag.GetDefaultValue(handleType.Field(i)), - Description: ParseStructFieldTag.GetParamDesc(handleType.Field(i)), + Type: baseConvertType, + Format: itemField.Type.String(), + Enum: ValidateRule.Enum(itemField), + XEnumDescription: ParseStructFieldTag.EnumDescription(itemField), + 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) diff --git a/struct_field.go b/struct_field.go index cdcc96e..913f60b 100644 --- a/struct_field.go +++ b/struct_field.go @@ -150,3 +150,31 @@ func (psf parseStructFieldTag) Summary(structField reflect.StructField) string { } return paramName } + +// EnumDescription .枚举值详细描述 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 22:40 2025/2/18 +func (psf parseStructFieldTag) EnumDescription(structField reflect.StructField) map[string]string { + defaultTagList := []string{define.TagNameEnumDescription} + res := map[string]string{} + for _, tag := range defaultTagList { + if tagVal, exist := structField.Tag.Lookup(tag); exist && len(tagVal) > 0 { + tagVal = strings.ReplaceAll(tagVal, "###", "`") + enumList := strings.Split(tagVal, "||") + for _, enum := range enumList { + enumArr := strings.Split(enum, ":") + if len(enumArr) < 2 { + continue + } + res[enumArr[0]] = strings.Join(enumArr[1:], ":") + } + return res + } + } + if len(res) == 0 { + return nil + } + return res +}