// 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{} } 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 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, paramConfig.Name) } swaggerInfo.Definitions[parentPath].Properties[paramConfig.Name] = &define.SwaggerDefinitionProperty{ Description: paramConfig.Description, Type: util.GetSwaggerType(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) } // 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) { 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 = responseConfig.Type swaggerInfo.Definitions[parentPath].Properties[responseConfig.Field] = &define.SwaggerDefinitionProperty{ Description: responseConfig.Description, Type: responseConfig.Type, } return } if len(parentPath) == 0 { parentPath = uri + ".output" } if len(subPathArr) == 2 { if subPathArr[1] == "[]" { swaggerInfo.Definitions[parentPath].Properties[subPathArr[0]] = &define.SwaggerDefinitionProperty{ Description: responseConfig.Description, Type: "Array", } } else { generateResponseDefinitions(swaggerInfo, uri, parentPath+"."+subPathArr[0], strings.Join(subPathArr[1:], "."), responseConfig) } return } generateResponseDefinitions(swaggerInfo, uri, parentPath+"."+subPathArr[0], strings.Join(subPathArr[1:], "."), responseConfig) } func getUri(uri string) string { return "/" + strings.TrimLeft(uri, "/") }