649 lines
24 KiB
Go
649 lines
24 KiB
Go
// 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.openapiDocLocation2GatewayLocation(itemParam.In),
|
||
ParamType: hod.openapiDocType2GatewayType(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.openapiDocType2GatewayType(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.openapiDocType2GatewayType(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.openapiDocType2GatewayType(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.openapiDocType2GatewayType(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.openapiDocType2GatewayType(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.openapiDocType2GatewayType(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.openapiDocType2GatewayType(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)
|
||
}
|
||
|
||
// openapiDocLocation2GatewayLocation 文档数据位置转为网关的数据位置
|
||
//
|
||
// Author : go_developer@163.com<白茶清欢>
|
||
//
|
||
// Date : 21:06 2025/2/26
|
||
func (hod *handleOpenapiDoc) openapiDocLocation2GatewayLocation(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()
|
||
}
|
||
|
||
// openapiDocType2GatewayType 文档数据类型转为网关数据类型
|
||
//
|
||
// Author : go_developer@163.com<白茶清欢>
|
||
//
|
||
// Date : 21:07 2025/2/26
|
||
func (hod *handleOpenapiDoc) openapiDocType2GatewayType(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()
|
||
}
|