// Package swagger ... // // Description : swagger ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2024-04-19 18:16 package swagger import ( "git.zhangdeman.cn/gateway/api-doc/define" "git.zhangdeman.cn/gateway/api-doc/util" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/wrapper" "net/http" "strings" ) // Generate 生成文档 // // Author : zhangdeman001@ke.com<张德满> // // Date : 18:17 2024/4/19 func Generate(docConfig *define.SwaggerInput) (*define.Swagger, error) { formatDocConfig(docConfig) swaggerInfo := &define.Swagger{ Schemes: docConfig.Schemes, Swagger: define.SwaggerVersion2, Host: docConfig.Host, BasePath: docConfig.BasePath, Info: docConfig.Info, Paths: map[string]map[string]*define.SwaggerPathConfig{}, Definitions: map[string]*define.SwaggerDefinition{}, } generatePathConfig(swaggerInfo, docConfig) return swaggerInfo, nil } // formatDocConfig 格式化填充dock配置 // // Author : go_developer@163.com<白茶清欢> // // Date : 10:54 2024/4/22 func formatDocConfig(docConfig *define.SwaggerInput) { 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) itemPath.ContentType = wrapper.TernaryOperator.String(len(itemPath.ContentType) == 0, consts.MimeTypeJson, wrapper.String(itemPath.ContentType)).Value() // 默认post请求 itemPath.Method = strings.TrimSpace(itemPath.Method) itemPath.Method = wrapper.TernaryOperator.String(len(itemPath.ContentType) == 0, wrapper.String(strings.ToLower(http.MethodPost)), wrapper.String(strings.ToLower(itemPath.Method))).Value() // 默认summary itemPath.Summary = strings.TrimSpace(itemPath.Summary) itemPath.Summary = wrapper.TernaryOperator.String(len(itemPath.Summary) == 0, wrapper.String("接口 : "+itemPath.Uri), wrapper.String(itemPath.Summary)).Value() // 默认标签 itemPath.TagList = wrapper.TernaryOperator.Array(len(itemPath.TagList) == 0, wrapper.ArrayType([]string{"未分组"}), wrapper.ArrayType(itemPath.TagList)).ToStringSlice() for _, itemParam := range itemPath.ParameterList { // 填充默认参数位置 itemParam.In = strings.TrimSpace(itemParam.In) 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) itemParam.Type = wrapper.TernaryOperator.String(len(itemParam.Type) == 0, wrapper.String(itemParam.Type), wrapper.String(itemParam.Type)).Value() // 参数描述 itemParam.Description = strings.TrimSpace(itemParam.Description) itemParam.Description = wrapper.TernaryOperator.String(len(itemParam.Description) == 0, wrapper.String(itemParam.Type+" : "+itemParam.Name), wrapper.String(itemParam.Description)).Value() } for _, itemResponseConfig := range itemPath.ResponseList { for _, itemResponse := range itemResponseConfig.List { // 默认返回数据类型 itemResponse.Type = strings.TrimSpace(itemResponse.Type) itemResponse.Type = wrapper.TernaryOperator.String(len(itemResponse.Type) == 0, consts.DataTypeString, wrapper.String(itemResponse.Type)).Value() // 填充默认描述 itemResponse.Description = strings.TrimSpace(itemResponse.Description) itemResponse.Description = wrapper.TernaryOperator.String(len(itemResponse.Description) == 0, wrapper.String(itemResponse.Type+" : "+itemResponse.Field), wrapper.String(itemResponse.Description)).Value() } } } } // 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), Responses: 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{} hasDealResponseTable := map[string]bool{} for _, itemParamInput := range pathConfig.ParameterList { if len(itemParamInput.Name) == 0 { // 未指定参数名, 忽略 continue } // name 可能是 x.x.x 递归数组, 或者 x.x.[].x namePath := strings.Split(itemParamInput.Name, ".") realParamName := namePath[0] parentPath := "" if !strings.Contains(realParamName, ".") { realParamName = "jsonBody" parentPath = pathConfig.Uri + ".jsonBody" } generateParameterDefinitions(swaggerInfo, pathConfig.Uri, parentPath, itemParamInput.Name, itemParamInput) if _, exist := hasDealTable[realParamName]; !exist { hasDealTable[realParamName] = true generateParam := &define.SwaggerPathConfigParameter{ Type: wrapper.TernaryOperator.String(realParamName == "jsonBody", "", wrapper.String(itemParamInput.Type)).Value(), Description: wrapper.TernaryOperator.String(realParamName == "jsonBody", "参数结构", wrapper.String(itemParamInput.Description)).Value(), Name: realParamName, In: itemParamInput.In, Required: itemParamInput.Required, Schema: map[string]string{}, } if len(parentPath) > 0 { generateParam.Schema["$ref"] = "#/definitions/" + strings.TrimLeft(pathConfig.Uri, "/") + ".jsonBody" generateParam.Type = "" } swaggerInfo.Paths[pathConfig.Uri][pathConfig.Method].Parameters = append(swaggerInfo.Paths[pathConfig.Uri][pathConfig.Method].Parameters, generateParam) } } for _, itemResponseConfig := range pathConfig.ResponseList { for _, itemResponseInput := range itemResponseConfig.List { if len(itemResponseInput.Field) == 0 { // 未指定参数名, 忽略 continue } // name 可能是 x.x.x 递归数组, 或者 x.x.[].x outputDefine := strings.TrimLeft(pathConfig.Uri, "/") + "." + itemResponseConfig.Code + ".output" generateResponseDefinitions(swaggerInfo, pathConfig.Uri, outputDefine, itemResponseInput.Field, itemResponseInput) namePath := strings.Split(itemResponseInput.Field, ".") if _, exist := hasDealResponseTable[namePath[0]]; !exist { hasDealResponseTable[namePath[0]] = true swaggerInfo.Paths[pathConfig.Uri][pathConfig.Method].Responses[itemResponseConfig.Code] = &define.SwaggerPathConfigResponse{ Description: "返回数据", Schema: map[string]string{ "$ref": "#/definitions/" + outputDefine, }, } } } } } // 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) { uri = strings.TrimLeft(uri, "/") parentPath = strings.TrimLeft(parentPath, "/") if nil == swaggerInfo.Definitions { swaggerInfo.Definitions = map[string]*define.SwaggerDefinition{} } parentPathArr := strings.Split(parentPath, ".") checkPath := parentPath if len(parentPathArr) >= 2 { checkPath = strings.Join([]string{parentPathArr[0], parentPathArr[1]}, ".") } subPathArr := strings.Split(subPath, ".") if _, exist := swaggerInfo.Definitions[checkPath]; !exist && len(parentPath) > 0 { swaggerInfo.Definitions[checkPath] = &define.SwaggerDefinition{ Type: "object", Required: make([]string, 0), Properties: make(map[string]*define.SwaggerDefinitionProperty), } } if len(subPathArr) == 1 { if paramConfig.In != strings.ToLower(consts.RequestLocationBody) { // 长度为1, 还不在 body, 无需生成结构体 return } if _, exist := swaggerInfo.Definitions[parentPath]; !exist { swaggerInfo.Definitions[parentPath] = &define.SwaggerDefinition{ Type: "object", Required: make([]string, 0), Properties: make(map[string]*define.SwaggerDefinitionProperty), } } swaggerInfo.Definitions[parentPath].Type = "object" if paramConfig.Required { swaggerInfo.Definitions[parentPath].Required = append(swaggerInfo.Definitions[parentPath].Required, subPath) } swaggerInfo.Definitions[parentPath].Properties[subPath] = &define.SwaggerDefinitionProperty{ Description: paramConfig.Description, Type: util.GetSwaggerType(paramConfig.Type), } return } if len(parentPath) == 0 { parentPath = uri + ".input" } if len(subPathArr) == 2 { if _, exist := swaggerInfo.Definitions[parentPath]; !exist { swaggerInfo.Definitions[parentPath] = &define.SwaggerDefinition{ Type: "object", Required: make([]string, 0), Properties: make(map[string]*define.SwaggerDefinitionProperty), } } if subPathArr[1] == "[]" { swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = &define.SwaggerDefinitionProperty{ Description: paramConfig.Description, Type: "array", Items: map[string]string{ "type": util.GetSwaggerType(paramConfig.Type), }, } } else { if subPathArr[0] == "[]" { generateParameterDefinitions(swaggerInfo, uri, parentPath+".item", strings.Join(subPathArr[1:], "."), paramConfig) return } else { swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = &define.SwaggerDefinitionProperty{ Description: "参数描述", Type: "object", AllOf: []map[string]string{map[string]string{ "$ref": "#/definitions/" + parentPath + "." + subPathArr[0], }}, } } storageSubPath := parentPath + "." + subPathArr[0] if subPathArr[1] == "[]" { storageSubPath += ".item" } if _, exist := swaggerInfo.Definitions[storageSubPath]; !exist { swaggerInfo.Definitions[storageSubPath] = &define.SwaggerDefinition{ Type: wrapper.TernaryOperator.String(strings.HasSuffix(parentPath, "[]"), "array", "object").Value(), Required: make([]string, 0), Properties: make(map[string]*define.SwaggerDefinitionProperty), } } if paramConfig.Required { swaggerInfo.Definitions[storageSubPath].Required = append(swaggerInfo.Definitions[storageSubPath].Required, subPathArr[1]) } swaggerInfo.Definitions[storageSubPath].Properties[subPathArr[1]] = &define.SwaggerDefinitionProperty{ Description: paramConfig.Description, Type: util.GetSwaggerType(paramConfig.Type), } } return } if _, exist := swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]]; !exist { if subPathArr[1] == "[]" { nextParentPath := parentPath + "." + subPathArr[0] + ".item" swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = &define.SwaggerDefinitionProperty{ Description: "参数描述", Type: "array", Items: map[string]string{ "$ref": "#/definitions/" + nextParentPath, }, } generateParameterDefinitions(swaggerInfo, uri, nextParentPath, strings.Join(subPathArr[2:], "."), paramConfig) return } itemSwaggerDefinition := &define.SwaggerDefinitionProperty{ Description: "对象描述", Type: wrapper.TernaryOperator.String(subPathArr[1] == "[]", "array", "object").Value(), Items: nil, AllOf: nil, } if itemSwaggerDefinition.Type == "object" { itemSwaggerDefinition.AllOf = []map[string]string{map[string]string{ "$ref": "#/definitions/" + parentPath + "." + subPathArr[0], }} } else if itemSwaggerDefinition.Type == "array" { itemSwaggerDefinition.Description = "数组描述" itemSwaggerDefinition.Items = map[string]string{ "$ref": "#/definitions/" + parentPath + "." + subPathArr[0] + ".item", } } swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = itemSwaggerDefinition } // fmt.Println(parentPath + "." + subPathArr[0]) generateParameterDefinitions(swaggerInfo, uri, parentPath+"."+subPathArr[0], strings.Join(subPathArr[1:], "."), paramConfig) } // generateResponseDefinitions ... // // Author : go_developer@163.com<白茶清欢> // // Date : 18:28 2024/4/22 func generateResponseDefinitions(swaggerInfo *define.Swagger, uri string, parentPath string, subPath string, responseConfig *define.SwaggerResponseItemInput) { uri = strings.TrimLeft(uri, "/") parentPath = strings.TrimLeft(parentPath, "/") if nil == swaggerInfo.Definitions { swaggerInfo.Definitions = map[string]*define.SwaggerDefinition{} } parentPathArr := strings.Split(parentPath, ".") checkPath := parentPath if len(parentPathArr) >= 3 { checkPath = strings.Join([]string{parentPathArr[0], parentPathArr[1], parentPathArr[2]}, ".") } subPathArr := strings.Split(subPath, ".") if _, exist := swaggerInfo.Definitions[checkPath]; !exist && len(parentPath) > 0 { swaggerInfo.Definitions[checkPath] = &define.SwaggerDefinition{ Type: "object", Required: make([]string, 0), Properties: make(map[string]*define.SwaggerDefinitionProperty), } } if len(subPathArr) == 1 { if _, exist := swaggerInfo.Definitions[parentPath]; !exist { swaggerInfo.Definitions[parentPath] = &define.SwaggerDefinition{ Type: "object", Required: make([]string, 0), Properties: make(map[string]*define.SwaggerDefinitionProperty), } } swaggerInfo.Definitions[parentPath].Type = "object" if responseConfig.IsRequired { swaggerInfo.Definitions[parentPath].Required = append(swaggerInfo.Definitions[parentPath].Required, subPath) } swaggerInfo.Definitions[parentPath].Properties[subPath] = &define.SwaggerDefinitionProperty{ Description: responseConfig.Description, Type: util.GetSwaggerType(responseConfig.Type), } return } if len(parentPath) == 0 { parentPath = uri + ".output" } if len(subPathArr) == 2 { if _, exist := swaggerInfo.Definitions[parentPath]; !exist { swaggerInfo.Definitions[parentPath] = &define.SwaggerDefinition{ Type: "object", Required: make([]string, 0), Properties: make(map[string]*define.SwaggerDefinitionProperty), } } if subPathArr[1] == "[]" { swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = &define.SwaggerDefinitionProperty{ Description: responseConfig.Description, Type: "array", Items: map[string]string{ "type": util.GetSwaggerType(responseConfig.Type), }, } } else { if subPathArr[0] == "[]" { generateResponseDefinitions(swaggerInfo, uri, parentPath+".item", strings.Join(subPathArr[1:], "."), responseConfig) return } else { swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = &define.SwaggerDefinitionProperty{ Description: "返回值描述", Type: "object", AllOf: []map[string]string{map[string]string{ "$ref": "#/definitions/" + parentPath + "." + subPathArr[0], }}, } } storageSubPath := parentPath + "." + subPathArr[0] if subPathArr[1] == "[]" { storageSubPath += ".item" } if _, exist := swaggerInfo.Definitions[storageSubPath]; !exist { swaggerInfo.Definitions[storageSubPath] = &define.SwaggerDefinition{ Type: wrapper.TernaryOperator.String(strings.HasSuffix(parentPath, "[]"), "array", "object").Value(), Required: make([]string, 0), Properties: make(map[string]*define.SwaggerDefinitionProperty), } } if responseConfig.IsRequired { swaggerInfo.Definitions[storageSubPath].Required = append(swaggerInfo.Definitions[storageSubPath].Required, subPathArr[1]) } swaggerInfo.Definitions[storageSubPath].Properties[subPathArr[1]] = &define.SwaggerDefinitionProperty{ Description: responseConfig.Description, Type: util.GetSwaggerType(responseConfig.Type), } } return } if _, exist := swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]]; !exist { if subPathArr[1] == "[]" { nextParentPath := parentPath + "." + subPathArr[0] + ".item" generateResponseDefinitions(swaggerInfo, uri, nextParentPath, strings.Join(subPathArr[2:], "."), responseConfig) return } itemSwaggerDefinition := &define.SwaggerDefinitionProperty{ Description: "对象描述", Type: wrapper.TernaryOperator.String(subPathArr[1] == "[]", "array", "object").Value(), Items: nil, AllOf: nil, } if itemSwaggerDefinition.Type == "object" { itemSwaggerDefinition.AllOf = []map[string]string{map[string]string{ "$ref": "#/definitions/" + parentPath + "." + subPathArr[0], }} } else if itemSwaggerDefinition.Type == "array" { itemSwaggerDefinition.Description = "数组描述" itemSwaggerDefinition.Items = map[string]string{ "$ref": "#/definitions/" + parentPath + "." + subPathArr[0] + ".item", } } swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = itemSwaggerDefinition } // fmt.Println(parentPath + "." + subPathArr[0]) generateResponseDefinitions(swaggerInfo, uri, parentPath+"."+subPathArr[0], strings.Join(subPathArr[1:], "."), responseConfig) } func getUri(uri string) string { return "/" + strings.TrimLeft(uri, "/") }