diff --git a/.gitignore b/.gitignore index d01e513cde2b..a3c9b90ad66b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ go.work.sum # vim auto backup file *~ !OWNERS +tools/goctl/goctl.exe diff --git a/tools/goctl/api/swagger/options.go b/tools/goctl/api/swagger/options.go index a2a35fa4d5f0..800a24d7123b 100644 --- a/tools/goctl/api/swagger/options.go +++ b/tools/goctl/api/swagger/options.go @@ -62,23 +62,23 @@ func rangeValueFromOptions(options []string) (minimum *float64, maximum *float64 } func enumsValueFromOptions(options []string) []any { - if len(options) == 0 { - return []any{} - } + var enums []any for _, option := range options { - if strings.HasPrefix(option, enumFlag) { - var resp = make([]any, 0) - val := option[8:] - fields := util.FieldsAndTrimSpace(val, func(r rune) bool { - return r == '|' - }) - for _, field := range fields { - resp = append(resp, field) + if strings.HasPrefix(option, "options=") { + optionValue := strings.TrimPrefix(option, "options=") + for _, enum := range strings.Split(optionValue, "|") { + if enum == "true" { + enums = append(enums, true) + } else if enum == "false" { + enums = append(enums, false) + } else { + enums = append(enums, enum) + } } - return resp + break } } - return []any{} + return enums } func defValueFromOptions(ctx Context, options []string, apiType spec.Type) any { diff --git a/tools/goctl/api/swagger/parameter.go b/tools/goctl/api/swagger/parameter.go index 2f5970535f0b..8c8c7645b763 100644 --- a/tools/goctl/api/swagger/parameter.go +++ b/tools/goctl/api/swagger/parameter.go @@ -8,22 +8,25 @@ import ( apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec" ) -func isPostJson(ctx Context, method string, tp apiSpec.Type) (string, bool) { - if strings.EqualFold(method, http.MethodPost) { +func isJsonMethod(ctx Context, method string, tp apiSpec.Type) (string, bool) { + // 支持POST、PUT、PATCH方法使用JSON请求体 + if !strings.EqualFold(method, http.MethodPost) && + !strings.EqualFold(method, http.MethodPut) && + !strings.EqualFold(method, http.MethodPatch) { return "", false } structType, ok := tp.(apiSpec.DefineStruct) if !ok { return "", false } - var isPostJson bool + var hasJsonFields bool rangeMemberAndDo(ctx, structType, func(tag *apiSpec.Tags, required bool, member apiSpec.Member) { jsonTag, _ := tag.Get(tagJson) - if !isPostJson { - isPostJson = jsonTag != nil + if !hasJsonFields { + hasJsonFields = jsonTag != nil } }) - return structType.RawName, isPostJson + return structType.RawName, hasJsonFields } func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Parameter { @@ -100,7 +103,8 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para } if hasForm { minimum, maximum, exclusiveMinimum, exclusiveMaximum := rangeValueFromOptions(formTag.Options) - if strings.EqualFold(method, http.MethodGet) { + // GET和DELETE请求都使用query参数 + if strings.EqualFold(method, http.MethodGet) || strings.EqualFold(method, http.MethodDelete) { resp = append(resp, spec.Parameter{ CommonValidations: spec.CommonValidations{ Maximum: maximum, @@ -145,7 +149,6 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para }, }) } - } if hasJson { minimum, maximum, exclusiveMinimum, exclusiveMaximum := rangeValueFromOptions(jsonTag.Options) @@ -181,7 +184,7 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para }) if len(properties) > 0 { if ctx.UseDefinitions { - structName, ok := isPostJson(ctx, method, tp) + structName, ok := isJsonMethod(ctx, method, tp) if ok { resp = append(resp, spec.Parameter{ ParamProps: spec.ParamProps{ diff --git a/tools/goctl/api/swagger/properties.go b/tools/goctl/api/swagger/properties.go index e5bf6d204af8..54122d67ae79 100644 --- a/tools/goctl/api/swagger/properties.go +++ b/tools/goctl/api/swagger/properties.go @@ -50,6 +50,14 @@ func propertiesFromType(ctx Context, tp apiSpec.Type) (spec.SchemaProperties, [] requiredFields = append(requiredFields, jsonTagString) } + if ctx.UseDefinitions { + schema := buildSchemaWithDefinitions(ctx, member.Type, example, member.Comment, minimum, maximum, exclusiveMinimum, exclusiveMaximum, defaultValue, enum) + if schema != nil { + properties[jsonTagString] = *schema + return + } + } + schema := spec.Schema{ SwaggerSchemaProps: spec.SwaggerSchemaProps{ Example: example, @@ -75,12 +83,6 @@ func propertiesFromType(ctx Context, tp apiSpec.Type) (spec.SchemaProperties, [] schema.Properties = p schema.Required = r } - if ctx.UseDefinitions { - structName, containsStruct := containsStruct(member.Type) - if containsStruct { - schema.SchemaProps.Ref = spec.MustCreateRef(getRefName(structName)) - } - } properties[jsonTagString] = schema }) @@ -89,6 +91,97 @@ func propertiesFromType(ctx Context, tp apiSpec.Type) (spec.SchemaProperties, [] return properties, requiredFields } +// buildSchemaWithDefinitions Correctly handle $ref references for composite types +func buildSchemaWithDefinitions(ctx Context, tp apiSpec.Type, example any, comment string, minimum, maximum *float64, exclusiveMinimum, exclusiveMaximum bool, defaultValue any, enum []any) *spec.Schema { + switch val := tp.(type) { + case apiSpec.DefineStruct: + return &spec.Schema{ + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + Example: example, + }, + SchemaProps: spec.SchemaProps{ + Description: formatComment(comment), + Ref: spec.MustCreateRef(getRefName(val.RawName)), + }, + } + case apiSpec.PointerType: + return buildSchemaWithDefinitions(ctx, val.Type, example, comment, minimum, maximum, exclusiveMinimum, exclusiveMaximum, defaultValue, enum) + case apiSpec.ArrayType: + itemSchema := buildSchemaWithDefinitions(ctx, val.Value, nil, "", nil, nil, false, false, nil, nil) + if itemSchema != nil { + return &spec.Schema{ + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + Example: example, + }, + SchemaProps: spec.SchemaProps{ + Description: formatComment(comment), + Type: spec.StringOrArray{"array"}, + Items: &spec.SchemaOrArray{ + Schema: itemSchema, + }, + }, + } + } + + structName, containsStruct := containsStruct(val.Value) + if containsStruct { + return &spec.Schema{ + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + Example: example, + }, + SchemaProps: spec.SchemaProps{ + Description: formatComment(comment), + Type: spec.StringOrArray{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef(getRefName(structName)), + }, + }, + }, + }, + } + } + case apiSpec.MapType: + valueSchema := buildSchemaWithDefinitions(ctx, val.Value, nil, "", nil, nil, false, false, nil, nil) + if valueSchema != nil { + return &spec.Schema{ + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + Example: example, + }, + SchemaProps: spec.SchemaProps{ + Description: formatComment(comment), + Type: spec.StringOrArray{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Schema: valueSchema, + }, + }, + } + } + structName, containsStruct := containsStruct(val.Value) + if containsStruct { + return &spec.Schema{ + SwaggerSchemaProps: spec.SwaggerSchemaProps{ + Example: example, + }, + SchemaProps: spec.SchemaProps{ + Description: formatComment(comment), + Type: spec.StringOrArray{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef(getRefName(structName)), + }, + }, + }, + }, + } + } + } + + return nil +} + func containsStruct(tp apiSpec.Type) (string, bool) { switch val := tp.(type) { case apiSpec.PointerType: diff --git a/tools/goctl/api/swagger/swagger.go b/tools/goctl/api/swagger/swagger.go index 41966bf77537..da04f27c08dd 100644 --- a/tools/goctl/api/swagger/swagger.go +++ b/tools/goctl/api/swagger/swagger.go @@ -73,6 +73,33 @@ func itemsFromGoType(ctx Context, tp apiSpec.Type) *spec.SchemaOrArray { if !ok { return nil } + + if ctx.UseDefinitions { + structName, containsStruct := containsStruct(array.Value) + if containsStruct { + if _, ok := array.Value.(apiSpec.DefineStruct); ok { + return &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef(getRefName(structName)), + }, + }, + } + } + if ptr, ok := array.Value.(apiSpec.PointerType); ok { + if _, ok := ptr.Type.(apiSpec.DefineStruct); ok { + return &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: spec.MustCreateRef(getRefName(structName)), + }, + }, + } + } + } + } + } + return itemFromGoType(ctx, array.Value) }