Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/goai: When generating enums documents and validating enums, is it possible to support customizing the actual interactive enumeration values? ? #4082

Open
shuqingzai opened this issue Dec 26, 2024 · 7 comments

Comments

@shuqingzai
Copy link

shuqingzai commented Dec 26, 2024

Is your feature request related to a problem?

Option Yes

Describe the solution you'd like

目前使用 make enums 生成 enums 注入程序时,只会直接获取实际枚举值作为注入内容,是否有策略可以自定义写入内容,类似 Java 或 C# 的枚举类,自定义映射值

  1. 希望对外部使用 字符串 枚举值输出,方便查看,见名思意
  2. 对内部存储使用 int 减少存储数据

参考下面的文件,希望对外显示的是 string 而不是 int ( OpenAPI文档与验证enums)

  1. main.go
package main

import (
	"context"

	_ "demo/internal"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"

	"demo/internal"
)

type Response struct {
	Message string `json:"message" dc:"消息提示"`
	Data    any    `json:"data"    dc:"执行结果"`
}

type Body struct {
	Name string `json:"name" v:"required|max-length:100" dc:"姓名"`
	Age  int    `json:"age"  v:"required" dc:"年龄"`
	// TODO:: 如果传递 String ,enums 规则将不会通过,但实际 UnmarshalJSON 是可以完成的,希望能够支持枚举类型
	Gender internal.Gender `json:"gender"  v:"required|enums" dc:"性别:Secret=保密,Female=女生,Male=男生"`
}

type HelloReq struct {
	g.Meta `path:"/" method:"POST" tags:"Test" summary:"enum case"`

	Body
}

type HelloRes struct {
	Body `json:"body" v:"required"`
}

type Hello struct{}

func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
	res = &HelloRes{
		Body: req.Body,
	}
	return
}

func Middleware(r *ghttp.Request) {
	r.Middleware.Next()

	var (
		msg string
		res = r.GetHandlerResponse()
		err = r.GetError()
	)
	if err != nil {
		msg = err.Error()
	} else {
		msg = "OK"
	}
	r.Response.WriteJson(Response{
		Message: msg,
		Data:    res,
	})
}

func main() {
	s := g.Server()
	s.Group("/", func(group *ghttp.RouterGroup) {
		group.Middleware(Middleware)
		group.Bind(
			new(Hello),
		)
	})
	s.SetOpenApiPath("/api.json")
	s.SetSwaggerPath("/swagger")
	s.SetPort(8000)
	s.Run()
}
  1. internal/gender.go
package internal

import (
	"bytes"
	"database/sql/driver"
	"fmt"
	"strconv"
)

type Gender int32

const (
	// GenderSecret 保密
	GenderSecret Gender = 0
	// GenderFemale 女生
	GenderFemale Gender = 1
	// GenderMale   男生
	GenderMale Gender = 2
)

var (
	_GenderValues = map[Gender]string{
		GenderSecret: "Secret",
		GenderFemale: "Female",
		GenderMale:   "Male",
	}
	_GenderName = map[string]Gender{
		"Secret": GenderSecret,
		"Female": GenderFemale,
		"Male":   GenderMale,
	}
)

func (e Gender) String() string {
	if v, ok := _GenderValues[e]; ok {
		return v
	}
	return _GenderValues[GenderSecret]
}

// MarshalJSON 实现接口: [json.Marshaler]
func (e Gender) MarshalJSON() ([]byte, error) {
	return []byte(`"` + e.String() + `"`), nil
}

// UnmarshalJSON 实现接口: [json.Unmarshaler]
func (e *Gender) UnmarshalJSON(b []byte) error {
	str := string(bytes.Trim(b, `"`))
	if v, ok := _GenderName[str]; ok {
		*e = v
		return nil
	}
	return fmt.Errorf("invalid value of enum Gender: %s", str)
}

// Scan 实现接口: [database/sql.Scanner]
//
// 用于将数据库存储值转换为枚举值
func (e *Gender) Scan(src any) error {
	if src == nil {
		*e = 0
		return nil
	}

	switch vv := src.(type) {
	case int64:
		te := Gender(vv)
		if _, ok := _GenderValues[te]; ok {
			*e = te
			return nil
		}
	case []byte:
		if v, err := ParseGenderWithInt(string(vv)); err == nil {
			*e = v
			return nil
		}
	case string:
		if v, err := ParseGenderWithInt(vv); err == nil {
			*e = v
			return nil
		}
	}
	return fmt.Errorf("can not convert %v to Gender, invalid type %T", src, src)
}

// Value 实现接口: [database/sql/driver.Valuer]
//
// 用于将枚举值转换为数据库存储值,转换后的类型必须是数据库支持的类型。
func (e Gender) Value() (driver.Value, error) {
	return int64(e), nil
}

// ParseGenderWithInt 用于转换枚举值
//
//   - 尝试使用字符串转换
//   - 尝试使用整数转换
func ParseGenderWithInt(str string) (Gender, error) {
	if v, ok := _GenderName[str]; ok {
		return v, nil
	}

	if v, err := strconv.ParseInt(str, 10, 64); err != nil {
		return GenderSecret, fmt.Errorf("can not convert %v to Gender, err: %w", str, err)
	} else {
		te := Gender(v)
		if _, ok := _GenderValues[te]; ok {
			return te, nil
		}
	}
	return GenderSecret, fmt.Errorf("invalid value of enum Gender: %s", str)
}
  1. 启动程序
# 1. 生成枚举值类映射
gf gen enums -p internal/boot_enums.go -s .

# 2. 编译与启动程序
go build -o app main.go && ./app

image

image

Describe alternatives you've considered

可以支持自定义接口实现返回枚举值的真实值列表??

Additional

No response

@Issues-translate-bot Issues-translate-bot changed the title net/goai: 在生成 enums 文档与验证 enums 时,是否可以支持自定义实际交互的枚举值?? net/goai: When generating enums documents and validating enums, is it possible to support customizing the actual interactive enumeration values? ? Dec 26, 2024
@gqcn
Copy link
Member

gqcn commented Dec 26, 2024

@shuqingzai 你可以把string的枚举类型和枚举值定义到api下,至于内部的枚举值怎么转换内部可以自行转换。

@Issues-translate-bot
Copy link

Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿


@shuqingzai You can define the enumeration type and enumeration value of string under api. As for how to convert the internal enumeration value, you can convert it yourself.

@shuqingzai
Copy link
Author

@shuqingzai 你可以把string的枚举类型和枚举值定义到api下,至于内部的枚举值怎么转换内部可以自行转换。

@gqcn 谢谢你的回复,但是我不太明白你所说的内部自行转换?可以说清楚点吗?

我希望可以在文档和 enums 验证中有所体现,现在是支持吗?但根据我阅读 GF 生成 enums 的命令源码,似乎不支持

  1. OpenAPI 文档显示的我所定义的枚举值
    image
  2. 在设置 v 标签验证是读取的是我设置的 字符串 而不是 数字 ,因为 GF 在验证 enums 时是读取下面这个表格的,所以希望有方法自定义验证的枚举值
    image

根据你说的,我的理解是你让我定义字符串 enums ,但是存入库时, SQL 的 Scan 和 Value 读取和返回数字,我不知道是否有理解错?

@Issues-translate-bot
Copy link

Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿


@shuqingzai You can define the enumeration type and enumeration value of string under api. As for how to convert the internal enumeration value, you can convert it yourself.

@gqcn Thank you for your reply, but I don’t quite understand what you mean by internal self-conversion? Can you make it clearer?

I hope this is reflected in the documentation and enums validation, is it supported now? But according to my reading of the command source code for GF to generate enums, it seems that it is not supported.

  1. The enumeration value I defined as shown in the OpenAPI documentation
    image
  2. When setting the v tag verification, the string I set is read instead of the number. Because GF reads the following table when verifying enums, so I hope there is a way to customize the enumeration value for verification.
    image

According to what you said, my understanding is that you asked me to define string enums, but when storing it in the database, SQL's Scan and Value read and return numbers. I don't know if I understand it wrong?

@gqcn
Copy link
Member

gqcn commented Dec 26, 2024

@shuqingzai 我的思路如下,你看看有没什么问题或者建议:

  1. api中定义字符串类型的enums,这样文档体现出来的enums也是字符串。
  2. 在入库前将字符串转换为int,我看你已经有映射map了。
  3. 在读取int字段后再自行转换为api的字符串。

@Issues-translate-bot
Copy link

Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿


@shuqingzai My idea is as follows, please see if you have any questions or suggestions:

  1. Define string type enums in api, so that the enums reflected in the document are also strings.
  2. Convert the string to int before storing it in the database. I think you already have a mapping map.
  3. After reading the int field, convert it into an api string.

@shuqingzai
Copy link
Author

shuqingzai commented Dec 27, 2024

@shuqingzai 我的思路如下,你看看有没什么问题或者建议:

  1. api中定义字符串类型的enums,这样文档体现出来的enums也是字符串。
  2. 在入库前将字符串转换为int,我看你已经有映射map了。
  3. 在读取int字段后再自行转换为api的字符串。

@gqcn 按你说的,我理解应该没错,但是这改变了初衷,直接移除了 int 转向 string

  1. 程序或存储交互使用 int -- 可以有效的减少程序所需的内存、数据转换等,当项目中大量使用到 enum 时,这尤为突出
  2. 在开发微服务项目时,希望可以与 Protobuf 行为保持一致,可以参考:EnumerationsProtoJSON Formatjson/#json-options
  3. 仅在特定序列化输出(如: JSON )时,自定义输出显示为 string ,方便外部对接人员查看,很容易见名思意

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants