mirror of
https://gitee.com/johng/gf
synced 2026-07-04 13:02:36 +08:00
feat(tpl): 增加自定义标签支持,优化生成的结构体字段标签配置
This commit is contained in:
308
cmd/gf/internal/cmd/gen/tpl/TAG_USAGE.md
Normal file
308
cmd/gf/internal/cmd/gen/tpl/TAG_USAGE.md
Normal file
@ -0,0 +1,308 @@
|
||||
# 标签配置使用指南
|
||||
|
||||
## 功能概述
|
||||
|
||||
`gf gen tpl` 现在支持灵活的标签配置,可以选择性地为生成的结构体字段添加 `omitempty` 或其他自定义标签。
|
||||
|
||||
## 配置选项一览
|
||||
|
||||
| 选项 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `jsonOmitempty` | bool | false | 为所有字段添加 omitempty |
|
||||
| `jsonOmitemptyAuto` | bool | false | 仅为可空字段自动添加 omitempty |
|
||||
| `withOrmTag` | bool | true | 是否添加 orm 标签 |
|
||||
| `descriptionTag` | bool | false | 是否添加 description 标签 |
|
||||
| `noJsonTag` | bool | false | 是否禁用 JSON 标签 |
|
||||
| `fieldMapping.tags` | map | - | 字段级自定义标签 |
|
||||
|
||||
## 配置方式
|
||||
|
||||
### 1. 全局开关 - `jsonOmitempty`
|
||||
|
||||
为所有字段的 JSON 标签添加 `omitempty`:
|
||||
|
||||
```yaml
|
||||
gfcli:
|
||||
gen:
|
||||
tpl:
|
||||
jsonOmitempty: true
|
||||
```
|
||||
|
||||
**生成结果:**
|
||||
```go
|
||||
type User struct {
|
||||
ID int `json:"id,omitempty" orm:"id" description:"用户ID"`
|
||||
Name string `json:"name,omitempty" orm:"name" description:"用户名"`
|
||||
Email string `json:"email,omitempty" orm:"email" description:"邮箱"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 智能判断 - `jsonOmitemptyAuto` (推荐)
|
||||
|
||||
仅为可空字段自动添加 `omitempty`:
|
||||
|
||||
```yaml
|
||||
gfcli:
|
||||
gen:
|
||||
tpl:
|
||||
jsonOmitemptyAuto: true
|
||||
```
|
||||
|
||||
**假设数据库表结构:**
|
||||
```sql
|
||||
CREATE TABLE user (
|
||||
id INT NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(100) NULL, -- 可空字段
|
||||
age INT NULL -- 可空字段
|
||||
);
|
||||
```
|
||||
|
||||
**生成结果:**
|
||||
```go
|
||||
type User struct {
|
||||
ID int `json:"id" orm:"id" description:"用户ID"`
|
||||
Name string `json:"name" orm:"name" description:"用户名"`
|
||||
Email string `json:"email,omitempty" orm:"email" description:"邮箱"` // 自动添加
|
||||
Age int `json:"age,omitempty" orm:"age" description:"年龄"` // 自动添加
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. ORM 标签控制 - `withOrmTag`
|
||||
|
||||
控制是否添加 orm 标签 (默认启用):
|
||||
|
||||
```yaml
|
||||
gfcli:
|
||||
gen:
|
||||
tpl:
|
||||
withOrmTag: false # 不生成 orm 标签
|
||||
```
|
||||
|
||||
**生成结果:**
|
||||
```go
|
||||
type User struct {
|
||||
ID int `json:"id" description:"用户ID"` // 没有 orm 标签
|
||||
Name string `json:"name" description:"用户名"` // 没有 orm 标签
|
||||
Email string `json:"email" description:"邮箱"` // 没有 orm 标签
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 字段级精确控制 - `fieldMapping`
|
||||
|
||||
针对特定字段自定义标签 (优先级最高):
|
||||
|
||||
```yaml
|
||||
gfcli:
|
||||
gen:
|
||||
tpl:
|
||||
fieldMapping:
|
||||
user.password:
|
||||
type: string
|
||||
tags:
|
||||
json: "-" # 不序列化
|
||||
|
||||
user.email:
|
||||
type: string
|
||||
tags:
|
||||
json: "email,omitempty"
|
||||
validate: "required,email"
|
||||
binding: "required"
|
||||
|
||||
user.status:
|
||||
type: int
|
||||
tags:
|
||||
json: "status,omitempty"
|
||||
validate: "oneof=0 1 2"
|
||||
example: "1"
|
||||
```
|
||||
|
||||
**生成结果:**
|
||||
```go
|
||||
type User struct {
|
||||
Password string `json:"-" orm:"password" description:"密码"`
|
||||
Email string `binding:"required" json:"email,omitempty" validate:"required,email" description:"邮箱"`
|
||||
Status int `example:"1" json:"status,omitempty" validate:"oneof=0 1 2" description:"状态"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见标签示例
|
||||
|
||||
### validate 标签 (gin validator)
|
||||
|
||||
```yaml
|
||||
fieldMapping:
|
||||
user.email:
|
||||
tags:
|
||||
validate: "required,email"
|
||||
|
||||
user.age:
|
||||
tags:
|
||||
validate: "gte=0,lte=150"
|
||||
|
||||
user.password:
|
||||
tags:
|
||||
validate: "required,min=8,max=32"
|
||||
```
|
||||
|
||||
### binding 标签 (gin binding)
|
||||
|
||||
```yaml
|
||||
fieldMapping:
|
||||
user.name:
|
||||
tags:
|
||||
binding: "required"
|
||||
|
||||
user.email:
|
||||
tags:
|
||||
binding: "required,email"
|
||||
```
|
||||
|
||||
### swagger 文档标签
|
||||
|
||||
```yaml
|
||||
fieldMapping:
|
||||
user.id:
|
||||
tags:
|
||||
example: "1"
|
||||
description: "用户唯一标识"
|
||||
|
||||
user.status:
|
||||
tags:
|
||||
example: "1"
|
||||
enums: "0,1,2"
|
||||
```
|
||||
|
||||
### 多个自定义标签组合
|
||||
|
||||
```yaml
|
||||
fieldMapping:
|
||||
user.email:
|
||||
type: string
|
||||
tags:
|
||||
json: "email,omitempty"
|
||||
validate: "required,email"
|
||||
binding: "required"
|
||||
example: "user@example.com"
|
||||
description: "用户邮箱地址"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置优先级
|
||||
|
||||
标签配置的优先级从高到低:
|
||||
|
||||
1. **fieldMapping.tags** - 字段级自定义标签 (优先级最高)
|
||||
2. **jsonOmitempty** - 全局 omitempty 开关
|
||||
3. **jsonOmitemptyAuto** - 智能判断可空字段
|
||||
4. **默认行为** - 不添加 omitempty
|
||||
|
||||
---
|
||||
|
||||
## 完整配置示例
|
||||
|
||||
```yaml
|
||||
gfcli:
|
||||
gen:
|
||||
tpl:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
path: "./output"
|
||||
tplPath: "./templates"
|
||||
jsonCase: "CamelLower"
|
||||
importPrefix: "github.com/example/project"
|
||||
|
||||
# 全局配置
|
||||
jsonOmitemptyAuto: true # 可空字段自动添加 omitempty
|
||||
withOrmTag: true # 添加 orm 标签 (默认)
|
||||
descriptionTag: true # 添加 description 标签
|
||||
|
||||
# 类型映射
|
||||
typeMapping:
|
||||
decimal:
|
||||
type: decimal.Decimal
|
||||
import: github.com/shopspring/decimal
|
||||
|
||||
# 字段级配置
|
||||
fieldMapping:
|
||||
user.password:
|
||||
type: string
|
||||
tags:
|
||||
json: "-"
|
||||
|
||||
user.email:
|
||||
type: string
|
||||
tags:
|
||||
json: "email,omitempty"
|
||||
validate: "required,email"
|
||||
binding: "required"
|
||||
|
||||
order.total_amount:
|
||||
type: decimal.Decimal
|
||||
import: github.com/shopspring/decimal
|
||||
tags:
|
||||
json: "totalAmount,omitempty"
|
||||
validate: "gt=0"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 命令行使用
|
||||
|
||||
```bash
|
||||
# 使用配置文件
|
||||
gf gen tpl
|
||||
|
||||
# 命令行参数
|
||||
gf gen tpl -tp ./templates -p ./output -ja -wo
|
||||
# -ja: jsonOmitemptyAuto
|
||||
# -wo: withOrmTag
|
||||
|
||||
# 组合使用
|
||||
gf gen tpl -l "mysql:root:pass@tcp(127.0.0.1:3306)/db" -tp ./tpl -ja -c -wo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 模板中使用
|
||||
|
||||
如果你需要在自定义模板中使用标签功能:
|
||||
|
||||
```go
|
||||
// entity.tpl
|
||||
type {{.table.NameCaseCamel}} struct { {{range $i,$v := .table.Fields}}
|
||||
{{$v.NameCaseCamel}} {{$v.LocalType}} {{$v.BuildTags $.tagInput}} // {{$v.Comment}}{{end}}
|
||||
}
|
||||
```
|
||||
|
||||
或者分别使用单个标签方法:
|
||||
|
||||
```go
|
||||
type {{.table.NameCaseCamel}} struct { {{range $i,$v := .table.Fields}}
|
||||
{{$v.NameCaseCamel}} {{$v.LocalType}} `json:"{{$v.JsonTag $.tagInput.JsonOmitempty $.tagInput.JsonOmitemptyAuto}}" orm:"{{$v.OrmTag}}"` // {{$v.Comment}}{{end}}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **字段名格式**: `fieldMapping` 中的 key 格式为 `表名.字段名`,使用数据库中的实际字段名 (非驼峰)
|
||||
|
||||
2. **标签顺序**: 自定义标签会按字母顺序排列,确保生成结果一致
|
||||
|
||||
3. **特殊字符**: 如果标签值包含双引号,会自动转义
|
||||
|
||||
4. **DO 文件**: DO 文件 (model/do) 只保留 description 标签,不包含 JSON/ORM 标签
|
||||
|
||||
5. **兼容性**: 与现有的 `typeMapping` 和 `fieldMapping` 完全兼容
|
||||
|
||||
6. **默认值**: `withOrmTag` 默认为 `true`,如果不需要 orm 标签,需要显式设置为 `false`
|
||||
82
cmd/gf/internal/cmd/gen/tpl/testdata/config_example.yaml
vendored
Normal file
82
cmd/gf/internal/cmd/gen/tpl/testdata/config_example.yaml
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
# gf gen tpl 标签配置示例
|
||||
|
||||
gfcli:
|
||||
gen:
|
||||
tpl:
|
||||
# 数据库连接
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
|
||||
# 输出路径
|
||||
path: "./output"
|
||||
|
||||
# 模板路径
|
||||
tplPath: "./testdata"
|
||||
|
||||
# JSON 命名规则
|
||||
jsonCase: "CamelLower"
|
||||
|
||||
# 导入路径前缀
|
||||
importPrefix: "github.com/example/project"
|
||||
|
||||
# ===== 标签配置选项 =====
|
||||
|
||||
# 方式1: 全局为所有字段添加 omitempty
|
||||
jsonOmitempty: false
|
||||
|
||||
# 方式2: 自动为可空字段添加 omitempty (推荐)
|
||||
jsonOmitemptyAuto: true
|
||||
|
||||
# 是否添加 orm 标签 (默认: true)
|
||||
withOrmTag: true
|
||||
|
||||
# 是否添加 description 标签
|
||||
descriptionTag: true
|
||||
|
||||
# 是否禁用 JSON 标签
|
||||
noJsonTag: false
|
||||
|
||||
# ===== 类型映射 =====
|
||||
|
||||
typeMapping:
|
||||
decimal:
|
||||
type: decimal.Decimal
|
||||
import: github.com/shopspring/decimal
|
||||
numeric:
|
||||
type: string
|
||||
|
||||
# ===== 字段级配置 (最灵活) =====
|
||||
|
||||
fieldMapping:
|
||||
# 表名.字段名 格式
|
||||
user.password:
|
||||
type: string
|
||||
tags:
|
||||
json: "-" # 不序列化密码字段
|
||||
|
||||
user.email:
|
||||
type: string
|
||||
tags:
|
||||
json: "email,omitempty"
|
||||
validate: "required,email"
|
||||
binding: "required"
|
||||
|
||||
user.age:
|
||||
type: int
|
||||
tags:
|
||||
json: "age"
|
||||
validate: "gte=0,lte=150"
|
||||
|
||||
user.status:
|
||||
type: int
|
||||
tags:
|
||||
json: "status,omitempty"
|
||||
validate: "oneof=0 1 2"
|
||||
example: "1"
|
||||
|
||||
# 自定义类型示例
|
||||
order.total_amount:
|
||||
type: decimal.Decimal
|
||||
import: github.com/shopspring/decimal
|
||||
tags:
|
||||
json: "totalAmount,omitempty"
|
||||
validate: "gt=0"
|
||||
@ -10,5 +10,5 @@ import ({{range $k,$v := .table.Imports}}
|
||||
{{end}}
|
||||
// {{.table.NameCaseCamel}} is the golang structure for table {{.table.Name}}.
|
||||
type {{.table.NameCaseCamel}} struct { {{range $i,$v := .table.Fields}}
|
||||
{{$v.NameCaseCamel}} {{$v.LocalType}} `json:"{{$v.NameJsonCase}}" orm:"{{$v.Name}}" description:"{{$v.Comment}}"` // {{$v.Comment}}{{end}}
|
||||
{{$v.NameCaseCamel}} {{$v.LocalType}} {{$v.BuildTags $.tagInput}} // {{$v.Comment}}{{end}}
|
||||
}
|
||||
|
||||
@ -61,6 +61,9 @@ CONFIGURATION SUPPORT
|
||||
table_name.field_name:
|
||||
type: decimal.Decimal
|
||||
import: github.com/shopspring/decimal
|
||||
tags:
|
||||
json: "field_name,omitempty"
|
||||
validate: "required"
|
||||
`
|
||||
CGenTplBriefPath = `directory path for generated files`
|
||||
CGenTplBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
|
||||
@ -105,6 +108,9 @@ generated json tag case for model struct, cases are as follows:
|
||||
CGenTplBriefTplDaoInternalPath = `template file path for dao internal file`
|
||||
CGenTplBriefTplDaoDoPathPath = `template file path for dao do file`
|
||||
CGenTplBriefTplDaoEntityPath = `template file path for dao entity file`
|
||||
CGenTplBriefJsonOmitempty = `add omitempty to all json tags`
|
||||
CGenTplBriefJsonOmitemptyAuto = `automatically add omitempty to json tags for nullable fields`
|
||||
CGenTplBriefWithOrmTag = `add orm tag for entity fields`
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -143,6 +149,9 @@ func init() {
|
||||
`CGenTplBriefTplDaoInternalPath`: CGenTplBriefTplDaoInternalPath,
|
||||
`CGenTplBriefTplDaoDoPathPath`: CGenTplBriefTplDaoDoPathPath,
|
||||
`CGenTplBriefTplDaoEntityPath`: CGenTplBriefTplDaoEntityPath,
|
||||
`CGenTplBriefJsonOmitempty`: CGenTplBriefJsonOmitempty,
|
||||
`CGenTplBriefJsonOmitemptyAuto`: CGenTplBriefJsonOmitemptyAuto,
|
||||
`CGenTplBriefWithOrmTag`: CGenTplBriefWithOrmTag,
|
||||
})
|
||||
}
|
||||
|
||||
@ -170,22 +179,26 @@ type (
|
||||
// TplDaoInternalPath string `name:"tplDaoInternalPath" short:"t2" brief:"{CGenTplBriefTplDaoInternalPath}"`
|
||||
// TplDaoDoPath string `name:"tplDaoDoPath" short:"t3" brief:"{CGenTplBriefTplDaoDoPathPath}"`
|
||||
// TplDaoEntityPath string `name:"tplDaoEntityPath" short:"t4" brief:"{CGenTplBriefTplDaoEntityPath}"`
|
||||
StdTime bool `name:"stdTime" short:"s" brief:"{CGenTplBriefStdTime}" orphan:"true"`
|
||||
WithTime bool `name:"withTime" short:"w" brief:"{CGenTplBriefWithTime}" orphan:"true"`
|
||||
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{CGenTplBriefGJsonSupport}" orphan:"true"`
|
||||
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{CGenTplBriefOverwriteDao}" orphan:"true"`
|
||||
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{CGenTplBriefDescriptionTag}" orphan:"true"`
|
||||
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenTplBriefNoJsonTag}" orphan:"true"`
|
||||
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenTplBriefNoModelComment}" orphan:"true"`
|
||||
Clear bool `name:"clear" short:"a" brief:"{CGenTplBriefClear}" orphan:"true"`
|
||||
TypeMapping map[string]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenTplBriefTypeMapping}" orphan:"true"`
|
||||
FieldMapping map[string]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenTplBriefFieldMapping}" orphan:"true"`
|
||||
StdTime bool `name:"stdTime" short:"s" brief:"{CGenTplBriefStdTime}" orphan:"true"`
|
||||
WithTime bool `name:"withTime" short:"w" brief:"{CGenTplBriefWithTime}" orphan:"true"`
|
||||
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{CGenTplBriefGJsonSupport}" orphan:"true"`
|
||||
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{CGenTplBriefOverwriteDao}" orphan:"true"`
|
||||
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{CGenTplBriefDescriptionTag}" orphan:"true"`
|
||||
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenTplBriefNoJsonTag}" orphan:"true"`
|
||||
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenTplBriefNoModelComment}" orphan:"true"`
|
||||
Clear bool `name:"clear" short:"a" brief:"{CGenTplBriefClear}" orphan:"true"`
|
||||
JsonOmitempty bool `name:"jsonOmitempty" short:"jo" brief:"{CGenTplBriefJsonOmitempty}" orphan:"true"`
|
||||
JsonOmitemptyAuto bool `name:"jsonOmitemptyAuto" short:"ja" brief:"{CGenTplBriefJsonOmitemptyAuto}" orphan:"true"`
|
||||
WithOrmTag bool `name:"withOrmTag" short:"wo" brief:"{CGenTplBriefWithOrmTag}" orphan:"false" d:"false"`
|
||||
TypeMapping map[string]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenTplBriefTypeMapping}" orphan:"true"`
|
||||
FieldMapping map[string]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenTplBriefFieldMapping}" orphan:"true"`
|
||||
}
|
||||
CGenTplOutput struct{}
|
||||
|
||||
CustomAttributeType struct {
|
||||
Type string `brief:"custom attribute type name"`
|
||||
Import string `brief:"custom import for this type"`
|
||||
Type string `brief:"custom attribute type name"`
|
||||
Import string `brief:"custom import for this type"`
|
||||
Tags map[string]string `brief:"custom tags for this field, e.g. json, validate, binding"`
|
||||
}
|
||||
)
|
||||
|
||||
@ -304,10 +317,19 @@ func (c CGenTpl) Tpl(ctx context.Context, in CGenTplInput) (out *CGenTplOutput,
|
||||
view := gview.New()
|
||||
|
||||
for _, table := range tables {
|
||||
// Create tag input for this table
|
||||
tagInput := TagBuildInput{
|
||||
NoJsonTag: in.NoJsonTag,
|
||||
JsonOmitempty: in.JsonOmitempty,
|
||||
JsonOmitemptyAuto: in.JsonOmitemptyAuto,
|
||||
WithOrmTag: in.WithOrmTag,
|
||||
DescriptionTag: in.DescriptionTag,
|
||||
}
|
||||
|
||||
tplData := g.Map{
|
||||
"table": table,
|
||||
"tables": tables,
|
||||
"table": table,
|
||||
"tables": tables,
|
||||
"tagInput": tagInput,
|
||||
}
|
||||
fmt.Println(table.FieldsJsonStr(in.JsonCase))
|
||||
for _, tpl := range tplList {
|
||||
|
||||
@ -2,6 +2,8 @@ package tpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
@ -12,8 +14,9 @@ import (
|
||||
// TableField description
|
||||
type TableField struct {
|
||||
gdb.TableField
|
||||
LocalType string
|
||||
JsonCase string
|
||||
LocalType string
|
||||
JsonCase string
|
||||
CustomTags map[string]string // 自定义标签
|
||||
}
|
||||
|
||||
type TableFields []*TableField
|
||||
@ -144,3 +147,110 @@ func (f *TableField) NameCaseSnake() string {
|
||||
func (f *TableField) NameCaseKebabScreaming() string {
|
||||
return gstr.CaseKebabScreaming(f.Name)
|
||||
}
|
||||
|
||||
// IsNullable returns whether the field is nullable
|
||||
func (f *TableField) IsNullable() bool {
|
||||
return f.Null
|
||||
}
|
||||
|
||||
// JsonTag generates json tag for the field
|
||||
func (f *TableField) JsonTag(omitempty bool, omitemptyAuto bool) string {
|
||||
if f.CustomTags != nil {
|
||||
if jsonTag, ok := f.CustomTags["json"]; ok {
|
||||
return jsonTag
|
||||
}
|
||||
}
|
||||
|
||||
name := f.NameJsonCase()
|
||||
if omitempty || (omitemptyAuto && f.IsNullable()) {
|
||||
return name + ",omitempty"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// OrmTag generates orm tag for the field
|
||||
func (f *TableField) OrmTag() string {
|
||||
if f.CustomTags != nil {
|
||||
if ormTag, ok := f.CustomTags["orm"]; ok {
|
||||
return ormTag
|
||||
}
|
||||
}
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// DescriptionTag generates description tag for the field
|
||||
func (f *TableField) DescriptionTag() string {
|
||||
if f.CustomTags != nil {
|
||||
if descTag, ok := f.CustomTags["description"]; ok {
|
||||
return descTag
|
||||
}
|
||||
}
|
||||
// 转义双引号
|
||||
comment := strings.ReplaceAll(f.Comment, `"`, `\"`)
|
||||
return comment
|
||||
}
|
||||
|
||||
// CustomTag returns custom tag value by name
|
||||
func (f *TableField) CustomTag(name string) string {
|
||||
if f.CustomTags == nil {
|
||||
return ""
|
||||
}
|
||||
return f.CustomTags[name]
|
||||
}
|
||||
|
||||
// BuildTags builds all tags for the field
|
||||
func (f *TableField) BuildTags(in TagBuildInput) string {
|
||||
var tags []string
|
||||
|
||||
// JSON tag
|
||||
if !in.NoJsonTag {
|
||||
jsonValue := f.JsonTag(in.JsonOmitempty, in.JsonOmitemptyAuto)
|
||||
tags = append(tags, fmt.Sprintf(`json:"%s"`, jsonValue))
|
||||
}
|
||||
|
||||
// ORM tag
|
||||
if in.WithOrmTag {
|
||||
ormValue := f.OrmTag()
|
||||
tags = append(tags, fmt.Sprintf(`orm:"%s"`, ormValue))
|
||||
}
|
||||
|
||||
// Description tag
|
||||
if in.DescriptionTag {
|
||||
descValue := f.DescriptionTag()
|
||||
tags = append(tags, fmt.Sprintf(`description:"%s"`, descValue))
|
||||
}
|
||||
|
||||
// Custom tags from CustomTags map
|
||||
if f.CustomTags != nil {
|
||||
// 按字母顺序遍历,确保输出稳定
|
||||
var keys []string
|
||||
for k := range f.CustomTags {
|
||||
// 跳过已处理的标准标签
|
||||
if k == "json" || k == "orm" || k == "description" {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
v := f.CustomTags[k]
|
||||
tags = append(tags, fmt.Sprintf(`%s:"%s"`, k, v))
|
||||
}
|
||||
}
|
||||
|
||||
if len(tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return "`" + strings.Join(tags, " ") + "`"
|
||||
}
|
||||
|
||||
// TagBuildInput for building tags
|
||||
type TagBuildInput struct {
|
||||
NoJsonTag bool
|
||||
JsonOmitempty bool
|
||||
JsonOmitemptyAuto bool
|
||||
WithOrmTag bool
|
||||
DescriptionTag bool
|
||||
}
|
||||
|
||||
@ -100,7 +100,10 @@ func (t *Table) toTableFields(in CGenTplInput) {
|
||||
field := &TableField{
|
||||
TableField: *v,
|
||||
JsonCase: in.JsonCase,
|
||||
CustomTags: make(map[string]string),
|
||||
}
|
||||
|
||||
// 设置字段类型
|
||||
appendImport := field.GetLocalTypeName(context.Background(), t.db, Input{
|
||||
TypeMapping: in.TypeMapping,
|
||||
FieldMapping: in.FieldMapping,
|
||||
@ -110,6 +113,18 @@ func (t *Table) toTableFields(in CGenTplInput) {
|
||||
if appendImport != "" {
|
||||
t.Imports[appendImport] = struct{}{}
|
||||
}
|
||||
|
||||
// 从 FieldMapping 中提取自定义标签
|
||||
if in.FieldMapping != nil {
|
||||
if fieldMapping, ok := in.FieldMapping[v.Name]; ok {
|
||||
if fieldMapping.Tags != nil {
|
||||
for tagName, tagValue := range fieldMapping.Tags {
|
||||
field.CustomTags[tagName] = tagValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Fields[v.Index] = field
|
||||
}
|
||||
}
|
||||
@ -144,6 +159,16 @@ func (t *Table) FieldsJsonStr(caseName string) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// TagInput holds input for tag generation
|
||||
type TagInput struct {
|
||||
in CGenTplInput
|
||||
}
|
||||
|
||||
// GetTagInput returns TagInput for template usage
|
||||
func (t *Table) GetTagInput(in CGenTplInput) TagInput {
|
||||
return TagInput{in: in}
|
||||
}
|
||||
|
||||
// GetTables 获取数据库表结构信息
|
||||
func (t *TplObj) GetTables() (Tables, error) {
|
||||
nameList, err := t.db.Tables(t.ctx)
|
||||
|
||||
Reference in New Issue
Block a user