Compare commits
57 Commits
feature/fi
...
e02098e643
Author | SHA1 | Date | |
---|---|---|---|
e02098e643 | |||
de49c7e1d6 | |||
9a881e3bf2 | |||
66e5b00562 | |||
b919c9f485 | |||
4b9d44296d | |||
a736cff687 | |||
0da14a1492 | |||
0b18c69e6e | |||
5afe9bed09 | |||
0899ad9fe6 | |||
cef0969b3e | |||
2363b2aa83 | |||
130c302ad2 | |||
b5dbc330fc | |||
22550f9ac0 | |||
71d6db967d | |||
1538b52cf7 | |||
22d937a4b1 | |||
2fe0d11b46 | |||
6987b4829b | |||
d7cbf81799 | |||
bd7f6d2cfd | |||
f76641346f | |||
d248c1a093 | |||
9f915c779d | |||
58ba52f919 | |||
0e1ceaf613 | |||
998c1613e4 | |||
e2d0efe09e | |||
f6c3e0380d | |||
e1191b8c05 | |||
32186500f5 | |||
57dfd12aa5 | |||
339b7aca77 | |||
b8a7f0585a | |||
7aa4d3c417 | |||
93d399d845 | |||
ab8f68c799 | |||
8cd00367e4 | |||
db15e16f99 | |||
4e54b5fe33 | |||
d407826f88 | |||
848adb5b29 | |||
f0f15a4df3 | |||
1ff465b953 | |||
0fc52fe51b | |||
f6810bafbd | |||
6449c7399f | |||
670d2b5cb3 | |||
04febf36a1 | |||
4ef680d917 | |||
bd25ea3d96 | |||
15b7f1ad4e | |||
dab7b0c811 | |||
31a407e241 | |||
d23a5651af |
20
common.go
20
common.go
@ -47,24 +47,34 @@ func GetUriPathParamList(uriPath string) []*define.ParamConfig {
|
||||
// Date : 11:52 2024/12/24
|
||||
func GetDataType(docParamType string, formatType string) string {
|
||||
docParamType = strings.ToLower(docParamType)
|
||||
formatType = strings.ToLower(formatType)
|
||||
formatType = strings.ReplaceAll(strings.ToLower(formatType), "interface{}", "any") // 内置支持的类型统一使用any, 不使用 interface{}
|
||||
if len(formatType) == 0 {
|
||||
formatType = docParamType
|
||||
}
|
||||
// format type 和内置的支持数据类型一致, 直接返回对应类型
|
||||
for _, itemType := range consts.DataTypeList {
|
||||
if itemType.Value.String() == formatType {
|
||||
return itemType.Value.String()
|
||||
}
|
||||
}
|
||||
|
||||
// format type 和内置的支持数据类型不一致, 根据文档类型进行转换
|
||||
switch docParamType {
|
||||
case consts.SwaggerDataTypeInteger:
|
||||
if formatType == "int64" {
|
||||
if formatType == consts.DataTypeInt.String() {
|
||||
return consts.DataTypeInt.String()
|
||||
}
|
||||
return consts.DataTypeUint.String()
|
||||
return consts.DataTypeInt.String()
|
||||
case "string", "apikey":
|
||||
return consts.DataTypeString.String()
|
||||
case "object":
|
||||
return consts.DataTypeMapStrAny.String()
|
||||
case "boolean":
|
||||
return consts.DataTypeBool.String()
|
||||
case "number", "float", "double", "float32", "float64":
|
||||
return consts.DataTypeFloat.String()
|
||||
case "number", "float", "double", "float64":
|
||||
return consts.DataTypeFloat64.String()
|
||||
case "float32":
|
||||
return consts.DataTypeFloat32.String()
|
||||
case "array":
|
||||
if formatType == "integer" {
|
||||
return consts.DataTypeSliceInt.String()
|
||||
|
@ -119,21 +119,29 @@ 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"` // 格式化类型
|
||||
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 any `json:"example,omitempty"` // 一个用于示范此 schema实例的示例,可以是任意格式。为了表达无法用 JSON 或 YAML 格式呈现的示例,可以使用 string 类型的值,且在必要的地方需要使用字符转义。
|
||||
Description string `json:"description,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 any `json:"type,omitempty"` // 类型 (string | []string)
|
||||
Items *PropertyXOf `json:"items,omitempty"` // items 必须存在如果 type 的值是 array。
|
||||
OneOf []*PropertyXOf `json:"oneOf,omitempty"` // type 是一个对象, allOf 指向对象描述
|
||||
Ref string `json:"$ref,omitempty"` // 类型引用
|
||||
Format string `json:"format,omitempty"` // 格式化类型
|
||||
Maximum *int64 `json:"maximum,omitempty"` // 最大值
|
||||
Minimum *int64 `json:"minimum,omitempty"` // 最小值
|
||||
MinLength *int64 `json:"minLength,omitempty"` // 字符串最小长度
|
||||
MaxLength *int64 `json:"maxLength,omitempty"` // 字符串最大长度
|
||||
Default any `json:"default,omitempty"` // 默认值
|
||||
}
|
||||
|
||||
// Property 是从 JSON Schema 提取出来的,但是做了一些调整以适应 OpenAPI Specification。
|
||||
@ -142,16 +150,22 @@ type Schema struct {
|
||||
//
|
||||
// Date : 17:05 2024/7/19
|
||||
type Property struct {
|
||||
Type string `json:"type,omitempty"` // 数据类型, swagger本身的定义
|
||||
Type any `json:"type,omitempty"` // 数据类型(string | []string), 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可以被用来呈现富文本格式.
|
||||
Example any `json:"example,omitempty"` // 媒体类型的示例。示例对象应该符合此媒体类型的格式, 这里指定的example对象 object is mutually exclusive of the examples object. 而且如果引用的schema也包含示例,在这里指定的example值将会覆盖schema提供的示例。
|
||||
Maximum *int64 `json:"maximum,omitempty"` // 最大值
|
||||
Minimum *int64 `json:"minimum,omitempty"` // 最小值
|
||||
MinLength *int64 `json:"minLength,omitempty"` // 字符串最小长度
|
||||
MaxLength *int64 `json:"maxLength,omitempty"` // 字符串最大长度
|
||||
AllOf []*PropertyXOf `json:"allOf,omitempty"` // type 是一个对象, allOf 指向对象描述
|
||||
OneOf []*PropertyXOf `json:"oneOf,omitempty"` // type 是一个对象, allOf 指向对象描述
|
||||
AnyOf []*PropertyXOf `json:"anyOf,omitempty"` // type 是一个对象, allOf 指向对象描述
|
||||
Items *PropertyXOf `json:"items,omitempty"` // items 必须存在如果 type 的值是 array。
|
||||
AdditionalProperties *PropertyXOf `json:"additionalProperties,omitempty"` // additionalProperties 是一个用于描述模型中包含未在属性列表中定义的额外属性的选项。它允许接受任意的一个或多个键值对。它的作用是为了在模型定义中包含未知或动态属性。通常,在设计 API 时,我们无法预先知道 API 用户会传递什么样的额外属性,这时就可以使用 additionalProperties 功能来灵活地处理这些未知属性。
|
||||
AdditionalProperties any `json:"additionalProperties,omitempty"` // additionalProperties(PropertyXOf | bool) 是一个用于描述模型中包含未在属性列表中定义的额外属性的选项。它允许接受任意的一个或多个键值对。它的作用是为了在模型定义中包含未知或动态属性。通常,在设计 API 时,我们无法预先知道 API 用户会传递什么样的额外属性,这时就可以使用 additionalProperties 功能来灵活地处理这些未知属性。
|
||||
Properties map[string]*Property `json:"properties,omitempty"` // type = object 时, 定义对象属性
|
||||
Ref string `json:"$ref,omitempty"` // 对描述的引用
|
||||
}
|
||||
@ -162,7 +176,12 @@ 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"` // 真实类型
|
||||
Maximum *int64 `json:"maximum,omitempty"` // 最大值
|
||||
Minimum *int64 `json:"minimum,omitempty"` // 最小值
|
||||
Ref string `json:"$ref,omitempty"` // 引用的结构描述
|
||||
// Items *PropertyXOf `json:"items,omitempty"` // 数据每一项已用类型
|
||||
}
|
||||
|
||||
// SchemaDiscriminator 当一个 request bodies 或 response payloads 可以是多种 schemas 时,可以使用一个 discriminator 对象来帮助序列化、反序列化和校验
|
||||
@ -208,7 +227,7 @@ type RequestBody struct {
|
||||
// Date : 17:21 2024/7/19
|
||||
type Media struct {
|
||||
Schema *Schema `json:"schema,omitempty"` // 定义此媒体类型的结构。
|
||||
Example string `json:"example,omitempty"` // 媒体类型的示例。示例对象应该符合此媒体类型的格式, 这里指定的example对象 object is mutually exclusive of the examples object. 而且如果引用的schema也包含示例,在这里指定的example值将会覆盖schema提供的示例。
|
||||
Example map[string]any `json:"example,omitempty"` // 媒体类型的示例。示例对象应该符合此媒体类型的格式, 这里指定的example对象 object is mutually exclusive of the examples object. 而且如果引用的schema也包含示例,在这里指定的example值将会覆盖schema提供的示例。
|
||||
Examples map[string]*Example `json:"examples,omitempty"` // 媒体类型的示例,每个媒体对象的值都应该匹配它对应的媒体类型的格式。 The examples object is mutually exclusive of the example object. 而且如果引用的schema也包含示例,在这里指定的example值将会覆盖schema提供的示例。
|
||||
Encoding map[string]*Encoding `json:"encoding,omitempty"` // 属性名与编码信息的映射。每个属性名必须存在于schema属性的key中,当媒体类型等于multipart或application/x-www-form-urlencoded时,编码对象信息仅适用于requestBody。
|
||||
}
|
||||
|
56
define/parser_api.go
Normal file
56
define/parser_api.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Package parser ...
|
||||
//
|
||||
// Description : parser ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-04-11 20:21
|
||||
package define
|
||||
|
||||
// ApiConfig 解析出的接口配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:21 2025/4/11
|
||||
type ApiConfig struct {
|
||||
Tag string `json:"tag" dc:"接口所属标签"` // 接口所属标签
|
||||
Name string `json:"name" binding:"required,min=1" dc:"接口名称" err:"项目接口名称必须存在,且不能为空"` // 接口名称
|
||||
Uri string `json:"uri" binding:"required,min=1,max=512" dc:"项目接口uri" err:"项目接口路径必须存在,且长度在[1,512]之间"` // 项目接口路由
|
||||
MethodID int64 `json:"method_id" dc:"请求方法ID"` // 请求方法
|
||||
Method string `json:"method" binding:"required_without=MethodID" dc:"请求方法" err:"请求方法与请求方法ID至少存在一个"`
|
||||
ContentType string `json:"content_type" dc:"请求类型" binding:"required_without=ContentTypeID" err:"请求类型ID与请求类型至少存在一个"`
|
||||
Description string `json:"description" dc:"项目接口描述"` // 接口描述
|
||||
ParamList []*ApiParamItem `json:"param_list" dc:"项目接口参数列表"` // 参数列表
|
||||
ResultList []*ApiResultItem `json:"result_list" dc:"项目接口返回值列表"` // 返回值列表
|
||||
}
|
||||
|
||||
// ApiParamItem 接口参数配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:23 2025/4/11
|
||||
type ApiParamItem struct {
|
||||
Title string `json:"title" binding:"required,min=1" dc:"参数标题" err:"参数标题必须存在,且不能为空"` // 参数标题
|
||||
Name string `json:"name" binding:"required,min=1" dc:"参数名称" err:"参数名称必须存在,且不能为空"` // 参数名称
|
||||
Location string `json:"location" binding:"required,oneof=HEADER COOKIE BODY QUERY PATH" dc:"参数位置" err:"参数位置必须存在,且需要是一个有效合法值"` // 参数位置
|
||||
ParamType string `json:"param_type" binding:"required,validate_data_type" dc:"参数类型" err:"参数类型必须存在,且需要是一个有效的合法值"` // 参数类型
|
||||
IsRequired bool `json:"is_required" binding:"required" dc:"参数是否必传" err:"参数是否必传必须存在,且需要是一个有效合法值"` // 是否必传
|
||||
DefaultValue string `json:"default_value" dc:"默认值"` // 默认值
|
||||
ExampleValue string `json:"example_value" dc:"示例值"` // 示例值
|
||||
Description string `json:"description" dc:"参数描述"` // 参数描述
|
||||
}
|
||||
|
||||
// ApiResultItem 接口返回值配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:23 2025/4/11
|
||||
type ApiResultItem struct {
|
||||
Title string `json:"title" binding:"required,min=1,max=64" dc:"返回值标题" err:"返回值标题必须存在,且长度在[1,64]之间"` // 返回值标题
|
||||
DataPath string `json:"data_path" binding:"required,min=1,max=512" dc:"返回值路径" err:"返回值路径必须存在,且长度在[1,512]之间"` // 返回值路径
|
||||
DataLocation string `json:"data_location" binding:"required,oneof=BODY HEADER COOKIE" dc:"返回值位置" err:"返回值位置必须存在,其需要时一个有效的合法值"` // 返回值位置
|
||||
DataType string `json:"data_type" binding:"required,validate_data_type" dc:"返回值类型" err:"返回值类型必须存在,且需要是一个有效的合法值"` // 返回值类型
|
||||
DefaultValue string `json:"default_value" dc:"默认值"` // 默认值
|
||||
ExampleValue string `json:"example_value" dc:"示例值"` // 示例值
|
||||
Description string `json:"description" dc:"返回值描述"` // 返回值描述
|
||||
}
|
15
define/seagger_ui.go
Normal file
15
define/seagger_ui.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Package define ...
|
||||
//
|
||||
// Description : define ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-02-16 13:18
|
||||
package define
|
||||
|
||||
const (
|
||||
SwaggerUIThemeDefault = "swaggerUI" // 文档默认主题
|
||||
SwaggerUIThemeKnife4go = "knife4go" // knife4go 主题
|
||||
SwaggerUIThemeYDocLucky = "ydoc-lucky-ui" // YDoc Lucky UI 主题: https://github.com/NoBugBoy/LuckyUI
|
||||
SwaggerUIThemeRedocFree = "redoc-free" // redoc UI 主题, 开源免费版: https://github.com/Redocly/redoc
|
||||
)
|
@ -8,26 +8,31 @@
|
||||
package define
|
||||
|
||||
const (
|
||||
TagJson = "json"
|
||||
TagXml = "xml"
|
||||
TagYaml = "yaml"
|
||||
TagYml = "yml"
|
||||
TagForm = "form"
|
||||
TagBinding = "binding"
|
||||
TagValidate = "validate"
|
||||
TagErr = "err"
|
||||
TagMsg = "msg"
|
||||
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"
|
||||
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
|
||||
TagExample = "example" // 示例值
|
||||
TagEg = "eg" // 示例值
|
||||
TagContentType = "content_type"
|
||||
TagOutputContentType = "output_content_type"
|
||||
TagNameOmitempty = "omitempty"
|
||||
TagNameEnumDescription = "enum-desc" // 枚举值描述: enum1:enum1-desc||enum2:enum2-desc
|
||||
)
|
||||
|
414
generate.go
414
generate.go
@ -207,7 +207,7 @@ func (g *Generate) AddApiFromInAndOut(uriPrefix string, paramType reflect.Type,
|
||||
Schema: &define.Schema{
|
||||
Ref: g.getSchemaRef(paramSchemaName),
|
||||
},
|
||||
Example: "",
|
||||
Example: nil,
|
||||
Examples: nil,
|
||||
Encoding: nil,
|
||||
}
|
||||
@ -220,7 +220,7 @@ func (g *Generate) AddApiFromInAndOut(uriPrefix string, paramType reflect.Type,
|
||||
Schema: &define.Schema{
|
||||
Ref: g.getSchemaRef(resultSchemaName),
|
||||
},
|
||||
Example: "",
|
||||
Example: nil,
|
||||
Examples: nil,
|
||||
Encoding: nil,
|
||||
}
|
||||
@ -270,7 +270,7 @@ func (g *Generate) getApiDocBaseCfg(baseCfg *define.UriBaseConfig, paramType ref
|
||||
Summary: baseCfg.Summary,
|
||||
Description: baseCfg.Description,
|
||||
ExternalDocs: nil,
|
||||
OperationID: baseCfg.Method + "-" + baseCfg.Uri,
|
||||
OperationID: baseCfg.Summary + "(" + baseCfg.Method + "-" + strings.ReplaceAll(strings.TrimLeft(baseCfg.Uri, "/"), "/", "-") + ")",
|
||||
Parameters: make([]*define.PathConfigParameter, 0),
|
||||
RequestBody: &define.RequestBody{
|
||||
Required: true,
|
||||
@ -326,7 +326,12 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe
|
||||
baseReqCfg.Parameters = make([]*define.PathConfigParameter, 0)
|
||||
}
|
||||
for i := 0; i < inputType.NumField(); i++ {
|
||||
propertyName := ParseStructField.GetParamName(inputType.Field(i))
|
||||
if inputType.Field(i).Anonymous {
|
||||
// 匿名字段, 直接对齐到当前的父级
|
||||
g.ParseReadConfigParam(requestCfg, baseReqCfg, inputType.Field(i).Type)
|
||||
continue
|
||||
}
|
||||
propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i))
|
||||
if propertyName == "-" {
|
||||
continue
|
||||
}
|
||||
@ -334,11 +339,46 @@ 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 *[]
|
||||
itemParam := &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,
|
||||
}
|
||||
g.setStructFieldProperty(itemParam.Schema, inputType.Field(i))
|
||||
baseReqCfg.Parameters = append(baseReqCfg.Parameters, itemParam)
|
||||
continue
|
||||
}
|
||||
if inputType.Field(i).Type.Kind() == reflect.Interface {
|
||||
itemParam := &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{
|
||||
OneOf: g.anyTypeConfig(inputType.Field(i)).OneOf,
|
||||
Format: realInputTypeFormat,
|
||||
},
|
||||
}
|
||||
g.setStructFieldProperty(itemParam.Schema, inputType.Field(i))
|
||||
baseReqCfg.Parameters = append(baseReqCfg.Parameters, itemParam)
|
||||
continue
|
||||
}
|
||||
if inputType.Field(i).Type.Kind() == reflect.Ptr {
|
||||
// 处理指针
|
||||
if inputType.Field(i).Type.Elem().Kind() == reflect.Struct {
|
||||
@ -347,9 +387,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),
|
||||
@ -359,22 +399,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: 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()),
|
||||
Format: realInputTypeFormat,
|
||||
},
|
||||
AllowEmptyValue: false,
|
||||
Style: "",
|
||||
Explode: false,
|
||||
AllowReserved: false,
|
||||
})
|
||||
|
||||
}
|
||||
continue
|
||||
}
|
||||
@ -382,40 +407,20 @@ 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: ParseStructField.GetParamName(inputType.Field(i)),
|
||||
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
|
||||
}
|
||||
} 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,
|
||||
Ref: "",
|
||||
Format: realInputTypeFormat,
|
||||
Type: convertBaseType,
|
||||
Items: nil,
|
||||
Ref: "",
|
||||
Format: realInputTypeFormat,
|
||||
Enum: ValidateRule.Enum(inputType.Field(i)),
|
||||
XEnumDescription: ParseStructFieldTag.EnumDescription(inputType.Field(i)),
|
||||
Example: ParseStructFieldTag.GetExampleValue(inputType.Field(i)),
|
||||
},
|
||||
AllowEmptyValue: false,
|
||||
Style: "",
|
||||
@ -451,7 +456,7 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
|
||||
}
|
||||
inputNameArr := strings.Split(inputType.Name(), ".")
|
||||
inputName := inputNameArr[len(inputNameArr)-1]
|
||||
schemaName := strings.ReplaceAll(pkgPath+"."+inputName, "/", "-")
|
||||
schemaName := strings.ReplaceAll(pkgPath+"."+inputName, "/", ".")
|
||||
if schemaName == "-" {
|
||||
// 忽略的属性
|
||||
return schemaName
|
||||
@ -482,45 +487,66 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
|
||||
}
|
||||
// 数组
|
||||
if inputType.Kind() == reflect.Slice || inputType.Kind() == reflect.Array {
|
||||
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
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
return schemaName
|
||||
}
|
||||
// 结构体
|
||||
if inputType.Kind() == reflect.Struct {
|
||||
for i := 0; i < inputType.NumField(); i++ {
|
||||
propertyName := ParseStructField.GetParamName(inputType.Field(i))
|
||||
propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i))
|
||||
if propertyName == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if inputType.Field(i).Anonymous {
|
||||
// 处理匿名字段
|
||||
g.handleAnonymousField(schemaName, inputType.Field(i))
|
||||
continue
|
||||
}
|
||||
if inputType.Kind() == reflect.Interface {
|
||||
// 处理interface{}类型参数
|
||||
g.docData.Components.Schemas[schemaName].Properties[propertyName] = g.anyTypeConfig(inputType.Field(i))
|
||||
continue
|
||||
}
|
||||
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{
|
||||
// Description: ParseStructFieldTag.GetParamDesc(inputType.Field(i)),
|
||||
Ref: g.getSchemaRef(schemaNameNext),
|
||||
}
|
||||
} 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())),
|
||||
Format: inputType.Field(i).Type.String(),
|
||||
Default: ParseStructField.GetDefaultValue(inputType.Field(i)),
|
||||
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
||||
Type: convertBaseType,
|
||||
Format: inputType.Field(i).Type.String(),
|
||||
}
|
||||
}
|
||||
// 设置参数各种属性
|
||||
g.setStructFieldProperty(g.docData.Components.Schemas[schemaName], inputType.Field(i))
|
||||
continue
|
||||
}
|
||||
if inputType.Field(i).Type.Kind() == reflect.Struct ||
|
||||
@ -530,36 +556,45 @@ 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[propertyName] = &define.Property{
|
||||
Type: consts.SwaggerDataTypeObject,
|
||||
Format: inputType.Field(i).Type.String(),
|
||||
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
||||
Properties: map[string]*define.Property{},
|
||||
Type: consts.SwaggerDataTypeObject,
|
||||
Format: inputType.Field(i).Type.String(),
|
||||
Properties: map[string]*define.Property{},
|
||||
}
|
||||
} else if inputType.Field(i).Type.Kind() == reflect.Array ||
|
||||
inputType.Field(i).Type.Kind() == reflect.Slice {
|
||||
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,
|
||||
Format: inputType.Field(i).Type.String(),
|
||||
Description: ParseStructField.GetParamDesc(inputType.Field(i)),
|
||||
Items: &define.PropertyXOf{
|
||||
Ref: g.getSchemaRef(g.parseSliceItem(schemaName, inputType.Field(i).Type)),
|
||||
},
|
||||
Properties: map[string]*define.Property{},
|
||||
Type: consts.SwaggerDataTypeArray,
|
||||
Format: inputType.Field(i).Type.String(),
|
||||
Items: propertyXOf,
|
||||
}
|
||||
} else {
|
||||
g.AddComponentsSchema(schemaName, propertyName, inputType.Field(i).Type)
|
||||
}
|
||||
|
||||
} else {
|
||||
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)),
|
||||
if inputType.Field(i).Type.Kind() == reflect.Interface {
|
||||
g.docData.Components.Schemas[schemaName].Properties[propertyName] = g.anyTypeConfig(inputType.Field(i))
|
||||
g.docData.Components.Schemas[schemaName].Properties[propertyName].Example = ParseStructFieldTag.GetExampleValue(inputType.Field(i))
|
||||
} else {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置参数各种属性
|
||||
g.setStructFieldProperty(schemaName, inputType.Field(i))
|
||||
g.setStructFieldProperty(g.docData.Components.Schemas[schemaName], inputType.Field(i))
|
||||
}
|
||||
return schemaName
|
||||
}
|
||||
@ -569,7 +604,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(),
|
||||
@ -580,26 +615,79 @@ 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++ {
|
||||
itemField := handleType.Field(i)
|
||||
if itemField.Anonymous {
|
||||
// 递归处理多层嵌套匿名字段
|
||||
g.handleAnonymousField(schemaName, itemField)
|
||||
continue
|
||||
} else {
|
||||
baseConvertType, isBaseType := g.realBaseType2SwaggerType(itemField.Type.String())
|
||||
if !isBaseType {
|
||||
paramName := ParseStructFieldTag.GetParamName(itemField)
|
||||
if itemField.Type.Kind() == reflect.Interface {
|
||||
g.docData.Components.Schemas[schemaName].Properties[paramName] = g.anyTypeConfig(itemField)
|
||||
}
|
||||
g.AddComponentsSchema(schemaName, handleType.Field(i).Type.PkgPath(), handleType.Field(i).Type)
|
||||
continue
|
||||
} else {
|
||||
paramName := ParseStructFieldTag.GetParamName(itemField)
|
||||
g.docData.Components.Schemas[schemaName].Properties[paramName] = &define.Property{
|
||||
Type: baseConvertType,
|
||||
Format: itemField.Type.String(),
|
||||
Example: ParseStructFieldTag.GetExampleValue(itemField),
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseSliceItem 解析数组每一项
|
||||
//
|
||||
// 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 获取引用的类型
|
||||
@ -612,7 +700,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数据类型
|
||||
@ -620,48 +712,75 @@ 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<白茶清欢>
|
||||
//
|
||||
// Date : 16:13 2025/2/13
|
||||
func (g *Generate) setStructFieldProperty(schemaName string, structField reflect.StructField) {
|
||||
paramName := ParseStructField.GetParamName(structField)
|
||||
if ValidateRule.IsRequired(structField) {
|
||||
g.docData.Components.Schemas[schemaName].Required = append(g.docData.Components.Schemas[schemaName].Required, paramName)
|
||||
func (g *Generate) setStructFieldProperty(schema *define.Schema, structField reflect.StructField) {
|
||||
paramName := ParseStructFieldTag.GetParamName(structField)
|
||||
if paramName == "" || paramName == "-" {
|
||||
return
|
||||
}
|
||||
isRequired := ValidateRule.IsRequired(structField)
|
||||
enum := ValidateRule.Enum(structField)
|
||||
xEnumDescription := ParseStructFieldTag.EnumDescription(structField)
|
||||
example := ParseStructFieldTag.GetExampleValue(structField)
|
||||
description := ParseStructFieldTag.GetParamDesc(structField)
|
||||
maxVal := ValidateRule.Maximum(structField)
|
||||
minVal := ValidateRule.Minimum(structField)
|
||||
if nil != schema {
|
||||
if isRequired {
|
||||
schema.Required = append(schema.Required, paramName)
|
||||
}
|
||||
if nil == schema.Properties[paramName] {
|
||||
if schema.Type == consts.SwaggerDataTypeString {
|
||||
schema.MinLength = minVal
|
||||
schema.MaxLength = maxVal
|
||||
} else {
|
||||
schema.Minimum = minVal
|
||||
schema.Maximum = maxVal
|
||||
}
|
||||
schema.Enum = enum
|
||||
schema.XEnumDescription = xEnumDescription
|
||||
schema.Example = example
|
||||
schema.Description = description
|
||||
return
|
||||
}
|
||||
if schema.Properties[paramName].Type == consts.SwaggerDataTypeString {
|
||||
schema.Properties[paramName].MinLength = minVal
|
||||
schema.Properties[paramName].MaxLength = maxVal
|
||||
} else {
|
||||
schema.Properties[paramName].Minimum = minVal
|
||||
schema.Properties[paramName].Maximum = maxVal
|
||||
}
|
||||
schema.Properties[paramName].Enum = enum
|
||||
schema.Properties[paramName].XEnumDescription = xEnumDescription
|
||||
schema.Properties[paramName].Example = example
|
||||
schema.Properties[paramName].Description = description
|
||||
}
|
||||
g.docData.Components.Schemas[schemaName].Properties[ParseStructField.GetParamName(structField)].Enum = ValidateRule.Enum(structField)
|
||||
}
|
||||
|
||||
// parseBaseUriConfig 通过Meta字段解析Uri基础配置信息
|
||||
@ -718,12 +837,75 @@ 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 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")
|
||||
}
|
||||
if res.Uri == "" {
|
||||
return nil, errors.New("baseCfg.Uri is empty")
|
||||
}
|
||||
if nil == g.docData.Tags {
|
||||
g.docData.Tags = make([]*define.TagItem, 0)
|
||||
}
|
||||
// 增加tag
|
||||
for _, itemTag := range res.TagList {
|
||||
exist := false
|
||||
for _, t := range g.docData.Tags {
|
||||
if itemTag == t.Name {
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
g.docData.Tags = append(g.docData.Tags, &define.TagItem{
|
||||
Name: itemTag,
|
||||
Description: itemTag,
|
||||
})
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// anyTypeConfig 任意类型数据配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:33 2025/2/19
|
||||
func (g *Generate) anyTypeConfig(structField reflect.StructField) *define.Property {
|
||||
return &define.Property{
|
||||
Description: ParseStructFieldTag.GetParamDesc(structField),
|
||||
OneOf: []*define.PropertyXOf{
|
||||
{
|
||||
Type: consts.SwaggerDataTypeObject,
|
||||
Format: "map[string]any",
|
||||
},
|
||||
/* {
|
||||
Type: consts.SwaggerDataTypeArray,
|
||||
Format: "[]any",
|
||||
Items: &define.PropertyXOf{
|
||||
Items: nil,
|
||||
},
|
||||
},*/
|
||||
{
|
||||
Type: consts.SwaggerDataTypeInteger,
|
||||
Format: "int/uint",
|
||||
},
|
||||
{
|
||||
Type: consts.SwaggerDataTypeNumber,
|
||||
Format: "int/uint/float",
|
||||
},
|
||||
{
|
||||
Type: consts.SwaggerDataTypeString,
|
||||
Format: "string",
|
||||
},
|
||||
{
|
||||
Type: consts.SwaggerDataTypeBoolean,
|
||||
Format: "bool",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
52
go.mod
52
go.mod
@ -1,22 +1,62 @@
|
||||
module git.zhangdeman.cn/gateway/api-doc
|
||||
|
||||
go 1.22.2
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250208020330-a50062af46a1
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/go-webtools/knife4go v1.0.4
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
|
||||
github.com/bytedance/sonic v1.13.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sbabiv/xml2map v1.2.1 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/swaggo/swag v1.16.4 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.14 // indirect
|
||||
golang.org/x/arch v0.17.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
172
go.sum
172
go.sum
@ -1,29 +1,122 @@
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250208020330-a50062af46a1 h1:vv4X72I6s6XcTi0ykj2v/cgMZyseFyE2LkS4WloICCs=
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250208020330-a50062af46a1/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995 h1:LmPRAf0AsxRVFPibdpZR89ajlsz8hof2IvMMyTqiEq4=
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
|
||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 h1:gUDlQMuJ4xNfP2Abl1Msmpa3fASLWYkNlqDFF/6GN0Y=
|
||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI=
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd h1:q7GG14qgXKB4MEXQFOe7/UYebsqMfPaSX80TcPdOosI=
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd/go.mod h1:+D6uPSljwHywjVY5WSBY4TRVMj26TN5f5cFGEYMldjs=
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9 h1:/GLQaFoLb+ciHOtAS2BIyPNnf4O5ME3AC5PUaJY9kfs=
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9/go.mod h1:ABJ655C5QenQNOzf7LjCe4sSB52CXvaWLX2Zg4uwDJY=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9 h1:yF770WIDNwyiKL0nwmBGmjZvNCLXtHQL4xJyffPjTMU=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9/go.mod h1:I76wxEsWq7KnMQ84elpwTjEqq4I49QFw60tp5h7iGBs=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740 h1:zPUoylfJTbc0EcxW+NEzOTBmoeFZ2I/rLFBnEzxb4Wk=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740/go.mod h1:1ct92dbVc49pmXusA/iGfcQUJzcYmJ+cjAhgc3sDv1I=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
|
||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-webtools/knife4go v1.0.4 h1:p32SApmM0sx2/Y5p0QfeaGv5KD96R1mj2CaHdyH8jy8=
|
||||
github.com/go-webtools/knife4go v1.0.4/go.mod h1:trOlXN1tqBJ7R44sHON3exGvzCwjbsVriIHEenry3d8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/sbabiv/xml2map v1.2.1 h1:1lT7t0hhUvXZCkdxqtq4n8/ZCnwLWGq4rDuDv5XOoFE=
|
||||
github.com/sbabiv/xml2map v1.2.1/go.mod h1:2TPoAfcaM7+Sd4iriPvzyntb2mx7GY+kkQpB/GQa/eo=
|
||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
|
||||
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
||||
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
|
||||
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
@ -31,7 +124,60 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.14 h1:yOQvXCBc3Ij46LRkRoh4Yd5qK6LVOgi0bYOXfb7ifjw=
|
||||
github.com/ugorji/go/codec v1.2.14/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
|
||||
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
|
37
parser.go
37
parser.go
@ -8,34 +8,35 @@
|
||||
package api_doc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.zhangdeman.cn/gateway/api-doc/define"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"github.com/tidwall/gjson"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse 解析swagger文档
|
||||
// ParseOpenapi3 解析openapi文档
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:54 2024/12/23
|
||||
func Parse(docUrl string) (*define.DocParseResult, error) {
|
||||
// Date : 21:02 2025/2/25
|
||||
func ParseOpenapi3(docUrl string, docContent string) (*define.OpenapiDoc, error) {
|
||||
var (
|
||||
err error
|
||||
docContent []byte
|
||||
docRes define.OpenapiDoc
|
||||
err error
|
||||
docByte []byte
|
||||
)
|
||||
|
||||
if docContent, err = serialize.File.ReadFromRemote(docUrl); nil != err {
|
||||
if len(docContent) < 2 {
|
||||
if len(docUrl) == 0 {
|
||||
return nil, errors.New("doc url and doc content all empty")
|
||||
}
|
||||
if docByte, err = serialize.File.ReadFromRemote(docUrl); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
docByte = []byte(docContent)
|
||||
}
|
||||
if err = serialize.JSON.UnmarshalWithNumber(docByte, &docRes); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
swaggerVersion := gjson.GetBytes(docContent, "swagger").String()
|
||||
if "" == swaggerVersion || !strings.HasPrefix(swaggerVersion, "2.") {
|
||||
// 未指定swagger版本或swagger版本3.x
|
||||
return ParseOpenapi3(docContent)
|
||||
}
|
||||
return ParseSwagger2(docContent)
|
||||
}
|
||||
|
||||
func ParseOpenapi3(docContent []byte) (*define.DocParseResult, error) {
|
||||
return nil, nil
|
||||
return &docRes, nil
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/gateway/api-doc/define"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@ -25,10 +24,18 @@ 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"`
|
||||
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"`
|
||||
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 {
|
||||
Meta `json:"-" deprecated:"false" path:"/user/detail" method:"DELETE" desc:"测试接口" tag:"用户,搜索" content_type:"application/json" output_content_type:"application/json"`
|
||||
@ -88,8 +95,7 @@ func Test_parser_Openapi3(t *testing.T) {
|
||||
fmt.Println(string(byteData))
|
||||
}
|
||||
|
||||
func TestParseForSwagger(t *testing.T) {
|
||||
docUrl := "https://git.zhangdeman.cn/swagger.v1.json"
|
||||
res, _ := Parse(docUrl)
|
||||
serialize.JSON.ConsoleOutput(res)
|
||||
func Test_parse_Openapi3_doc(t *testing.T) {
|
||||
res, err := ParseOpenapi3("http://localhost:10990/static-server/github-openapi.json", "")
|
||||
fmt.Println(res, err)
|
||||
}
|
||||
|
29
redoc-free/index.html
Normal file
29
redoc-free/index.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{DOC_TITLE}}</title>
|
||||
<!-- needed for adaptive design -->
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700 -->
|
||||
<!-- 预留占位符, 可以通过外部替换 -->
|
||||
<link href="{{CSS_FAMILY}}" rel="stylesheet">
|
||||
|
||||
<!--
|
||||
Redoc doesn't change outer page styles
|
||||
-->
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 预留占位符, 可以通过外部替换 -->
|
||||
<!--doc.json, 相对于外部设置好的base_url-->
|
||||
<redoc spec-url='{{DOC_PATH}}'></redoc>
|
||||
<!--https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js-->
|
||||
<script src="{{REDOC_STANDALONE_JS}}"> </script>
|
||||
</body>
|
||||
</html>
|
141
struct_field.go
141
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,
|
||||
@ -34,7 +34,9 @@ func (psf parseStructField) GetParamName(structField reflect.StructField) string
|
||||
}
|
||||
for _, tag := range paramNameTagList {
|
||||
tagVal := structField.Tag.Get(tag)
|
||||
if tagVal != "" {
|
||||
tagVal = strings.TrimSuffix(strings.TrimPrefix(tagVal, define.TagNameOmitempty+","), ","+define.TagNameOmitempty)
|
||||
tagVal = strings.Trim(tagVal, ",")
|
||||
if tagVal != "" && tagVal != define.TagNameOmitempty {
|
||||
return tagVal
|
||||
}
|
||||
}
|
||||
@ -47,12 +49,12 @@ 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 {
|
||||
descTagList := []string{define.TagDesc, define.TagDescription}
|
||||
func (psf parseStructFieldTag) GetParamDesc(structField reflect.StructField) string {
|
||||
descTagList := []string{define.TagDc, define.TagDesc, define.TagDescription}
|
||||
for _, tag := range descTagList {
|
||||
tagVal := structField.Tag.Get(tag)
|
||||
if tagVal != "" {
|
||||
return tagVal
|
||||
return strings.ReplaceAll(tagVal, "###", "`")
|
||||
}
|
||||
}
|
||||
// 没有显示的设置参数描述, 则使用参数名作为参数描述
|
||||
@ -64,35 +66,38 @@ 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 {
|
||||
if val, exist := structField.Tag.Lookup(tag); exist && val != "" {
|
||||
if strings.HasPrefix(fieldType, "int") {
|
||||
i, _ := strconv.Atoi(val)
|
||||
return i
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "uint") {
|
||||
uintVal, _ := strconv.ParseUint(val, 10, 64)
|
||||
return uintVal
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "float") {
|
||||
floatVal, _ := strconv.ParseFloat(val, 64)
|
||||
return floatVal
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "string") {
|
||||
return val
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "bool") {
|
||||
if val == "true" {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
val, exist := structField.Tag.Lookup(tag)
|
||||
val = strings.TrimSpace(val)
|
||||
if !exist || len(val) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "int") {
|
||||
i, _ := strconv.Atoi(val)
|
||||
return i
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "uint") {
|
||||
uintVal, _ := strconv.ParseUint(val, 10, 64)
|
||||
return uintVal
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "float") {
|
||||
floatVal, _ := strconv.ParseFloat(val, 64)
|
||||
return floatVal
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "string") {
|
||||
return val
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "bool") {
|
||||
if val == "true" {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -102,7 +107,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 {
|
||||
@ -117,7 +122,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") {
|
||||
@ -132,7 +137,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 {
|
||||
@ -145,3 +150,71 @@ func (psf parseStructField) 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
|
||||
}
|
||||
|
||||
// GetExampleValue 示例值
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:42 2025/2/20
|
||||
func (psf parseStructFieldTag) GetExampleValue(structField reflect.StructField) any {
|
||||
descTagList := []string{define.TagEg, define.TagExample}
|
||||
fieldType := structField.Type.Kind().String()
|
||||
for _, tag := range descTagList {
|
||||
val := strings.TrimSpace(structField.Tag.Get(tag))
|
||||
if val == "" {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "int") {
|
||||
i, _ := strconv.Atoi(val)
|
||||
return i
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "uint") {
|
||||
uintVal, _ := strconv.ParseUint(val, 10, 64)
|
||||
return uintVal
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "float") {
|
||||
floatVal, _ := strconv.ParseFloat(val, 64)
|
||||
return floatVal
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "string") {
|
||||
return val
|
||||
}
|
||||
if strings.HasPrefix(fieldType, "bool") {
|
||||
if val == "true" {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
648
swagger/parser.go
Normal file
648
swagger/parser.go
Normal file
@ -0,0 +1,648 @@
|
||||
// Package swagger ...
|
||||
//
|
||||
// Description : swagger ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-04-11 20:07
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
apiDocDefine "git.zhangdeman.cn/gateway/api-doc/define"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/wrapper"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HandleOpenapiDocRes ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:00 2025/2/26
|
||||
func HandleOpenapiDocRes(docRes *apiDocDefine.OpenapiDoc, ignoreApiTable map[string]bool, dataField string) ([]*apiDocDefine.ApiConfig, error) {
|
||||
if nil == ignoreApiTable {
|
||||
ignoreApiTable = make(map[string]bool)
|
||||
}
|
||||
hod := &handleOpenapiDoc{
|
||||
docRes: docRes,
|
||||
parseRes: make([]*apiDocDefine.ApiConfig, 0),
|
||||
ignoreApiTable: ignoreApiTable,
|
||||
dataField: dataField,
|
||||
}
|
||||
if err := hod.Parse(); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
return hod.parseRes, nil
|
||||
}
|
||||
|
||||
type handleOpenapiDoc struct {
|
||||
docRes *apiDocDefine.OpenapiDoc
|
||||
parseRes []*apiDocDefine.ApiConfig
|
||||
ignoreApiTable map[string]bool
|
||||
dataField string
|
||||
}
|
||||
|
||||
func (hod *handleOpenapiDoc) Parse() error {
|
||||
// 排序, 保证输出顺序一致性
|
||||
uriList := make([]string, 0)
|
||||
for uri := range hod.docRes.Paths {
|
||||
uriList = append(uriList, uri)
|
||||
}
|
||||
sort.Strings(uriList)
|
||||
for _, uri := range uriList {
|
||||
itemPath := hod.docRes.Paths[uri]
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodGet, itemPath.Get); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodPut, itemPath.Put); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodPost, itemPath.Post); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodHead, itemPath.Head); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodPatch, itemPath.Patch); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodDelete, itemPath.Delete); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodConnect, itemPath.Connect); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodOptions, itemPath.Options); nil != err {
|
||||
return err
|
||||
}
|
||||
if err := hod.apiPathConfigToProjectConfig(uri, http.MethodTrace, itemPath.Trace); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// apiPathConfigToProjectConfig 解析请求方法
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:48 2025/2/26
|
||||
func (hod *handleOpenapiDoc) apiPathConfigToProjectConfig(uri string, method string, apiPathConfig *apiDocDefine.PathItemOperationConfig) error {
|
||||
if hod.ignoreApiTable[fmt.Sprintf("%v_%v", strings.ToUpper(method), uri)] {
|
||||
// 接口已存在
|
||||
return nil
|
||||
}
|
||||
if apiPathConfig == nil {
|
||||
return nil
|
||||
}
|
||||
requestContentTypeList := make([]string, 0)
|
||||
if nil != apiPathConfig.RequestBody {
|
||||
for contentType := range apiPathConfig.RequestBody.Content {
|
||||
requestContentTypeList = append(requestContentTypeList, contentType)
|
||||
}
|
||||
}
|
||||
if len(requestContentTypeList) == 0 {
|
||||
requestContentTypeList = append(requestContentTypeList, consts.MimeTypeXWWWFormUrlencoded)
|
||||
}
|
||||
sort.Strings(requestContentTypeList)
|
||||
|
||||
tag := "未知分组"
|
||||
if len(apiPathConfig.Tags) > 0 {
|
||||
tag = apiPathConfig.Tags[0]
|
||||
}
|
||||
importUriConfig := &apiDocDefine.ApiConfig{
|
||||
Tag: tag,
|
||||
Name: apiPathConfig.Summary,
|
||||
Uri: uri,
|
||||
Method: strings.ToUpper(method),
|
||||
ContentType: requestContentTypeList[0],
|
||||
Description: apiPathConfig.Description,
|
||||
ParamList: make([]*apiDocDefine.ApiParamItem, 0),
|
||||
ResultList: make([]*apiDocDefine.ApiResultItem, 0),
|
||||
}
|
||||
// 解析公共的 Parameters , 任意请求方法都可能有 // TODO: 数组&对象递归处理
|
||||
for _, itemParam := range apiPathConfig.Parameters {
|
||||
importUriConfig.ParamList = append(importUriConfig.ParamList, &apiDocDefine.ApiParamItem{
|
||||
Title: itemParam.Name,
|
||||
Name: itemParam.Name,
|
||||
Location: hod.openapiDocLocationFormat(itemParam.In),
|
||||
ParamType: hod.openapiDocType2GoType(fmt.Sprintf("%v", itemParam.Schema.Type), itemParam.Schema.Format),
|
||||
IsRequired: itemParam.Required,
|
||||
DefaultValue: "∂",
|
||||
ExampleValue: "",
|
||||
Description: itemParam.Description,
|
||||
})
|
||||
}
|
||||
// post 类请求 body
|
||||
if err := hod.handleRequestBody(importUriConfig, apiPathConfig.RequestBody, requestContentTypeList[0]); nil != err {
|
||||
return err
|
||||
}
|
||||
// 处理response
|
||||
if err := hod.handleResponseBody(importUriConfig, apiPathConfig.Responses); nil != err {
|
||||
return err
|
||||
}
|
||||
// 解析response
|
||||
hod.parseRes = append(hod.parseRes, importUriConfig)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleRequestBody 解析request body
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 12:17 2025/2/27
|
||||
func (hod *handleOpenapiDoc) handleRequestBody(importUriConfig *apiDocDefine.ApiConfig, requestBodyData *apiDocDefine.RequestBody, selectRequestContentType string) error {
|
||||
if nil == requestBodyData || nil == requestBodyData.Content {
|
||||
return nil
|
||||
}
|
||||
if nil == requestBodyData.Content[selectRequestContentType] {
|
||||
return fmt.Errorf("select content_type = %v is not found in requestBodyData.Content", selectRequestContentType)
|
||||
}
|
||||
requestBody := requestBodyData.Content[selectRequestContentType].Schema
|
||||
if len(requestBody.Ref) == 0 {
|
||||
return nil
|
||||
}
|
||||
requestBodyRequiredParamTable := map[string]bool{}
|
||||
for _, itemParamName := range requestBody.Required {
|
||||
requestBodyRequiredParamTable[itemParamName] = true
|
||||
}
|
||||
if len(requestBody.Ref) > 0 {
|
||||
refCfg, err := hod.getResComponentsConfig(requestBody.Ref)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
// ref 配置合并
|
||||
if nil == requestBodyData.Content[selectRequestContentType].Schema.Properties {
|
||||
requestBodyData.Content[selectRequestContentType].Schema.Properties = make(map[string]*apiDocDefine.Property)
|
||||
}
|
||||
for paramName, itemParam := range refCfg.Properties {
|
||||
requestBodyData.Content[selectRequestContentType].Schema.Properties[paramName] = itemParam
|
||||
}
|
||||
for _, itemParamName := range refCfg.Required {
|
||||
requestBodyRequiredParamTable[itemParamName] = true
|
||||
}
|
||||
}
|
||||
for requestBodyParamName, requestBodyParamConfig := range requestBody.Properties {
|
||||
// 对象、数组、引用递归解析
|
||||
if err := hod.expendObjectOrArrayRequest(importUriConfig, requestBodyRequiredParamTable, "", "", requestBodyParamName, requestBodyParamConfig); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hod *handleOpenapiDoc) expendObjectOrArrayRequest(importUriConfig *apiDocDefine.ApiConfig, requestBodyRequiredParamTable map[string]bool, rootRef string, parentPath string, currentPropertyName string, currentProperty *apiDocDefine.Property) error {
|
||||
if len(parentPath) > 0 {
|
||||
currentPropertyName = parentPath + "." + currentPropertyName
|
||||
}
|
||||
defaultValue := wrapper.AnyDataType(currentProperty.Default).ToString().Value()
|
||||
// 基础数据类型
|
||||
if len(currentProperty.Properties) == 0 && currentProperty.Type != consts.SwaggerDataTypeObject && currentProperty.Type != consts.SwaggerDataTypeArray && len(currentProperty.Ref) == 0 {
|
||||
importUriConfig.ParamList = append(importUriConfig.ParamList, &apiDocDefine.ApiParamItem{
|
||||
Title: currentPropertyName,
|
||||
Name: currentPropertyName,
|
||||
Location: consts.RequestDataLocationBody.String(),
|
||||
ParamType: hod.openapiDocType2GoType(fmt.Sprintf("%v", currentProperty.Type), currentProperty.Format),
|
||||
IsRequired: requestBodyRequiredParamTable[currentPropertyName],
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: "",
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// 处理数组(注意循环引用导致的递归死循环)
|
||||
if currentProperty.Type == consts.SwaggerDataTypeArray {
|
||||
if nil == currentProperty.Items {
|
||||
if strings.HasSuffix(parentPath, ".[]") {
|
||||
// 说明已经set过,不处理
|
||||
return nil
|
||||
}
|
||||
importUriConfig.ParamList = append(importUriConfig.ParamList, &apiDocDefine.ApiParamItem{
|
||||
Title: currentPropertyName,
|
||||
Name: currentPropertyName,
|
||||
Location: consts.RequestDataLocationBody.String(),
|
||||
ParamType: consts.DataTypeSliceAny.String(),
|
||||
IsRequired: requestBodyRequiredParamTable[currentPropertyName],
|
||||
DefaultValue: "",
|
||||
ExampleValue: "",
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// 数组类型, 如果是基础类型的数组, 直接返回, map 数组或嵌套数组层层展开
|
||||
if currentProperty.Items.Type == consts.SwaggerDataTypeObject || currentProperty.Items.Type == consts.SwaggerDataTypeArray || len(currentProperty.Items.Ref) > 0 {
|
||||
if !strings.HasSuffix(parentPath, ".[]") {
|
||||
dataType := hod.openapiDocType2GoType(fmt.Sprintf("%v", currentProperty.Type), currentProperty.Format)
|
||||
// 数组根key没设置过进行set
|
||||
importUriConfig.ParamList = append(importUriConfig.ParamList, &apiDocDefine.ApiParamItem{
|
||||
Title: currentPropertyName,
|
||||
Name: currentPropertyName,
|
||||
Location: consts.RequestDataLocationBody.String(),
|
||||
ParamType: "[]" + dataType,
|
||||
IsRequired: requestBodyRequiredParamTable[currentPropertyName],
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: "",
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
}
|
||||
|
||||
if len(currentProperty.Items.Ref) == 0 {
|
||||
return nil
|
||||
}
|
||||
// 解析ref内容
|
||||
refDefine, err := hod.getResComponentsConfig(currentProperty.Items.Ref)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(rootRef, currentProperty.Items.Ref) {
|
||||
dataType := consts.DataTypeAny.String()
|
||||
// 循环引用
|
||||
if refDefine.Type == consts.SwaggerDataTypeObject {
|
||||
dataType = consts.DataTypeSliceMapStringAny.String()
|
||||
} else if refDefine.Type == consts.SwaggerDataTypeArray {
|
||||
dataType = consts.DataTypeSliceSlice.String()
|
||||
}
|
||||
importUriConfig.ParamList = append(importUriConfig.ParamList, &apiDocDefine.ApiParamItem{
|
||||
Title: currentPropertyName,
|
||||
Name: currentPropertyName,
|
||||
Location: consts.RequestDataLocationBody.String(),
|
||||
ParamType: dataType,
|
||||
IsRequired: requestBodyRequiredParamTable[currentPropertyName],
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: "",
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// 数组子项是数组或者对象
|
||||
currentPropertyName = currentPropertyName + ".[]"
|
||||
newRequiredTable := make(map[string]bool)
|
||||
for _, item := range refDefine.Required {
|
||||
newRequiredTable[item] = true
|
||||
}
|
||||
for itemName, itemVal := range refDefine.Properties {
|
||||
if err = hod.expendObjectOrArrayRequest(importUriConfig, newRequiredTable, rootRef+currentProperty.Items.Ref, currentPropertyName, itemName, itemVal); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
// 数组子项是基础类型
|
||||
dataType := hod.openapiDocType2GoType(fmt.Sprintf("%v", currentProperty.Type), currentProperty.Format)
|
||||
if len(currentProperty.Format) == 0 {
|
||||
// 未指定format, dataType 添加 [] 前缀
|
||||
dataType = "[]" + dataType
|
||||
}
|
||||
importUriConfig.ParamList = append(importUriConfig.ParamList, &apiDocDefine.ApiParamItem{
|
||||
Title: currentPropertyName,
|
||||
Name: currentPropertyName,
|
||||
Location: consts.RequestDataLocationBody.String(),
|
||||
ParamType: dataType,
|
||||
IsRequired: requestBodyRequiredParamTable[currentPropertyName],
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: "",
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if currentProperty.Type == consts.SwaggerDataTypeObject && currentProperty.Ref == "" && len(currentProperty.Properties) == 0 {
|
||||
// 对象类型, 且未设置引用类型, 并且属性也为空, 对应any数据类型
|
||||
importUriConfig.ParamList = append(importUriConfig.ParamList, &apiDocDefine.ApiParamItem{
|
||||
Title: currentPropertyName,
|
||||
Name: currentPropertyName,
|
||||
Location: consts.RequestDataLocationBody.String(),
|
||||
ParamType: consts.DataTypeAny.String(),
|
||||
IsRequired: requestBodyRequiredParamTable[currentPropertyName],
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: "",
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// 遍历 Properties
|
||||
for fieldName, fieldVal := range currentProperty.Properties {
|
||||
importUriConfig.ParamList = append(importUriConfig.ParamList, &apiDocDefine.ApiParamItem{
|
||||
Title: fieldName,
|
||||
Name: fieldName,
|
||||
Location: consts.RequestDataLocationBody.String(),
|
||||
ParamType: hod.openapiDocType2GoType(fmt.Sprintf("%v", fieldVal.Type), fieldVal.Format),
|
||||
IsRequired: requestBodyRequiredParamTable[currentPropertyName],
|
||||
DefaultValue: defaultValue,
|
||||
Description: fieldVal.Description,
|
||||
})
|
||||
// 对象或者数组, 深度递归
|
||||
if fieldVal.Type == consts.SwaggerDataTypeObject || fieldVal.Type == consts.SwaggerDataTypeArray {
|
||||
if err := hod.expendObjectOrArrayRequest(importUriConfig, requestBodyRequiredParamTable, rootRef, currentPropertyName, fieldName, fieldVal); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(fieldVal.Ref) > 0 {
|
||||
// 引用的结构体定义, 递归解析
|
||||
if propertyResDefine, err := hod.getResComponentsConfig(fieldVal.Ref); nil != err {
|
||||
return err
|
||||
} else {
|
||||
newRequiredTable := make(map[string]bool)
|
||||
for _, item := range propertyResDefine.Required {
|
||||
newRequiredTable[item] = true
|
||||
}
|
||||
for _, itemProperty := range propertyResDefine.Properties {
|
||||
if err = hod.expendObjectOrArrayRequest(importUriConfig, newRequiredTable, rootRef+fieldVal.Ref, currentPropertyName, fieldName, itemProperty); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleResponseBody 解析响应body
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 13:44 2025/2/27
|
||||
func (hod *handleOpenapiDoc) handleResponseBody(importUriConfig *apiDocDefine.ApiConfig, requestBodyTable map[string]*apiDocDefine.Response) error {
|
||||
if nil == requestBodyTable || nil == requestBodyTable["200"] {
|
||||
return nil
|
||||
}
|
||||
if len(requestBodyTable["200"].Content) == 0 {
|
||||
// 无返回值
|
||||
return nil
|
||||
}
|
||||
contentTypeList := []string{}
|
||||
for itemContentType := range requestBodyTable["200"].Content {
|
||||
contentTypeList = append(contentTypeList, itemContentType)
|
||||
}
|
||||
if len(contentTypeList) == 0 {
|
||||
contentTypeList = append(contentTypeList, consts.MimeTypeJson)
|
||||
}
|
||||
sort.Strings(contentTypeList)
|
||||
selectResponseContentType := contentTypeList[0]
|
||||
responseBodyData := requestBodyTable["200"].Content[selectResponseContentType].Schema
|
||||
// 处理properties
|
||||
if hod.dataField == "" {
|
||||
// 未指定数据字段
|
||||
for resultName, itemResult := range responseBodyData.Properties {
|
||||
if err := hod.expendObjectOrArrayPath(importUriConfig, "", "", resultName, itemResult); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 指定数据字段
|
||||
fieldConfig := responseBodyData.Properties[hod.dataField]
|
||||
if nil == fieldConfig {
|
||||
return fmt.Errorf("data_field=%v 在相应body中不存在, uri=%v", hod.dataField, importUriConfig.Uri)
|
||||
}
|
||||
for fieldName, fieldVal := range fieldConfig.Properties {
|
||||
// 引用的结构体定义, 递归解析
|
||||
if err := hod.expendObjectOrArrayPath(importUriConfig, "", "", fieldName, fieldVal); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(responseBodyData.Ref) == 0 {
|
||||
return nil
|
||||
}
|
||||
refCfg, err := hod.getResComponentsConfig(responseBodyData.Ref)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
// ref 配置合并
|
||||
for resultName, itemResult := range refCfg.Properties {
|
||||
if err = hod.expendObjectOrArrayPath(importUriConfig, responseBodyData.Ref, "", resultName, itemResult); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// expendObjectOrArrayPath 展开ref对象路径
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:28 2025/4/8
|
||||
func (hod *handleOpenapiDoc) expendObjectOrArrayPath(importUriConfig *apiDocDefine.ApiConfig, rootRef string, parentPath string, currentPropertyName string, currentProperty *apiDocDefine.Property) error {
|
||||
if len(parentPath) > 0 {
|
||||
currentPropertyName = parentPath + "." + currentPropertyName
|
||||
}
|
||||
defaultValue := wrapper.AnyDataType(currentProperty.Default).ToString().Value()
|
||||
// 基础数据类型
|
||||
if len(currentProperty.Properties) == 0 && currentProperty.Type != consts.SwaggerDataTypeObject && currentProperty.Type != consts.SwaggerDataTypeArray && len(currentProperty.Ref) == 0 {
|
||||
importUriConfig.ResultList = append(importUriConfig.ResultList, &apiDocDefine.ApiResultItem{
|
||||
Title: currentPropertyName,
|
||||
DataPath: currentPropertyName,
|
||||
DataLocation: consts.RequestDataLocationBody.String(),
|
||||
DataType: hod.openapiDocType2GoType(fmt.Sprintf("%v", currentProperty.Type), currentProperty.Format),
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: defaultValue,
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// 处理数组(注意循环引用导致的递归死循环)
|
||||
if currentProperty.Type == consts.SwaggerDataTypeArray {
|
||||
if nil == currentProperty.Items {
|
||||
if strings.HasSuffix(parentPath, ".[]") {
|
||||
// 说明已经set过,不处理
|
||||
return nil
|
||||
}
|
||||
importUriConfig.ResultList = append(importUriConfig.ResultList, &apiDocDefine.ApiResultItem{
|
||||
Title: currentPropertyName,
|
||||
DataPath: currentPropertyName,
|
||||
DataLocation: consts.ResponseDataLocationBody.String(),
|
||||
DataType: consts.DataTypeSliceAny.String(),
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: defaultValue,
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// 数组类型, 如果是基础类型的数组, 直接返回, map 数组或嵌套数组层层展开
|
||||
if currentProperty.Items.Type == consts.SwaggerDataTypeObject || currentProperty.Items.Type == consts.SwaggerDataTypeArray || len(currentProperty.Items.Ref) > 0 {
|
||||
if !strings.HasSuffix(parentPath, ".[]") {
|
||||
// 数组根key没设置过进行set
|
||||
importUriConfig.ResultList = append(importUriConfig.ResultList, &apiDocDefine.ApiResultItem{
|
||||
Title: currentPropertyName,
|
||||
DataPath: currentPropertyName,
|
||||
DataLocation: consts.RequestDataLocationBody.String(),
|
||||
DataType: consts.DataTypeSliceAny.String(),
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: defaultValue,
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
}
|
||||
|
||||
if len(currentProperty.Items.Ref) == 0 {
|
||||
return nil
|
||||
}
|
||||
// 解析ref内容
|
||||
refDefine, err := hod.getResComponentsConfig(currentProperty.Items.Ref)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(rootRef, currentProperty.Items.Ref) {
|
||||
dataType := consts.DataTypeAny.String()
|
||||
// 循环引用
|
||||
if refDefine.Type == consts.SwaggerDataTypeObject {
|
||||
dataType = consts.DataTypeSliceMapStringAny.String()
|
||||
} else if refDefine.Type == consts.SwaggerDataTypeArray {
|
||||
dataType = consts.DataTypeSliceSlice.String()
|
||||
}
|
||||
importUriConfig.ResultList = append(importUriConfig.ResultList, &apiDocDefine.ApiResultItem{
|
||||
Title: currentPropertyName,
|
||||
DataPath: currentPropertyName,
|
||||
DataLocation: consts.RequestDataLocationBody.String(),
|
||||
DataType: dataType,
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: defaultValue,
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// 数组子项是数组或者对象
|
||||
currentPropertyName = currentPropertyName + ".[]"
|
||||
|
||||
for itemName, itemVal := range refDefine.Properties {
|
||||
if err = hod.expendObjectOrArrayPath(importUriConfig, rootRef+currentProperty.Items.Ref, currentPropertyName, itemName, itemVal); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
// 数组子项是基础类型
|
||||
dataType := hod.openapiDocType2GoType(fmt.Sprintf("%v", currentProperty.Type), currentProperty.Format)
|
||||
if len(currentProperty.Format) == 0 {
|
||||
// 未指定format, dataType 添加 [] 前缀
|
||||
dataType = "[]" + dataType
|
||||
}
|
||||
importUriConfig.ResultList = append(importUriConfig.ResultList, &apiDocDefine.ApiResultItem{
|
||||
Title: currentPropertyName,
|
||||
DataPath: currentPropertyName,
|
||||
DataLocation: consts.RequestDataLocationBody.String(),
|
||||
DataType: dataType,
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: defaultValue,
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if currentProperty.Type == consts.SwaggerDataTypeObject && currentProperty.Ref == "" && len(currentProperty.Properties) == 0 {
|
||||
// 对象类型, 且未设置引用类型, 并且属性也为空, 对应any数据类型
|
||||
importUriConfig.ResultList = append(importUriConfig.ResultList, &apiDocDefine.ApiResultItem{
|
||||
Title: currentPropertyName,
|
||||
DataPath: currentPropertyName,
|
||||
DataLocation: consts.RequestDataLocationBody.String(),
|
||||
DataType: consts.DataTypeAny.String(),
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: defaultValue,
|
||||
Description: currentProperty.Description,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
// 遍历 Properties
|
||||
for fieldName, fieldVal := range currentProperty.Properties {
|
||||
importUriConfig.ResultList = append(importUriConfig.ResultList, &apiDocDefine.ApiResultItem{
|
||||
Title: fieldName,
|
||||
DataPath: fieldName,
|
||||
DataLocation: consts.RequestDataLocationBody.String(),
|
||||
DataType: hod.openapiDocType2GoType(fmt.Sprintf("%v", fieldVal.Type), fieldVal.Format),
|
||||
Description: fieldVal.Description,
|
||||
DefaultValue: defaultValue,
|
||||
ExampleValue: defaultValue,
|
||||
})
|
||||
// 对象或者数组, 深度递归
|
||||
if fieldVal.Type == consts.SwaggerDataTypeObject || fieldVal.Type == consts.SwaggerDataTypeArray {
|
||||
if err := hod.expendObjectOrArrayPath(importUriConfig, rootRef, currentPropertyName, fieldName, fieldVal); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(fieldVal.Ref) > 0 {
|
||||
// 引用的结构体定义, 递归解析
|
||||
if propertyResDefine, err := hod.getResComponentsConfig(fieldVal.Ref); nil != err {
|
||||
return err
|
||||
} else {
|
||||
for _, itemProperty := range propertyResDefine.Properties {
|
||||
if err = hod.expendObjectOrArrayPath(importUriConfig, rootRef+fieldVal.Ref, currentPropertyName, fieldName, itemProperty); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getResComponentsConfig 获取ref指向components的定义
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 11:17 2025/2/27
|
||||
func (hod *handleOpenapiDoc) getResComponentsConfig(ref string) (*apiDocDefine.Schema, error) {
|
||||
refKey := strings.TrimPrefix(ref, "#/components/schemas/")
|
||||
if _, exist := hod.docRes.Components.Schemas[refKey]; exist {
|
||||
return hod.docRes.Components.Schemas[refKey], nil
|
||||
}
|
||||
// apifox 导出的文档, ref 部分, 空格被转义, 但是 components 定义中空格未被转义
|
||||
refKey = strings.ReplaceAll(refKey, "%20", " ")
|
||||
if _, exist := hod.docRes.Components.Schemas[refKey]; exist {
|
||||
return hod.docRes.Components.Schemas[refKey], nil
|
||||
}
|
||||
return nil, errors.New("components not found : " + refKey)
|
||||
}
|
||||
|
||||
// openapiDocLocationFormat 文档数据位置转为网关的数据位置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:06 2025/2/26
|
||||
func (hod *handleOpenapiDoc) openapiDocLocationFormat(openapiDocLocation string) string {
|
||||
openapiDocLocation = strings.ToUpper(openapiDocLocation)
|
||||
for _, itemLocation := range consts.RequestDataLocationList {
|
||||
if strings.ToUpper(itemLocation.Value.String()) == openapiDocLocation {
|
||||
return itemLocation.Value.String()
|
||||
}
|
||||
}
|
||||
// 没有明确配置参数位置:query/header/cookie/body等, 会自动按照请求尝试获取
|
||||
return consts.RequestDataLocationAny.String()
|
||||
}
|
||||
|
||||
// openapiDocType2GoType 文档数据类型转为网关数据类型
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:07 2025/2/26
|
||||
func (hod *handleOpenapiDoc) openapiDocType2GoType(openapiDocType string, formatType string) string {
|
||||
openapiDocType = strings.ToLower(openapiDocType)
|
||||
if formatType == "" {
|
||||
formatType = openapiDocType
|
||||
}
|
||||
formatType = strings.ReplaceAll(strings.ReplaceAll(formatType, " ", ""), "interface{}", "any")
|
||||
for _, itemType := range consts.DataTypeList {
|
||||
if itemType.Value.String() == formatType {
|
||||
return itemType.Value.String()
|
||||
}
|
||||
}
|
||||
switch openapiDocType {
|
||||
case consts.SwaggerDataTypeString:
|
||||
return consts.DataTypeString.String()
|
||||
case consts.SwaggerDataTypeInteger:
|
||||
return consts.DataTypeInt.String()
|
||||
case consts.SwaggerDataTypeNumber:
|
||||
return consts.DataTypeFloat64.String()
|
||||
case consts.SwaggerDataTypeArray:
|
||||
return consts.DataTypeSliceAny.String()
|
||||
case consts.SwaggerDataTypeObject:
|
||||
return consts.DataTypeMapStrAny.String()
|
||||
case consts.SwaggerDataTypeBoolean:
|
||||
return consts.DataTypeBool.String()
|
||||
}
|
||||
// 兜底数据类型any
|
||||
return consts.DataTypeAny.String()
|
||||
}
|
195
swagger_ui.go
Normal file
195
swagger_ui.go
Normal file
@ -0,0 +1,195 @@
|
||||
// Package api_doc ...
|
||||
//
|
||||
// Description : api_doc ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-02-16 13:19
|
||||
package api_doc
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/gateway/api-doc/define"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"github.com/gin-gonic/gin"
|
||||
knife4goFiles "github.com/go-webtools/knife4go"
|
||||
knife4goGin "github.com/go-webtools/knife4go/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed ydoc-lucky-ui/*
|
||||
var ydocUIFiles embed.FS
|
||||
|
||||
//go:embed redoc-free/index.html
|
||||
var redocFreeIndexContent string
|
||||
|
||||
// NewSwaggerUI ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:21 2025/2/15
|
||||
func NewSwaggerUI(info *define.Info, servers []*define.ServerItem, uiTheme string) *SwaggerUI {
|
||||
return &SwaggerUI{
|
||||
docInstance: NewOpenapiDoc(info, servers),
|
||||
uiTheme: uiTheme,
|
||||
}
|
||||
}
|
||||
|
||||
type SwaggerUI struct {
|
||||
docInstance *Generate // 文档实例
|
||||
uiTheme string // 文档主题, swaggerUI / knife4go, 默认 knife4go
|
||||
router *gin.Engine
|
||||
baseUri string
|
||||
}
|
||||
|
||||
// DocInstance 文档实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 13:23 2025/2/16
|
||||
func (su *SwaggerUI) DocInstance() *Generate {
|
||||
return su.docInstance
|
||||
}
|
||||
|
||||
// RegisterHandler ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:00 2025/2/16
|
||||
func (su *SwaggerUI) RegisterHandler(router *gin.Engine, baseUri string) {
|
||||
su.router = router
|
||||
baseUri = strings.TrimRight(baseUri, "/")
|
||||
if len(baseUri) == 0 {
|
||||
baseUri = "/docs/swagger"
|
||||
}
|
||||
su.baseUri = baseUri
|
||||
router.GET(baseUri+"/*any", func(ctx *gin.Context) {
|
||||
if ctx.Request.RequestURI == baseUri+"/doc.json" {
|
||||
// 默认swagger, 通过此接口读取文档数据
|
||||
ctx.JSON(http.StatusOK, su.docInstance.Doc())
|
||||
ctx.Abort()
|
||||
}
|
||||
if ctx.Request.RequestURI == "/doc/swagger/openapi.json" {
|
||||
// knife4go 文档通过此接口读取文档列表
|
||||
ctx.JSON(http.StatusOK, []map[string]any{
|
||||
{
|
||||
"name": "服务文档",
|
||||
"url": "doc.json",
|
||||
"swaggerVersion": consts.SwaggerDocVersion3,
|
||||
},
|
||||
})
|
||||
ctx.Abort()
|
||||
}
|
||||
}, su.Handler())
|
||||
router.GET("/swagger-resources", func(ctx *gin.Context) { // lucky UI获取分组信息
|
||||
ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*") // 允许访问所有域
|
||||
ctx.JSON(http.StatusOK, []map[string]any{
|
||||
{
|
||||
"name": "服务文档",
|
||||
"url": baseUri + "/doc.json",
|
||||
"swaggerVersion": consts.SwaggerDocVersion3,
|
||||
},
|
||||
})
|
||||
// ctx.JSON(http.StatusOK, swaggerInstance.docInstance.Data())
|
||||
})
|
||||
}
|
||||
|
||||
// Handler 访问文档的接口处理
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:34 2025/2/15
|
||||
func (su *SwaggerUI) Handler() func(ctx *gin.Context) {
|
||||
switch su.uiTheme {
|
||||
case define.SwaggerUIThemeKnife4go:
|
||||
return su.HandleKnife4goUI()
|
||||
case define.SwaggerUIThemeYDocLucky:
|
||||
// YDoc-Lucky-UI 主题处理
|
||||
return su.HandleLuckyUI()
|
||||
case define.SwaggerUIThemeDefault:
|
||||
return su.HandleSwaggerUI()
|
||||
case define.SwaggerUIThemeRedocFree:
|
||||
// redoc免费版
|
||||
return su.HandleRedocFreeUI()
|
||||
default:
|
||||
return su.HandleSwaggerUI()
|
||||
}
|
||||
}
|
||||
|
||||
// HandleLuckyUI ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:04 2025/2/16
|
||||
func (su *SwaggerUI) HandleLuckyUI() func(ctx *gin.Context) {
|
||||
// su.router.StaticFS(su.baseUri+"/assets", http.FS(ydocUIFiles))
|
||||
return func(ctx *gin.Context) {
|
||||
fileRealPath := strings.TrimPrefix(ctx.Request.RequestURI, su.baseUri)
|
||||
byteData, _ := ydocUIFiles.ReadFile(filepath.Join(define.SwaggerUIThemeYDocLucky, fileRealPath))
|
||||
if strings.HasSuffix(ctx.Request.RequestURI, "html") {
|
||||
byteData = []byte(strings.ReplaceAll(string(byteData), "{{BASE_URI}}", su.baseUri))
|
||||
}
|
||||
uriArr := strings.Split(ctx.Request.RequestURI, ".")
|
||||
contentType := "text/" + uriArr[len(uriArr)-1]
|
||||
if strings.HasSuffix(ctx.Request.RequestURI, "png") {
|
||||
contentType = "image/png"
|
||||
} else if strings.HasSuffix(ctx.Request.RequestURI, "js") {
|
||||
contentType = "application/javascript"
|
||||
}
|
||||
ctx.Header("Content-Type", contentType)
|
||||
ctx.String(http.StatusOK, string(byteData))
|
||||
ctx.Abort()
|
||||
}
|
||||
}
|
||||
|
||||
// HandleKnife4goUI ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:38 2025/2/15
|
||||
func (su *SwaggerUI) HandleKnife4goUI() func(ctx *gin.Context) {
|
||||
resetOption := func(cfg *knife4goGin.Config) {
|
||||
if nil == cfg {
|
||||
return
|
||||
}
|
||||
cfg.Title = "服务接口文档"
|
||||
}
|
||||
return knife4goGin.WrapHandler(knife4goFiles.Handler, resetOption)
|
||||
}
|
||||
|
||||
// HandleSwaggerUI ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:41 2025/2/15
|
||||
func (su *SwaggerUI) HandleSwaggerUI() func(ctx *gin.Context) {
|
||||
return ginSwagger.WrapHandler(swaggerFiles.Handler)
|
||||
}
|
||||
|
||||
// HandleRedocFreeUI 处理redoc_free主题
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:40 2025/2/18
|
||||
func (su *SwaggerUI) HandleRedocFreeUI() func(ctx *gin.Context) {
|
||||
return func(ctx *gin.Context) {
|
||||
// TODO : 这部分数据支持外部传参替换
|
||||
replaceTable := map[string]string{
|
||||
"{{DOC_TITLE}}": fmt.Sprintf("【%v】%v", su.docInstance.Doc().Info.Version, su.docInstance.Doc().Info.Title),
|
||||
"{{CSS_FAMILY}}": "https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700",
|
||||
"{{DOC_PATH}}": "doc.json",
|
||||
"{{REDOC_STANDALONE_JS}}": "https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js",
|
||||
}
|
||||
for k, v := range replaceTable {
|
||||
redocFreeIndexContent = strings.ReplaceAll(redocFreeIndexContent, k, v)
|
||||
}
|
||||
ctx.Header("Content-Type", "text/html; charset=utf-8")
|
||||
ctx.String(http.StatusOK, redocFreeIndexContent)
|
||||
ctx.Abort()
|
||||
}
|
||||
}
|
42
util/tool.go
42
util/tool.go
@ -50,15 +50,41 @@ func GetParameterDefaultLocation(requestMethod string) string {
|
||||
//
|
||||
// Date : 12:23 2024/4/22
|
||||
func GetSwaggerType(inputType string) string {
|
||||
convertTable := map[string]string{
|
||||
consts.DataTypeString.String(): "string",
|
||||
consts.DataTypeInt.String(): "integer",
|
||||
consts.DataTypeUint.String(): "integer",
|
||||
consts.DataTypeFloat.String(): "number",
|
||||
consts.DataTypeBool.String(): "boolean",
|
||||
for _, itemType := range consts.DataTypeBaseInt {
|
||||
if itemType.String() == inputType {
|
||||
return consts.SwaggerDataTypeInteger
|
||||
}
|
||||
}
|
||||
if _, exist := convertTable[inputType]; exist {
|
||||
return convertTable[inputType]
|
||||
for _, itemType := range consts.DataTypeBaseUint {
|
||||
if itemType.String() == inputType {
|
||||
return consts.SwaggerDataTypeInteger
|
||||
}
|
||||
}
|
||||
for _, itemType := range consts.DataTypeBaseFloat {
|
||||
if itemType.String() == inputType {
|
||||
return consts.SwaggerDataTypeDouble
|
||||
}
|
||||
}
|
||||
for _, itemType := range consts.DataTypeBaseString {
|
||||
if itemType.String() == inputType {
|
||||
return consts.SwaggerDataTypeString
|
||||
}
|
||||
}
|
||||
for _, itemType := range consts.DataTypeBaseString {
|
||||
if itemType.String() == inputType {
|
||||
return consts.SwaggerDataTypeString
|
||||
}
|
||||
}
|
||||
for _, itemType := range consts.DataTypeBaseBool {
|
||||
if itemType.String() == inputType {
|
||||
return consts.SwaggerDataTypeBoolean
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(inputType, "[]") {
|
||||
return consts.SwaggerDataTypeArray
|
||||
}
|
||||
if strings.HasPrefix(inputType, "map") {
|
||||
return consts.SwaggerDataTypeObject
|
||||
}
|
||||
return inputType
|
||||
}
|
||||
|
@ -78,6 +78,84 @@ func (r validateRule) Enum(structField reflect.StructField) []any {
|
||||
return anySlice
|
||||
}
|
||||
|
||||
// Minimum 最小值
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:51 2025/2/18
|
||||
func (r validateRule) Minimum(structField reflect.StructField) *int64 {
|
||||
ruleTable := r.getValidateRuleTable(structField)
|
||||
var (
|
||||
err error
|
||||
minVal, gteVal int64
|
||||
minStr, gteStr string
|
||||
minStrExist, gteStrExist bool
|
||||
)
|
||||
if minStr, minStrExist = ruleTable[consts.ValidatorRuleCommonMin.String()]; minStrExist && len(minStr) > 0 {
|
||||
if minVal, err = strconv.ParseInt(minStr, 10, 64); nil != err {
|
||||
panic("validate rule min val = " + minStr + " : " + err.Error())
|
||||
}
|
||||
}
|
||||
if gteStr, gteStrExist = ruleTable[consts.ValidateRuleGte.String()]; gteStrExist && len(gteStr) > 0 {
|
||||
if gteVal, err = strconv.ParseInt(gteStr, 10, 64); nil != err {
|
||||
panic("validate rule gte val = " + gteStr + " : " + err.Error())
|
||||
}
|
||||
}
|
||||
if !minStrExist && !gteStrExist {
|
||||
// 未配置
|
||||
return nil
|
||||
}
|
||||
if len(minStr) > 0 {
|
||||
if len(gteStr) > 0 {
|
||||
if gteVal > minVal {
|
||||
return &minVal
|
||||
}
|
||||
return >eVal
|
||||
}
|
||||
return &minVal
|
||||
}
|
||||
return >eVal
|
||||
}
|
||||
|
||||
// Maximum 最大值
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 22:00 2025/2/18
|
||||
func (r validateRule) Maximum(structField reflect.StructField) *int64 {
|
||||
ruleTable := r.getValidateRuleTable(structField)
|
||||
var (
|
||||
err error
|
||||
maxVal, lteVal int64
|
||||
maxStr, lteStr string
|
||||
maxStrExist, gleStrExist bool
|
||||
)
|
||||
if maxStr, maxStrExist = ruleTable[consts.ValidatorRuleCommonMax.String()]; maxStrExist && len(maxStr) > 0 {
|
||||
if maxVal, err = strconv.ParseInt(maxStr, 10, 64); nil != err {
|
||||
panic("validate rule min val = " + maxStr + " : " + err.Error())
|
||||
}
|
||||
}
|
||||
if lteStr, gleStrExist = ruleTable[consts.ValidateRuleLte.String()]; gleStrExist && len(lteStr) > 0 {
|
||||
if lteVal, err = strconv.ParseInt(lteStr, 10, 64); nil != err {
|
||||
panic("validate rule gte val = " + lteStr + " : " + err.Error())
|
||||
}
|
||||
}
|
||||
if !maxStrExist && !gleStrExist {
|
||||
// 未配置
|
||||
return nil
|
||||
}
|
||||
if len(maxStr) > 0 {
|
||||
if len(lteStr) > 0 {
|
||||
if lteVal > maxVal {
|
||||
return <eVal
|
||||
}
|
||||
return &maxVal
|
||||
}
|
||||
return &maxVal
|
||||
}
|
||||
return <eVal
|
||||
}
|
||||
|
||||
// getValidateRuleTable 解析验证规则表
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
@ -85,7 +163,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
|
||||
}
|
||||
|
1
ydoc-lucky-ui/assets/Api.525b760d.css
Normal file
1
ydoc-lucky-ui/assets/Api.525b760d.css
Normal file
@ -0,0 +1 @@
|
||||
#titleDoc[data-v-bf5b8710]{font-size:21px}[data-v-bf5b8710] .too-old td{color:#ff231e!important}
|
1
ydoc-lucky-ui/assets/Api.73b14880.js
Normal file
1
ydoc-lucky-ui/assets/Api.73b14880.js
Normal file
File diff suppressed because one or more lines are too long
1
ydoc-lucky-ui/assets/FirstPage.1d240e81.js
Normal file
1
ydoc-lucky-ui/assets/FirstPage.1d240e81.js
Normal file
@ -0,0 +1 @@
|
||||
import{i as e,e as t,f as a,g as l,j as u,w as s,a3 as d,a4 as o,z as f,A as n,a5 as i,O as r}from"./vendor.08310185.js";const _={id:"typeCount"},p={id:"typeCount"},c={id:"typeCount"},b={id:"typeCount"},h={setup(h){let v=e("g"),y=v.value.data,g=v.value.resource,E=y.paths,k=Object.keys(E),j=0,m=0,x=0,C=0;return k.forEach((e=>{let t=E[e];Object.keys(t).forEach((e=>{switch(e){case"get":j+=1;break;case"post":m+=1;break;case"put":x+=1;break;case"delete":C+=1}}))})),(e,h)=>(t(),a("div",null,[l(u(d),{title:"基础信息"},{default:s((()=>[l(u(o),{"x-gap":12,"y-gap":20,cols:2},{default:s((()=>[l(u(i),null,{default:s((()=>[l(u(d),{title:"标题:",hoverable:""},{default:s((()=>[f(n(u(y).info.title),1)])),_:1})])),_:1}),l(u(i),null,{default:s((()=>[l(u(d),{title:"Host:",hoverable:""},{default:s((()=>[f(n(u(y).info.contact.url),1)])),_:1})])),_:1}),l(u(i),null,{default:s((()=>[l(u(d),{title:"简介:",hoverable:""},{default:s((()=>[f(n(u(y).info.description),1)])),_:1})])),_:1}),l(u(i),null,{default:s((()=>[l(u(d),{title:"base path:",hoverable:""},{default:s((()=>[f(n(u(y).basePath),1)])),_:1})])),_:1}),l(u(i),null,{default:s((()=>[l(u(d),{title:"作者:",hoverable:""},{default:s((()=>[f(n(u(y).info.contact.name),1)])),_:1})])),_:1}),l(u(i),null,{default:s((()=>[l(u(d),{title:"分组名称:",hoverable:""},{default:s((()=>[f(n(u(g)[0].url),1)])),_:1})])),_:1})])),_:1})])),_:1}),l(u(d),{title:"Api类型统计",style:{"margin-top":"30px"}},{default:s((()=>[l(u(o),{"x-gap":50,"y-gap":10,cols:4},{default:s((()=>[l(u(i),null,{default:s((()=>[l(u(d),{title:"POST",hoverable:""},{default:s((()=>[r("span",_,n(u(m)),1)])),_:1})])),_:1}),l(u(i),null,{default:s((()=>[l(u(d),{title:"GET",hoverable:""},{default:s((()=>[r("span",p,n(u(j)),1)])),_:1})])),_:1}),l(u(i),null,{default:s((()=>[l(u(d),{title:"PUT",hoverable:""},{default:s((()=>[r("span",c,n(u(x)),1)])),_:1})])),_:1}),l(u(i),null,{default:s((()=>[l(u(d),{title:"DELETE",hoverable:""},{default:s((()=>[r("span",b,n(u(C)),1)])),_:1})])),_:1})])),_:1})])),_:1})]))}};export{h as default};
|
1
ydoc-lucky-ui/assets/FirstPage.48bcf13f.css
Normal file
1
ydoc-lucky-ui/assets/FirstPage.48bcf13f.css
Normal file
@ -0,0 +1 @@
|
||||
#typeCount{font-size:24px}
|
1
ydoc-lucky-ui/assets/Manager.02a611ee.js
Normal file
1
ydoc-lucky-ui/assets/Manager.02a611ee.js
Normal file
@ -0,0 +1 @@
|
||||
import{r as e,u as a,o as t,e as l,y as s,w as r,j as i,T as o,a3 as u,f as n,g as d,ao as c,q as p,ap as g,t as y,v,x as f,aq as m,W as k,z as S,b as h,ar as _,as as x,O,A as I,aa as J}from"./vendor.08310185.js";const N={key:0},b=S("点我清除全部用例"),q=S(" 你确定要全部清理吗,我会把LocalStorage全部干掉哦。 "),z={style:{"font-size":"18px"}},C={key:1},E={setup(S){let E=e([]),j=e([{title:"名称",key:"title"},{title:"状态",key:"status"},{title:"创建时间",key:"time"},{title:"请求参数",key:"param",render:e=>(e.param.forEach((e=>{delete e.in,delete e.type,delete e.required,delete e.active})),JSON.stringify(e.param))},{title:"请求体",key:"body"},{title:"操作",key:"action",render:e=>h(y,{type:"primary",onClick:()=>W(e)},{default:()=>"删除"})}]),P=e(!0);a();let W=e=>{let a=localStorage.getItem(e.group),t=JSON.parse(a);for(var l=0;l<t.length;l++)if(t[l].title==e.title){1==t.length?localStorage.removeItem(e.group):(t.splice(l,1),localStorage.setItem(e.group,JSON.stringify(t)));break}A()},w=()=>{localStorage.clear(),A()},A=()=>{P.vlaue=!1,E.value=[];for(var e=0;e<localStorage.length;e++){var a=localStorage.key(e);if(a.startsWith("lucky")){let e={},t=JSON.parse(localStorage.getItem(a));t.forEach((e=>{e.group=a})),e.key=a.split("_")[1],e.value=t,E.value.push(e)}}P.vlaue=!0};return t((()=>{A()})),(e,a)=>(l(),s(i(o),{vertical:""},{default:r((()=>[i(P)?(l(),s(i(u),{key:0,title:"请求用例管理"},{default:r((()=>[i(E).length>0?(l(),n("div",N,[d(i(o),{vertical:"",size:15},{default:r((()=>[d(i(c),{"positive-text":"好的,没关系.","negative-text":"Emmm,算了吧.",onPositiveClick:i(w)},{icon:r((()=>[d(i(p),null,{default:r((()=>[d(i(g))])),_:1})])),trigger:r((()=>[d(i(y),{dashed:"",type:"primary"},{default:r((()=>[b])),_:1})])),default:r((()=>[q])),_:1},8,["onPositiveClick"]),(l(!0),n(v,null,f(i(E),((e,a)=>(l(),n("div",null,[d(i(_),null,{default:r((()=>[d(i(x),{name:"index"},{header:r((()=>[O("span",z,I(e.key),1)])),default:r((()=>[d(i(J),{columns:i(j),data:e.value},null,8,["columns","data"])])),_:2},1024)])),_:2},1024)])))),256))])),_:1})])):(l(),n("div",C,[d(i(m),{status:"404",title:"好像没有多余的请求用例了",description:"去创建几个试试吧"})]))])),_:1})):k("",!0)])),_:1}))}};export{E as default};
|
1
ydoc-lucky-ui/assets/MethodTrace.9051cb05.js
Normal file
1
ydoc-lucky-ui/assets/MethodTrace.9051cb05.js
Normal file
@ -0,0 +1 @@
|
||||
import{a as l}from"./index.4ac3587a.js";import{r as a,o as e,e as t,f as u,g as s,j as n,T as d,w as o,l as f,z as p,b as r,D as b}from"./vendor.08310185.js";const i=p(" asdsa "),y={setup(p){const y=a([{label:()=>r(b,null,{default:()=>"电灯熄灭 物换星移 泥牛入海"}),key:"1"},{label:()=>r(b,null,{default:()=>"电灯熄灭 物换星移 泥牛入海"}),key:"3"},{label:()=>r(b,null,{default:()=>"电灯熄灭 物换星移 泥牛入海"}),key:"4"},{label:()=>r(b,null,{default:()=>"电灯熄灭 物换星移 泥牛入海"}),key:"5"},{label:()=>r(b,null,{default:()=>"电灯熄灭 物换星移 泥牛入海"}),key:"6"},{label:()=>r(b,null,{default:()=>"电灯熄灭 物换星移 泥牛入海"}),key:"7"},{label:()=>r(b,null,{default:()=>"电灯熄灭 物换星移 泥牛入海"}),key:"8"},{label:()=>r(b,null,{default:()=>"黑暗好像 一颗巨石 按在胸口"}),key:"2"}]);return e((()=>{let a=window.location.pathname.replace("/doc.html","");a="http://127.0.0.1:8888",l({url:"http://127.0.0.1:8888/getMc",method:"get"}).then((l=>{l.data&&console.log(l.data)}))})),(l,a)=>(t(),u("div",null,[s(n(d),null,{default:o((()=>[s(n(f),{options:y.value,style:{width:"180px"},"default-value":"1"},null,8,["options"]),i])),_:1})]))}};export{y as default};
|
1
ydoc-lucky-ui/assets/Models.a928c1a8.js
Normal file
1
ydoc-lucky-ui/assets/Models.a928c1a8.js
Normal file
@ -0,0 +1 @@
|
||||
import{e as s,y as t,w as a,j as e,a3 as o,g as u,aq as i,t as n,z as l}from"./vendor.08310185.js";const r=l("去提个Issues催一下"),c={setup(l){let c=()=>{window.open("https://github.com/NoBugBoy/YdocLuckyUi/issues/new")};return(l,d)=>(s(),t(e(o),null,{default:a((()=>[u(e(i),{status:"500",title:"作者太懒了,这个功能并不太想做",description:"也许你真的不太需要这个东西."},{footer:a((()=>[u(e(n),{onClick:e(c)},{default:a((()=>[r])),_:1},8,["onClick"])])),_:1})])),_:1}))}};export{c as default};
|
1
ydoc-lucky-ui/assets/Share.3315ee0c.js
Normal file
1
ydoc-lucky-ui/assets/Share.3315ee0c.js
Normal file
File diff suppressed because one or more lines are too long
5
ydoc-lucky-ui/assets/icon.a080a8a4.svg
Normal file
5
ydoc-lucky-ui/assets/icon.a080a8a4.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg height="16" width="8" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<polygon points="0,0 8,8 0,16"
|
||||
style="fill:#666;stroke:purple;stroke-width:0" />
|
||||
</svg>
|
After Width: | Height: | Size: 156 B |
1
ydoc-lucky-ui/assets/index.4ac3587a.js
Normal file
1
ydoc-lucky-ui/assets/index.4ac3587a.js
Normal file
File diff suppressed because one or more lines are too long
1
ydoc-lucky-ui/assets/index.5b18e4ae.css
Normal file
1
ydoc-lucky-ui/assets/index.5b18e4ae.css
Normal file
File diff suppressed because one or more lines are too long
BIN
ydoc-lucky-ui/assets/title.8b763cb0.png
Normal file
BIN
ydoc-lucky-ui/assets/title.8b763cb0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 340 KiB |
1
ydoc-lucky-ui/assets/vendor.0513d345.css
Normal file
1
ydoc-lucky-ui/assets/vendor.0513d345.css
Normal file
@ -0,0 +1 @@
|
||||
.jv-container{box-sizing:border-box;position:relative}.jv-container.boxed{border:1px solid #eee;border-radius:6px}.jv-container.boxed:hover{box-shadow:0 2px 7px #00000026;border-color:transparent;position:relative}.jv-container.jv-light{background:#fff;white-space:nowrap;color:#525252;font-size:14px;font-family:Consolas,Menlo,Courier,monospace}.jv-container.jv-light .jv-ellipsis{color:#999;background-color:#eee;display:inline-block;line-height:.9;font-size:.9em;padding:0 4px 2px;margin:0 4px;border-radius:3px;vertical-align:2px;cursor:pointer;user-select:none}.jv-container.jv-light .jv-button{color:#49b3ff}.jv-container.jv-light .jv-key{color:#111;margin-right:4px}.jv-container.jv-light .jv-item.jv-array{color:#111}.jv-container.jv-light .jv-item.jv-boolean{color:#fc1e70}.jv-container.jv-light .jv-item.jv-function{color:#067bca}.jv-container.jv-light .jv-item.jv-number{color:#fc1e70}.jv-container.jv-light .jv-item.jv-object{color:#111}.jv-container.jv-light .jv-item.jv-undefined{color:#e08331}.jv-container.jv-light .jv-item.jv-string{color:#42b983;word-break:break-word;white-space:normal}.jv-container.jv-light .jv-item.jv-string .jv-link{color:#0366d6}.jv-container.jv-light .jv-code .jv-toggle:before{padding:0 2px;border-radius:2px}.jv-container.jv-light .jv-code .jv-toggle:hover:before{background:#eee}.jv-container .jv-code{overflow:hidden;padding:30px 20px}.jv-container .jv-code.boxed{max-height:300px}.jv-container .jv-code.open{max-height:initial!important;overflow:visible;overflow-x:auto;padding-bottom:45px}.jv-container .jv-toggle{background-image:url(/assets/icon.a080a8a4.svg);background-repeat:no-repeat;background-size:contain;background-position:center center;cursor:pointer;width:10px;height:10px;margin-right:2px;display:inline-block;transition:transform .1s}.jv-container .jv-toggle.open{transform:rotate(90deg)}.jv-container .jv-more{position:absolute;z-index:1;bottom:0;left:0;right:0;height:40px;width:100%;text-align:center;cursor:pointer}.jv-container .jv-more .jv-toggle{position:relative;top:40%;z-index:2;color:#888;transition:all .1s;transform:rotate(90deg)}.jv-container .jv-more .jv-toggle.open{transform:rotate(-90deg)}.jv-container .jv-more:after{content:"";width:100%;height:100%;position:absolute;bottom:0;left:0;z-index:1;background:linear-gradient(to bottom,rgba(0,0,0,0) 20%,rgba(230,230,230,.3) 100%);transition:all .1s}.jv-container .jv-more:hover .jv-toggle{top:50%;color:#111}.jv-container .jv-more:hover:after{background:linear-gradient(to bottom,rgba(0,0,0,0) 20%,rgba(230,230,230,.3) 100%)}.jv-container .jv-button{position:relative;cursor:pointer;display:inline-block;padding:5px;z-index:5}.jv-container .jv-button.copied{opacity:.4;cursor:default}.jv-container .jv-tooltip{position:absolute}.jv-container .jv-tooltip.right{right:15px}.jv-container .jv-tooltip.left{left:15px}.jv-container .j-icon{font-size:12px}.jv-node{position:relative}.jv-node:after{content:","}.jv-node:last-of-type:after{content:""}.jv-node.toggle{margin-left:13px!important}.jv-node .jv-node{margin-left:25px}
|
14
ydoc-lucky-ui/assets/vendor.08310185.js
Normal file
14
ydoc-lucky-ui/assets/vendor.08310185.js
Normal file
File diff suppressed because one or more lines are too long
BIN
ydoc-lucky-ui/favicon.ico
Normal file
BIN
ydoc-lucky-ui/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
18
ydoc-lucky-ui/index.html
Normal file
18
ydoc-lucky-ui/index.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<link rel="icon" href="{{BASE_URI}}/assets/title.8b763cb0.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>接口文档</title>
|
||||
<script type="module" crossorigin src="{{BASE_URI}}/assets/index.4ac3587a.js"></script>
|
||||
<link rel="modulepreload" href="{{BASE_URI}}/assets/vendor.08310185.js">
|
||||
<link rel="stylesheet" href="{{BASE_URI}}/assets/vendor.0513d345.css">
|
||||
<link rel="stylesheet" href="{{BASE_URI}}/assets/index.5b18e4ae.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user