From 9569f7dfd0d2af70f8b83387f990d69e281d6e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Mon, 22 Apr 2024 18:21:46 +0800 Subject: [PATCH] =?UTF-8?q?v1=E5=9F=BA=E7=A1=80=E8=AF=B7=E6=B1=82=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/types.go | 32 ++++++------ swagger/run.go | 125 ++++++++++++++++++++++++++++++++++++++++++-- swagger/run_test.go | 63 ++++++++++++++++++++++ util/tool.go | 19 +++++++ 4 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 swagger/run_test.go diff --git a/define/types.go b/define/types.go index 89f3c11..505bd2e 100644 --- a/define/types.go +++ b/define/types.go @@ -66,6 +66,7 @@ type SwaggerPathConfigParameter struct { Name string `json:"name"` // 参数名称 In string `json:"in"` // 参数位置 Required bool `json:"required"` // 是否必传 + EnumList []interface{} `json:"enum_list"` // 枚举值列表 Schema map[string]string `json:"schema"` // 参数schema } @@ -85,9 +86,9 @@ type SwaggerPathConfigResponse struct { // // Date : 17:01 2024/4/19 type SwaggerDefinition struct { - Type string `json:"type"` // 类型 - Required []string `json:"required"` // 必传参数列表 - Properties map[string]SwaggerDefinitionProperty `json:"properties"` // 参数名 => 参数配置 + Type string `json:"type"` // 类型 + Required []string `json:"required"` // 必传参数列表 + Properties map[string]*SwaggerDefinitionProperty `json:"properties"` // 参数名 => 参数配置 } // SwaggerDefinitionProperty ... @@ -106,13 +107,13 @@ type SwaggerDefinitionProperty struct { // // Date : 16:12 2024/4/19 type Swagger struct { - Schemes []string `json:"schemes"` - Swagger string `json:"swagger"` // swagger版本 - 2.0 - Host string `json:"host"` // 服务域名 - BasePath string `json:"basePath"` // 基础path - Info Info `json:"info"` // 文档描述信息 - Paths map[string]map[string]SwaggerPathConfig `json:"paths"` // 接口列表 : 接口 => 请求方法 => 请求配置 - Definitions map[string]SwaggerDefinition `json:"definitions"` // 数据定义 + Schemes []string `json:"schemes"` + Swagger string `json:"swagger"` // swagger版本 - 2.0 + Host string `json:"host"` // 服务域名 + BasePath string `json:"basePath"` // 基础path + Info Info `json:"info"` // 文档描述信息 + Paths map[string]map[string]*SwaggerPathConfig `json:"paths"` // 接口列表 : 接口 => 请求方法 => 请求配置 + Definitions map[string]*SwaggerDefinition `json:"definitions"` // 数据定义 } // SwaggerInput ... @@ -151,11 +152,12 @@ type SwaggerPathInput struct { // // Date : 10:25 2024/4/22 type SwaggerParameterInput struct { - Type string `json:"type"` // 类型 - Description string `json:"description"` // 描述 - Name string `json:"name"` // 参数名称 - In string `json:"in"` // 参数位置 - Required bool `json:"required"` // 是否必传 + Type string `json:"type"` // 类型 + Description string `json:"description"` // 描述 + Name string `json:"name"` // 参数名称 + In string `json:"in"` // 参数位置 + Required bool `json:"required"` // 是否必传 + EnumList []interface{} `json:"enum_list"` // 枚举值列表 } // SwaggerResponseInput 响应数据 diff --git a/swagger/run.go b/swagger/run.go index 2f33ea2..9ad5f59 100644 --- a/swagger/run.go +++ b/swagger/run.go @@ -29,9 +29,10 @@ func Generate(docConfig *define.SwaggerInput) (*define.Swagger, error) { Host: docConfig.Host, BasePath: docConfig.BasePath, Info: docConfig.Info, - Paths: map[string]map[string]define.SwaggerPathConfig{}, - Definitions: map[string]define.SwaggerDefinition{}, + Paths: map[string]map[string]*define.SwaggerPathConfig{}, + Definitions: map[string]*define.SwaggerDefinition{}, } + generatePathConfig(swaggerInfo, docConfig) return swaggerInfo, nil } @@ -41,9 +42,13 @@ func Generate(docConfig *define.SwaggerInput) (*define.Swagger, error) { // // Date : 10:54 2024/4/22 func formatDocConfig(docConfig *define.SwaggerInput) { - if docConfig.Schemes == nil { - docConfig.Schemes = make([]string, 0) + if len(docConfig.Schemes) == 0 { + docConfig.Schemes = []string{consts.SchemeHTTP} } + docConfig.Host = wrapper.String(docConfig.Host).ReplaceChar(map[string]string{ + "http://": "", + "https://": "", + }).Value() for _, itemPath := range docConfig.PathConfigList { // 默认请求类型 application/json itemPath.ContentType = strings.TrimSpace(itemPath.ContentType) @@ -59,7 +64,7 @@ func formatDocConfig(docConfig *define.SwaggerInput) { for _, itemParam := range itemPath.ParameterList { // 填充默认参数位置 itemParam.In = strings.TrimSpace(itemParam.In) - itemPath.Summary = wrapper.TernaryOperator.String(len(itemParam.In) == 0, wrapper.String(util.GetParameterDefaultLocation(itemPath.Method)), wrapper.String(itemParam.In)).Value() + itemParam.In = wrapper.TernaryOperator.String(len(itemParam.In) == 0, wrapper.String(util.GetParameterDefaultLocation(itemPath.Method)), wrapper.String(itemParam.In)).Value() // 参数类型没填, 按照字符串处理 itemParam.Type = strings.TrimSpace(itemParam.Type) @@ -80,3 +85,113 @@ func formatDocConfig(docConfig *define.SwaggerInput) { } } } + +// generatePathConfig 生成接口配置 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 12:00 2024/4/22 +func generatePathConfig(swaggerInfo *define.Swagger, docConfig *define.SwaggerInput) { + for _, itemPath := range docConfig.PathConfigList { + itemPath.Uri = getUri(itemPath.Uri) + if _, exist := swaggerInfo.Paths[itemPath.Uri]; !exist { + swaggerInfo.Paths[itemPath.Uri] = map[string]*define.SwaggerPathConfig{} + } + // 处理请求类型的配置 + swaggerInfo.Paths[itemPath.Uri][itemPath.Method] = &define.SwaggerPathConfig{ + Description: itemPath.Description, + Consumes: []string{itemPath.ContentType}, + Tags: itemPath.TagList, + Summary: itemPath.Summary, + Parameters: make([]*define.SwaggerPathConfigParameter, 0), + Response: make(map[string]*define.SwaggerPathConfigResponse), + } + // 生成参数配置 + generatePathParameterConfig(swaggerInfo, itemPath) + } +} + +// generatePathParameterConfig 生成接口的参数配置 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 12:09 2024/4/22 +func generatePathParameterConfig(swaggerInfo *define.Swagger, pathConfig *define.SwaggerPathInput) { + swaggerInfo.Paths[pathConfig.Uri][pathConfig.Method].Parameters = make([]*define.SwaggerPathConfigParameter, 0) + hasDealTable := map[string]bool{} + + for _, itemParamInput := range pathConfig.ParameterList { + if len(itemParamInput.Name) == 0 { + // 未指定参数名, 忽略 + continue + } + // name 可能是 x.x.x 递归数组, 或者 x.x.[].x + generateParameterDefinitions(swaggerInfo, pathConfig.Uri, "", itemParamInput.Name, itemParamInput) + namePath := strings.Split(itemParamInput.Name, ".") + if _, exist := hasDealTable[namePath[0]]; !exist { + hasDealTable[namePath[0]] = true + swaggerInfo.Paths[pathConfig.Uri][pathConfig.Method].Parameters = append(swaggerInfo.Paths[pathConfig.Uri][pathConfig.Method].Parameters, &define.SwaggerPathConfigParameter{ + Type: itemParamInput.Type, + Description: itemParamInput.Description, + Name: namePath[0], + In: itemParamInput.In, + Required: itemParamInput.Required, + Schema: map[string]string{ + // "$ref": "#/definitions/" + pathConfig.Uri + ".input", + }, + }) + } + } +} + +// generateParameterDefinitions 生成请求参数的描述 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 14:06 2024/4/22 +func generateParameterDefinitions(swaggerInfo *define.Swagger, uri string, parentPath string, subPath string, paramConfig *define.SwaggerParameterInput) { + if nil == swaggerInfo.Definitions { + swaggerInfo.Definitions = map[string]*define.SwaggerDefinition{} + } + subPathArr := strings.Split(subPath, ".") + if _, exist := swaggerInfo.Definitions[parentPath]; !exist && len(parentPath) > 0 { + swaggerInfo.Definitions[parentPath] = &define.SwaggerDefinition{ + Type: "object", + Required: make([]string, 0), + Properties: make(map[string]*define.SwaggerDefinitionProperty), + } + } + if len(subPathArr) == 1 { + if len(parentPath) == 0 { + return + } + swaggerInfo.Definitions[parentPath].Type = paramConfig.Type + if paramConfig.Required { + swaggerInfo.Definitions[parentPath].Required = append(swaggerInfo.Definitions[parentPath].Required, paramConfig.Name) + } + /*swaggerInfo.Definitions[parentPath].Properties[paramConfig.Name] = &define.SwaggerDefinitionProperty{ + Description: paramConfig.Description, + Type: paramConfig.Type, + }*/ + return + } + if len(parentPath) == 0 { + parentPath = uri + ".input" + } + if len(subPathArr) == 2 { + if subPathArr[1] == "[]" { + swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = &define.SwaggerDefinitionProperty{ + Description: paramConfig.Description, + Type: "Array", + } + } else { + generateParameterDefinitions(swaggerInfo, uri, parentPath+"."+subPathArr[0], strings.Join(subPathArr[1:], "."), paramConfig) + } + return + } + generateParameterDefinitions(swaggerInfo, uri, parentPath+"."+subPathArr[0], strings.Join(subPathArr[1:], "."), paramConfig) +} + +func getUri(uri string) string { + return "/" + strings.TrimLeft(uri, "/") +} diff --git a/swagger/run_test.go b/swagger/run_test.go new file mode 100644 index 0000000..701a0e3 --- /dev/null +++ b/swagger/run_test.go @@ -0,0 +1,63 @@ +// Package swagger ... +// +// Description : swagger ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-04-22 16:02 +package swagger + +import ( + "encoding/json" + "fmt" + "git.zhangdeman.cn/gateway/api-doc/define" + "git.zhangdeman.cn/zhangdeman/consts" + "net/http" + "testing" +) + +func TestGenerate(t *testing.T) { + res, _ := Generate(&define.SwaggerInput{ + Schemes: []string{}, + Swagger: define.SwaggerVersion2, + Host: "http://www.baidu.com", + BasePath: "/", + Info: define.Info{ + Description: "测试", + Title: "测试", + Contact: define.Contact{ + Name: "白茶", + Url: "http://www.baidu.com", + Email: "go@email.com", + }, + License: define.License{ + Name: consts.LicenseApache20, + Url: consts.LicenseUrlTable[consts.LicenseApache20], + }, + Version: "", + }, + PathConfigList: []*define.SwaggerPathInput{ + &define.SwaggerPathInput{ + Uri: "/test", + Method: http.MethodPost, + ContentType: consts.MimeTypeJson, + Summary: "测试接口", + Description: "测试接口", + TagList: []string{"test"}, + ParameterList: []*define.SwaggerParameterInput{ + &define.SwaggerParameterInput{ + Type: consts.DataTypeString, + Description: "姓名", + Name: "name", + In: "body", + Required: true, + EnumList: []interface{}{"zhang", "de", "man"}, + }, + }, + ResponseList: nil, + }, + }, + }) + byteData, _ := json.MarshalIndent(res, "", "\t") + fmt.Println(string(byteData)) +} diff --git a/util/tool.go b/util/tool.go index a43093f..a2f1c4a 100644 --- a/util/tool.go +++ b/util/tool.go @@ -43,3 +43,22 @@ func GetParameterDefaultLocation(requestMethod string) string { } return consts.RequestLocationQuery } + +// GetSwaggerType 基于输入的类型, +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 12:23 2024/4/22 +func GetSwaggerType(inputType string) string { + convertTable := map[string]string{ + consts.DataTypeString: "string", + consts.DataTypeInt: "integer", + consts.DataTypeUint: "integer", + consts.DataTypeFloat: "number", + consts.DataTypeBool: "boolean", + } + if _, exist := convertTable[inputType]; exist { + return convertTable[inputType] + } + return inputType +}