Compare commits
10 Commits
9e8560cccf
...
feature/fi
Author | SHA1 | Date | |
---|---|---|---|
c35ddad807 | |||
0047befc07 | |||
8387ae08e9 | |||
55ecf3c8ea | |||
dd04ad543a | |||
ec0d894d36 | |||
7f069e3c7e | |||
d70cfe3e50 | |||
e41bac4cd1 | |||
a5398ae6b9 |
@ -100,17 +100,17 @@ type ExternalDocs struct {
|
||||
//
|
||||
// Date : 12:29 2024/7/19
|
||||
type PathConfigParameter struct {
|
||||
Name string `json:"name"` // 参数名称
|
||||
In string `json:"in"` // 必选. 参数的位置,可能的值有 "query", "header", "path" 或 "cookie"。
|
||||
Description string `json:"description"` // 对此参数的简要描述,这里可以包含使用示例。CommonMark syntax可以被用来呈现富文本格式.
|
||||
Required bool `json:"required"` // 标明此参数是否是必选参数。如果 参数位置 的值是 path,那么这个参数一定是 必选 的因此这里的值必须是true。其他的则视情况而定。此字段的默认值是false。
|
||||
Deprecated bool `json:"deprecated"` // 标明一个参数是被弃用的而且应该尽快移除对它的使用。
|
||||
Schema *Schema `json:"schema,omitempty,omitempty"` // 定义适用于此参数的类型结构。 Schema 对象 | Reference 对象
|
||||
AllowEmptyValue bool `json:"allowEmptyValue"` // 设置是否允许传递空参数,这只在参数值为query时有效,默认值是false。如果同时指定了style属性且值为n/a(无法被序列化),那么此字段 allowEmptyValue应该被忽略。
|
||||
Style string `json:"style,omitempty"` // 描述根据参数值类型的不同如何序列化参数。默认值为(基于in字段的值):query 对应 form;path 对应 simple; header 对应 simple; cookie 对应 form。
|
||||
Explode bool `json:"explode,omitempty"` // 当这个值为true时,参数值类型为array或object的参数使用数组内的值或对象的键值对生成带分隔符的参数值。对于其他类型的参数,这个字段没有任何影响。当 style 是 form时,这里的默认值是 true,对于其他 style 值类型,默认值是false。
|
||||
AllowReserved bool `json:"allowReserved,omitempty"` // 决定此参数的值是否允许不使用%号编码使用定义于 RFC3986内的保留字符 :/?#[]@!$&'()*+,;=。 这个属性仅用于in的值是query时,此字段的默认值是false。
|
||||
Ref string `json:"$ref,omitempty"` // 一个允许引用规范内部的其他部分或外部规范的对象。 Reference 对象 定义于 JSON Reference 且遵循相同的结构、行为和规则。
|
||||
Name string `json:"name"` // 参数名称
|
||||
In string `json:"in"` // 必选. 参数的位置,可能的值有 "query", "header", "path" 或 "cookie"。
|
||||
Description string `json:"description"` // 对此参数的简要描述,这里可以包含使用示例。CommonMark syntax可以被用来呈现富文本格式.
|
||||
Required bool `json:"required"` // 标明此参数是否是必选参数。如果 参数位置 的值是 path,那么这个参数一定是 必选 的因此这里的值必须是true。其他的则视情况而定。此字段的默认值是false。
|
||||
Deprecated bool `json:"deprecated"` // 标明一个参数是被弃用的而且应该尽快移除对它的使用。
|
||||
Schema *Schema `json:"schema,omitempty"` // 定义适用于此参数的类型结构。 Schema 对象 | Reference 对象
|
||||
AllowEmptyValue bool `json:"allowEmptyValue"` // 设置是否允许传递空参数,这只在参数值为query时有效,默认值是false。如果同时指定了style属性且值为n/a(无法被序列化),那么此字段 allowEmptyValue应该被忽略。
|
||||
Style string `json:"style,omitempty"` // 描述根据参数值类型的不同如何序列化参数。默认值为(基于in字段的值):query 对应 form;path 对应 simple; header 对应 simple; cookie 对应 form。
|
||||
Explode bool `json:"explode,omitempty"` // 当这个值为true时,参数值类型为array或object的参数使用数组内的值或对象的键值对生成带分隔符的参数值。对于其他类型的参数,这个字段没有任何影响。当 style 是 form时,这里的默认值是 true,对于其他 style 值类型,默认值是false。
|
||||
AllowReserved bool `json:"allowReserved,omitempty"` // 决定此参数的值是否允许不使用%号编码使用定义于 RFC3986内的保留字符 :/?#[]@!$&'()*+,;=。 这个属性仅用于in的值是query时,此字段的默认值是false。
|
||||
Ref string `json:"$ref,omitempty"` // 一个允许引用规范内部的其他部分或外部规范的对象。 Reference 对象 定义于 JSON Reference 且遵循相同的结构、行为和规则。
|
||||
}
|
||||
|
||||
// Schema ...
|
||||
|
123
generate.go
123
generate.go
@ -265,13 +265,12 @@ func (g *Generate) setApiDoc(baseCfg *define.UriBaseConfig, apiDocCfg *define.Pa
|
||||
//
|
||||
// Date : 16:10 2025/2/14
|
||||
func (g *Generate) getApiDocBaseCfg(baseCfg *define.UriBaseConfig, paramType reflect.Type) *define.PathItemOperationConfig {
|
||||
defaultPkgPath := wrapper.String(strings.TrimLeft(baseCfg.Uri, "/")).SnakeCaseToCamel()
|
||||
cfg := &define.PathItemOperationConfig{
|
||||
Tags: baseCfg.TagList,
|
||||
Summary: baseCfg.Summary,
|
||||
Description: baseCfg.Description,
|
||||
ExternalDocs: nil,
|
||||
OperationID: baseCfg.Method + "-" + defaultPkgPath,
|
||||
OperationID: baseCfg.Method + "-" + baseCfg.Uri,
|
||||
Parameters: make([]*define.PathConfigParameter, 0),
|
||||
RequestBody: &define.RequestBody{
|
||||
Required: true,
|
||||
@ -331,38 +330,59 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe
|
||||
if propertyName == "-" {
|
||||
continue
|
||||
}
|
||||
realInputTypeFormat := inputType.Field(i).Type.Kind().String()
|
||||
fieldType := inputType.Field(i).Type
|
||||
if inputType.Field(i).Type.Kind() == reflect.Ptr {
|
||||
fieldType = inputType.Field(i).Type.Elem()
|
||||
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
|
||||
}
|
||||
if fieldType.Kind() == reflect.Ptr ||
|
||||
fieldType.Kind() == reflect.Struct ||
|
||||
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() {
|
||||
// 针对基础类型指针
|
||||
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 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: ParseStructField.GetParamName(inputType.Field(i)),
|
||||
Name: propertyName,
|
||||
In: consts.SwaggerParameterInQuery,
|
||||
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
||||
Required: ValidateRule.IsRequired(inputType.Field(i)),
|
||||
Deprecated: ParseStructField.Deprecated(inputType.Field(i)),
|
||||
Schema: &define.Schema{
|
||||
// Format: realInputTypeFormat,
|
||||
Ref: g.getSchemaRef(schemaNameNext),
|
||||
}, AllowEmptyValue: false,
|
||||
Style: "",
|
||||
Explode: false,
|
||||
AllowReserved: false,
|
||||
})
|
||||
} else {
|
||||
// 当做默认基础类型, 默认不会出现 *map *[]
|
||||
baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{
|
||||
Name: propertyName,
|
||||
In: consts.SwaggerParameterInQuery,
|
||||
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
||||
Required: ValidateRule.IsRequired(inputType.Field(i)),
|
||||
Deprecated: ParseStructField.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
|
||||
}
|
||||
continue
|
||||
}
|
||||
if fieldType.Kind() == reflect.Struct ||
|
||||
fieldType.Kind() == reflect.Map ||
|
||||
fieldType.Kind() == reflect.Array ||
|
||||
fieldType.Kind() == reflect.Slice {
|
||||
// TODO: 完善逻辑解析
|
||||
} else {
|
||||
baseReqCfg.Parameters = append(baseReqCfg.Parameters, &define.PathConfigParameter{
|
||||
Name: ParseStructField.GetParamName(inputType.Field(i)),
|
||||
@ -404,6 +424,10 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe
|
||||
//
|
||||
// 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, "/", "-")
|
||||
@ -454,32 +478,34 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
|
||||
}
|
||||
// 结构体
|
||||
if inputType.Kind() == reflect.Struct {
|
||||
if len(rootSchemaName) == 0 {
|
||||
|
||||
} else {
|
||||
// g.docData.Components.Schemas[schemaName].Properties[""] = schemaName
|
||||
}
|
||||
// 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 ||
|
||||
inputType.Field(i).Type.Kind() == reflect.Array ||
|
||||
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() {
|
||||
// 针对基础类型指针
|
||||
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{
|
||||
Type: g.realBaseType2SwaggerType(convertType),
|
||||
Ref: g.getSchemaRef(schemaNameNext),
|
||||
}
|
||||
} else {
|
||||
// 当做默认基础类型, 默认不会出现 *map *[]
|
||||
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)),
|
||||
}
|
||||
continue
|
||||
}
|
||||
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{
|
||||
@ -499,8 +525,6 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
|
||||
},
|
||||
Properties: map[string]*define.Property{},
|
||||
}
|
||||
} else if inputType.Field(i).Type.Kind() == reflect.Ptr {
|
||||
|
||||
} else {
|
||||
g.AddComponentsSchema(schemaName, propertyName, inputType.Field(i).Type)
|
||||
}
|
||||
@ -520,11 +544,11 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
|
||||
}
|
||||
// 指针
|
||||
if inputType.Kind() == reflect.Ptr {
|
||||
convertType := g.realBaseType2SwaggerType(inputType.String())
|
||||
if convertType == inputType.String() {
|
||||
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(),
|
||||
@ -547,6 +571,9 @@ func (g *Generate) parseSliceItem(rootSchemaName string, inputType reflect.Type)
|
||||
}
|
||||
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()
|
||||
@ -563,6 +590,7 @@ func (g *Generate) getSchemaRef(schemaName string) string {
|
||||
if "" == schemaName {
|
||||
return ""
|
||||
}
|
||||
schemaName = strings.ReplaceAll(schemaName, "*", "") // 去除指针类型 *
|
||||
return "#/components/schemas/" + strings.ReplaceAll(schemaName, "/", "-")
|
||||
}
|
||||
|
||||
@ -640,8 +668,11 @@ func (g *Generate) parseBaseUriConfig(uriPrefix string, paramType reflect.Type)
|
||||
}
|
||||
res.Uri = metaField.Tag.Get(define.TagPath)
|
||||
if len(uriPrefix) > 0 {
|
||||
res.Uri = strings.TrimRight(uriPrefix, "/") + strings.TrimLeft(res.Uri, "/")
|
||||
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), ",")
|
||||
@ -650,20 +681,22 @@ func (g *Generate) parseBaseUriConfig(uriPrefix string, paramType reflect.Type)
|
||||
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.TagContentType), ",")
|
||||
if len(res.ContentType) == 0 {
|
||||
requestContentType := strings.TrimSpace(metaField.Tag.Get(define.TagContentType))
|
||||
if len(requestContentType) == 0 {
|
||||
if wrapper.ArrayType(g.readMethodList).Has(res.Method) >= 0 {
|
||||
// get类请求
|
||||
res.ContentType = []string{consts.MimeTypeXWWWFormUrlencoded}
|
||||
requestContentType = consts.MimeTypeXWWWFormUrlencoded
|
||||
} else {
|
||||
res.ContentType = []string{consts.MimeTypeJson}
|
||||
requestContentType = consts.MimeTypeJson
|
||||
}
|
||||
}
|
||||
res.OutputContentType = strings.Split(metaField.Tag.Get(define.TagOutputContentType), ",")
|
||||
if len(res.OutputContentType) == 0 {
|
||||
res.ContentType = strings.Split(requestContentType, ",")
|
||||
responseContentType := strings.TrimSpace(metaField.Tag.Get(define.TagOutputContentType))
|
||||
if len(responseContentType) == 0 {
|
||||
// 未设置响应类型默认JSON数据
|
||||
res.OutputContentType = []string{consts.MimeTypeJson}
|
||||
responseContentType = consts.MimeTypeJson
|
||||
}
|
||||
res.OutputContentType = strings.Split(responseContentType, ",")
|
||||
res.Summary = ParseStructField.Summary(metaField)
|
||||
if res.Method == "" {
|
||||
return nil, errors.New("baseCfg.Method is empty")
|
||||
|
@ -27,8 +27,8 @@ type Meta struct {
|
||||
func Test_parser_Openapi3(t *testing.T) {
|
||||
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"`
|
||||
Name *string `json:"name" d:"zhang" desc:"用户姓名" binding:"required"`
|
||||
Age string `json:"age" d:"18" desc:"年龄" binding:"required,oneof=12 13 18 90"`
|
||||
}
|
||||
type UserDelete struct {
|
||||
Meta `json:"-" deprecated:"false" path:"/user/detail" method:"DELETE" desc:"测试接口" tag:"用户,搜索" content_type:"application/json" output_content_type:"application/json"`
|
||||
@ -51,15 +51,16 @@ func Test_parser_Openapi3(t *testing.T) {
|
||||
Age string `json:"age" d:"18" desc:"年龄" binding:"required,oneof=12 13 18 90"`
|
||||
}
|
||||
type List struct {
|
||||
Total int64 `json:"total" binding:"required"`
|
||||
UserList []User `json:"user_list"`
|
||||
Total int64 `json:"total" binding:"required"`
|
||||
UserList []*User `json:"user_list"`
|
||||
UserDetail *User `json:"user_detail" desc:"用户详情"`
|
||||
}
|
||||
var o List
|
||||
var f User
|
||||
var fd UserDelete
|
||||
var up UserPut
|
||||
var ug UserGet
|
||||
var uh UserHead
|
||||
var o *List
|
||||
var f *User
|
||||
var fd *UserDelete
|
||||
var up *UserPut
|
||||
var ug *UserGet
|
||||
var uh *UserHead
|
||||
g := NewOpenapiDoc(nil, []*define.ServerItem{
|
||||
&define.ServerItem{
|
||||
Url: "http://127.0.0.1/v1",
|
||||
@ -78,11 +79,11 @@ func Test_parser_Openapi3(t *testing.T) {
|
||||
Variables: nil,
|
||||
},
|
||||
})
|
||||
g.AddApiFromInAndOut(reflect.TypeOf(f), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut(reflect.TypeOf(fd), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut(reflect.TypeOf(up), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut(reflect.TypeOf(ug), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut(reflect.TypeOf(uh), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut("", reflect.TypeOf(f), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut("", reflect.TypeOf(fd), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut("", reflect.TypeOf(up), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut("", reflect.TypeOf(ug), reflect.TypeOf(o))
|
||||
g.AddApiFromInAndOut("", reflect.TypeOf(uh), reflect.TypeOf(o))
|
||||
byteData, _ := json.Marshal(g.docData)
|
||||
fmt.Println(string(byteData))
|
||||
}
|
||||
|
Reference in New Issue
Block a user