This commit is contained in:
hailaz
2025-01-25 18:01:25 +08:00
parent 9f0de3d535
commit 3a2a73c786
7 changed files with 403 additions and 132 deletions

View File

@ -3,6 +3,14 @@
## 功能概述
基于数据库表结构通过自定义模板生成Go代码的工具。
## 功能设计
生成流程:
1. 读取数据库表结构
2. 解析出表结构信息,包括表名、表注释、字段列表
3. 根据规则裁切表数据,生成模板数据
4. 根据模板生成代码
## 命令参数设计
```shell

View File

@ -5,22 +5,22 @@
package dao
import (
"{{.table.PackageName}}/internal/cmd/gen/tpl/output/dao/internal"
"{{.table.PackageName}}/internal"
)
// internal{{.table.CaseCamel}}Dao is internal type for wrapping internal DAO implements.
type internal{{.table.CaseCamel}}Dao = *internal.{{.table.CaseCamel}}Dao
// internal{{.table.NameCaseCamel}}Dao is internal type for wrapping internal DAO implements.
type internal{{.table.NameCaseCamel}}Dao = *internal.{{.table.NameCaseCamel}}Dao
// {{.table.CaseCamelLower}}Dao is the data access object for table {{.table.Name}}.
// {{.table.NameCaseCamelLower}}Dao is the data access object for table {{.table.Name}}.
// You can define custom methods on it to extend its functionality as you wish.
type {{.table.CaseCamelLower}}Dao struct {
internal{{.table.CaseCamel}}Dao
type {{.table.NameCaseCamelLower}}Dao struct {
internal{{.table.NameCaseCamel}}Dao
}
var (
// {{.table.CaseCamel}} is globally public accessible object for table {{.table.Name}} operations.
{{.table.CaseCamel}} = {{.table.CaseCamelLower}}Dao{
internal.New{{.table.CaseCamel}}Dao(),
// {{.table.NameCaseCamel}} is globally public accessible object for table {{.table.Name}} operations.
{{.table.NameCaseCamel}} = {{.table.NameCaseCamelLower}}Dao{
internal.New{{.table.NameCaseCamel}}Dao(),
}
)

View File

@ -7,54 +7,54 @@ import (
"github.com/gogf/gf/v2/frame/g"
)
// {{.table.CaseCamel}}Dao is the data access object for table {{.table.Name}}.
type {{.table.CaseCamel}}Dao struct {
// {{.table.NameCaseCamel}}Dao is the data access object for table {{.table.Name}}.
type {{.table.NameCaseCamel}}Dao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns {{.table.CaseCamel}}Columns // columns contains all the column names of Table for convenient usage.
columns {{.table.NameCaseCamel}}Columns // columns contains all the column names of Table for convenient usage.
}
// {{.table.CaseCamel}}Columns defines and stores column names for table {{.table.Name}}.
type {{.table.CaseCamel}}Columns struct { {{range $i,$v := .table.Fields}}
{{$v.NameCase "Camel"}} string // {{$v.Comment}}{{end}}
// {{.table.NameCaseCamel}}Columns defines and stores column names for table {{.table.Name}}.
type {{.table.NameCaseCamel}}Columns struct { {{range $i,$v := .table.Fields}}
{{$v.NameCaseCamel}} string // {{$v.Comment}}{{end}}
}
// {{.table.CaseCamelLower}}Columns holds the columns for table {{.table.Name}}.
var {{.table.CaseCamelLower}}Columns = {{.table.CaseCamel}}Columns{ {{range $i,$v := .table.Fields}}
{{$v.CaseCamel}}: "{{$v.CaseCamelLower}}",{{end}}
// {{.table.NameCaseCamelLower}}Columns holds the columns for table {{.table.Name}}.
var {{.table.NameCaseCamelLower}}Columns = {{.table.NameCaseCamel}}Columns{ {{range $i,$v := .table.Fields}}
{{$v.NameCaseCamel}}: "{{$v.NameCaseCamelLower}}",{{end}}
}
// New{{.table.CaseCamel}}Dao creates and returns a new DAO object for table data access.
func New{{.table.CaseCamel}}Dao() *{{.table.CaseCamel}}Dao {
return &{{.table.CaseCamel}}Dao{
// New{{.table.NameCaseCamel}}Dao creates and returns a new DAO object for table data access.
func New{{.table.NameCaseCamel}}Dao() *{{.table.NameCaseCamel}}Dao {
return &{{.table.NameCaseCamel}}Dao{
group: "test",
table: "{{.table.Name}}",
columns: {{.table.CaseCamelLower}}Columns,
columns: {{.table.NameCaseCamelLower}}Columns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *{{.table.CaseCamel}}Dao) DB() gdb.DB {
func (dao *{{.table.NameCaseCamel}}Dao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *{{.table.CaseCamel}}Dao) Table() string {
func (dao *{{.table.NameCaseCamel}}Dao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *{{.table.CaseCamel}}Dao) Columns() {{.table.CaseCamel}}Columns {
func (dao *{{.table.NameCaseCamel}}Dao) Columns() {{.table.NameCaseCamel}}Columns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *{{.table.CaseCamel}}Dao) Group() string {
func (dao *{{.table.NameCaseCamel}}Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *{{.table.CaseCamel}}Dao) Ctx(ctx context.Context) *gdb.Model {
func (dao *{{.table.NameCaseCamel}}Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
@ -64,6 +64,6 @@ func (dao *{{.table.CaseCamel}}Dao) Ctx(ctx context.Context) *gdb.Model {
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *{{.table.CaseCamel}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
func (dao *{{.table.NameCaseCamel}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@ -31,35 +31,118 @@ const (
CGenTplBrief = `automatically generate template files`
CGenTplEg = `
gf gen tpl
gf gen tpl -t default -p ./template
gf gen tpl -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
gf gen tpl -p ./model -g user-center -t user,user_detail,user_login
gf gen tpl -r user_
`
CGenTplAd = `
CONFIGURATION SUPPORT
Options are also supported by configuration file.
It's suggested using configuration file instead of command line arguments.
The configuration node name is "gfcli.gen.tpl" which also supports multiple databases, for example(config.yaml):
It's suggested using configuration file instead of command line arguments making producing.
The configuration node name is "gfcli.gen.dao", which also supports multiple databases, for example(config.yaml):
gfcli:
gen:
tpl:
dao:
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables: "order,products"
jsonCase: "CamelLower"
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
path: "./my-app"
prefix: "primary_"
tables: "user, userDetail"
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
path: "./my-app"
prefix: "primary_"
tables: "user, userDetail"
typeMapping:
decimal:
type: decimal.Decimal
import: github.com/shopspring/decimal
numeric:
type: string
fieldMapping:
table_name.field_name:
type: decimal.Decimal
import: github.com/shopspring/decimal
`
CGenTplBriefPath = `output directory path (default: "./template")`
CGenTplBriefPath = `directory path for generated files`
CGenTplBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
CGenTplBriefTables = `generate models only for given tables, multiple table names separated with ','`
CGenTplBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','`
CGenTplBriefPrefix = `add prefix for all table of specified link/database tables`
CGenTplBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
CGenTplBriefRemoveFieldPrefix = `remove specified prefix of the field, multiple prefix separated with ','`
CGenTplBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables`
CGenTplBriefWithTime = `add created time for auto produced go files`
CGenTplBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables`
CGenTplBriefImportPrefix = `custom import prefix for generated go files`
CGenTplBriefDaoPath = `directory path for storing generated dao files under path`
CGenTplBriefDoPath = `directory path for storing generated do files under path`
CGenTplBriefEntityPath = `directory path for storing generated entity files under path`
CGenTplBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder`
CGenTplBriefModelFile = `custom file name for storing generated model content`
CGenTplBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default`
CGenTplBriefDescriptionTag = `add comment to description tag for each field`
CGenTplBriefNoJsonTag = `no json tag will be added for each field`
CGenTplBriefNoModelComment = `no model comment will be added for each field`
CGenTplBriefClear = `delete all generated go files that do not exist in database`
CGenTplBriefTypeMapping = `custom local type mapping for generated struct attributes relevant to fields of table`
CGenTplBriefFieldMapping = `custom local type mapping for generated struct attributes relevant to specific fields of table`
CGenTplBriefGroup = `
specifying the configuration group name of database for generated ORM instance,
it's not necessary and the default value is "default"
`
CGenTplBriefJsonCase = `
generated json tag case for model struct, cases are as follows:
| Case | Example |
|---------------- |--------------------|
| Camel | AnyKindOfString |
| CamelLower | anyKindOfString | default
| Snake | any_kind_of_string |
| SnakeScreaming | ANY_KIND_OF_STRING |
| SnakeFirstUpper | rgb_code_md5 |
| Kebab | any-kind-of-string |
| KebabScreaming | ANY-KIND-OF-STRING |
`
CGenTplBriefTplDaoIndexPath = `template file path for dao index file`
CGenTplBriefTplDaoInternalPath = `template file path for dao internal file`
CGenTplBriefTplDaoDoPathPath = `template file path for dao do file`
CGenTplBriefTplDaoEntityPath = `template file path for dao entity file`
)
func init() {
gtag.Sets(g.MapStrStr{
`CGenTplConfig`: CGenTplConfig,
`CGenTplUsage`: CGenTplUsage,
`CGenTplBrief`: CGenTplBrief,
`CGenTplEg`: CGenTplEg,
`CGenTplAd`: CGenTplAd,
`CGenTplConfig`: CGenTplConfig,
`CGenTplUsage`: CGenTplUsage,
`CGenTplBrief`: CGenTplBrief,
`CGenTplEg`: CGenTplEg,
`CGenTplAd`: CGenTplAd,
`CGenTplBriefPath`: CGenTplBriefPath,
`CGenTplBriefLink`: CGenTplBriefLink,
`CGenTplBriefTables`: CGenTplBriefTables,
`CGenTplBriefTablesEx`: CGenTplBriefTablesEx,
`CGenTplBriefPrefix`: CGenTplBriefPrefix,
`CGenTplBriefRemovePrefix`: CGenTplBriefRemovePrefix,
`CGenTplBriefRemoveFieldPrefix`: CGenTplBriefRemoveFieldPrefix,
`CGenTplBriefStdTime`: CGenTplBriefStdTime,
`CGenTplBriefWithTime`: CGenTplBriefWithTime,
`CGenTplBriefDaoPath`: CGenTplBriefDaoPath,
`CGenTplBriefDoPath`: CGenTplBriefDoPath,
`CGenTplBriefEntityPath`: CGenTplBriefEntityPath,
`CGenTplBriefGJsonSupport`: CGenTplBriefGJsonSupport,
`CGenTplBriefImportPrefix`: CGenTplBriefImportPrefix,
`CGenTplBriefOverwriteDao`: CGenTplBriefOverwriteDao,
`CGenTplBriefModelFile`: CGenTplBriefModelFile,
`CGenTplBriefModelFileForDao`: CGenTplBriefModelFileForDao,
`CGenTplBriefDescriptionTag`: CGenTplBriefDescriptionTag,
`CGenTplBriefNoJsonTag`: CGenTplBriefNoJsonTag,
`CGenTplBriefNoModelComment`: CGenTplBriefNoModelComment,
`CGenTplBriefClear`: CGenTplBriefClear,
`CGenTplBriefTypeMapping`: CGenTplBriefTypeMapping,
`CGenTplBriefFieldMapping`: CGenTplBriefFieldMapping,
`CGenTplBriefGroup`: CGenTplBriefGroup,
`CGenTplBriefJsonCase`: CGenTplBriefJsonCase,
`CGenTplBriefTplDaoIndexPath`: CGenTplBriefTplDaoIndexPath,
`CGenTplBriefTplDaoInternalPath`: CGenTplBriefTplDaoInternalPath,
`CGenTplBriefTplDaoDoPathPath`: CGenTplBriefTplDaoDoPathPath,
`CGenTplBriefTplDaoEntityPath`: CGenTplBriefTplDaoEntityPath,
})
}
@ -69,32 +152,34 @@ type (
g.Meta `name:"tpl" config:"{CGenTplConfig}" usage:"{CGenTplUsage}" brief:"{CGenTplBrief}" eg:"{CGenTplEg}" ad:"{CGenTplAd}"`
Path string `name:"path" short:"p" brief:"{CGenTplBriefPath}" d:"./output"`
TplPath string `name:"tplPath" short:"tp" brief:"模板目录路径"`
Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"`
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
Group string `name:"group" short:"g" brief:"{CGenDaoBriefGroup}" d:"default"`
Prefix string `name:"prefix" short:"f" brief:"{CGenDaoBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenDaoBriefRemovePrefix}"`
RemoveFieldPrefix string `name:"removeFieldPrefix" short:"rf" brief:"{CGenDaoBriefRemoveFieldPrefix}"`
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenDaoBriefJsonCase}" d:"CamelLower"`
ImportPrefix string `name:"importPrefix" short:"i" brief:"{CGenDaoBriefImportPrefix}"`
// DaoPath string `name:"daoPath" short:"d" brief:"{CGenDaoBriefDaoPath}" d:"dao"`
// DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"`
// EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"`
// TplDaoIndexPath string `name:"tplDaoIndexPath" short:"t1" brief:"{CGenDaoBriefTplDaoIndexPath}"`
// TplDaoInternalPath string `name:"tplDaoInternalPath" short:"t2" brief:"{CGenDaoBriefTplDaoInternalPath}"`
// TplDaoDoPath string `name:"tplDaoDoPath" short:"t3" brief:"{CGenDaoBriefTplDaoDoPathPath}"`
// TplDaoEntityPath string `name:"tplDaoEntityPath" short:"t4" brief:"{CGenDaoBriefTplDaoEntityPath}"`
StdTime bool `name:"stdTime" short:"s" brief:"{CGenDaoBriefStdTime}" orphan:"true"`
WithTime bool `name:"withTime" short:"w" brief:"{CGenDaoBriefWithTime}" orphan:"true"`
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{CGenDaoBriefGJsonSupport}" orphan:"true"`
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{CGenDaoBriefOverwriteDao}" orphan:"true"`
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{CGenDaoBriefDescriptionTag}" orphan:"true"`
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenDaoBriefNoJsonTag}" orphan:"true"`
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenDaoBriefNoModelComment}" orphan:"true"`
Clear bool `name:"clear" short:"a" brief:"{CGenDaoBriefClear}" orphan:"true"`
TypeMapping map[string]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"`
FieldMapping map[string]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenDaoBriefFieldMapping}" orphan:"true"`
Link string `name:"link" short:"l" brief:"{CGenTplBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{CGenTplBriefTables}"`
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenTplBriefTablesEx}"`
Group string `name:"group" short:"g" brief:"{CGenTplBriefGroup}" d:"default"`
Prefix string `name:"prefix" short:"f" brief:"{CGenTplBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenTplBriefRemovePrefix}"`
RemoveFieldPrefix string `name:"removeFieldPrefix" short:"rf" brief:"{CGenTplBriefRemoveFieldPrefix}"`
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenTplBriefJsonCase}" d:"CamelLower"`
ImportPrefix string `name:"importPrefix" short:"i" brief:"{CGenTplBriefImportPrefix}"`
// 新增过滤参数
TableNamePattern string `name:"tableNamePattern" short:"tn" brief:"表名匹配模式,支持通配符"`
// DaoPath string `name:"daoPath" short:"d" brief:"{CGenTplBriefDaoPath}" d:"dao"`
// DoPath string `name:"doPath" short:"o" brief:"{CGenTplBriefDoPath}" d:"model/do"`
// EntityPath string `name:"entityPath" short:"e" brief:"{CGenTplBriefEntityPath}" d:"model/entity"`
// TplDaoIndexPath string `name:"tplDaoIndexPath" short:"t1" brief:"{CGenTplBriefTplDaoIndexPath}"`
// 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"`
}
CGenTplOutput struct{}
@ -125,23 +210,47 @@ type (
DBFieldTypeName = string
)
// GetTables 获取数据库表结构信息
func GetTables(ctx context.Context, db gdb.DB) Tables {
tablesName, err := db.Tables(ctx)
// TplObj description
type TplObj struct {
ctx context.Context
in CGenTplInput
db gdb.DB
TplPathAbs string
}
// NewTpl description
//
// createTime: 2025-01-25 16:36:43
func NewTpl(ctx context.Context, in CGenTplInput) (*TplObj, error) {
db, err := in.GetDB()
if err != nil {
panic(err)
return nil, err
}
fmt.Println(tablesName)
tables := make(Tables, 0)
for _, v := range tablesName {
t, err := NewTable(ctx, db, v)
if err != nil {
panic(err)
}
t.SortFields(true)
tables = append(tables, t)
return &TplObj{
ctx: ctx,
in: in,
db: db,
TplPathAbs: gfile.Abs(in.TplPath),
}, nil
}
func (t *TplObj) ShowParams() {
mlog.Debug("tplPath:", t.in.TplPath)
mlog.Debug("output:", t.in.Path)
}
func (t *TplObj) Format() {
utils.GoFmt(t.in.Path)
}
// GetTplFileList description
//
// createTime: 2025-01-25 16:43:06
func (t *TplObj) GetTplFileList() ([]string, error) {
tplList, err := gfile.ScanDirFile(t.TplPathAbs, "*.tpl", true)
if err != nil {
return nil, err
}
return tables
return tplList, nil
}
func (c CGenTpl) Tpl(ctx context.Context, in CGenTplInput) (out *CGenTplOutput, err error) {
@ -163,17 +272,12 @@ func (c CGenTpl) Tpl(ctx context.Context, in CGenTplInput) (out *CGenTplOutput,
}
}
db, err := in.GetDB()
tplObj, err := NewTpl(ctx, in)
if err != nil {
return nil, err
}
outputDir := in.Path
tplRootDir := in.TplPath
mlog.Print("output directory:", outputDir)
mlog.Print("template directory:", tplRootDir)
tplRootDir = gfile.Abs(tplRootDir)
fmt.Println(tplRootDir)
tplList, err := gfile.ScanDirFile(tplRootDir, "*.tpl", true)
tplList, err := tplObj.GetTplFileList()
if err != nil {
panic(err)
}
@ -182,38 +286,64 @@ func (c CGenTpl) Tpl(ctx context.Context, in CGenTplInput) (out *CGenTplOutput,
fmt.Printf("%#v\n", Table{})
fmt.Printf("%#v\n", TableField{})
tables := GetTables(ctx, db)
tables, err := tplObj.GetTables()
if err != nil {
return nil, err
}
view := gview.New()
for _, table := range tables {
table.PackageName = "github.com/gogf/gf/cmd/gf/v2"
tplData := g.Map{
"table": table,
"tables": tables,
}
fmt.Println(table.FieldsJsonStr("Snake"))
fmt.Println(table.FieldsJsonStr(in.JsonCase))
for _, tpl := range tplList {
tplDir := gfile.Dir(tpl)
mlog.Print("generating template file:", tpl)
// 相对路径
relativePath := strings.TrimPrefix(gfile.Dir(tpl), tplObj.TplPathAbs)
mlog.Print("relativePath:", relativePath)
table.PackageName = filepath.ToSlash(filepath.Join(in.ImportPrefix, relativePath))
filePath := filepath.Join(relativePath, table.FileName())
mlog.Print("generating table filePath:", filePath)
res, err := view.Parse(ctx, tpl, tplData)
if err != nil {
panic(err)
mlog.Fatal(err)
}
// fmt.Println(res, err)
filePath := filepath.FromSlash(fmt.Sprintf(outputDir+"%s/%s.go", strings.TrimPrefix(tplDir, tplRootDir), table.Name))
err = gfile.PutContents(filePath, res)
fmt.Println(len(res), err)
err = tplObj.SaveFile(ctx, filePath, res)
if err != nil {
panic(err)
}
utils.GoFmt(filePath)
}
}
// Format generated files
tplObj.Format()
mlog.Print("template files generated successfully!")
return &CGenTplOutput{}, nil
}
// SaveFile description
//
// createTime: 2025-01-25 17:05:25
func (t *TplObj) SaveFile(ctx context.Context, path, content string) error {
mlog.Print("saving file:", path)
path = filepath.Join(t.in.Path, path)
mlog.Print("saving file:", path)
path = filepath.FromSlash(path)
mlog.Print("saving file:", path)
if err := gfile.PutContents(path, content); err != nil {
return err
}
return nil
}
// GetDB description
//
// createTime: 2025-01-24 16:58:46

View File

@ -13,12 +13,18 @@ import (
type TableField struct {
gdb.TableField
LocalType string
JsonCase string
}
type TableFields []*TableField
func (s TableFields) Len() int { return len(s) }
// Len returns the length of TableFields slice
func (s TableFields) Len() int { return len(s) }
// Swap swaps the elements with indexes i and j
func (s TableFields) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// Less reports whether the element with index i should sort before the element with index j
func (s TableFields) Less(i, j int) bool {
return strings.Compare(s[i].Name, s[j].Name) < 0
}
@ -94,27 +100,34 @@ func (field *TableField) GetLocalTypeName(ctx context.Context, db gdb.DB, in Inp
return
}
// NameCase description
func (f *TableField) NameCase(caseName string) string {
// NameJsonCase description
//
// createTime: 2025-01-25 15:27:01
func (f *TableField) NameJsonCase() string {
return gstr.CaseConvert(f.Name, gstr.CaseTypeMatch(f.JsonCase))
}
// NameCaseConvert 字段名转换
func (f *TableField) NameCaseConvert(caseName string) string {
return gstr.CaseConvert(f.Name, gstr.CaseTypeMatch(caseName))
}
// CaseCamel description
func (f *TableField) CaseCamel() string {
// NameCaseCamel returns the field name in camel case format
func (f *TableField) NameCaseCamel() string {
return gstr.CaseCamel(f.Name)
}
// CaseCamelLower description
func (f *TableField) CaseCamelLower() string {
// NameCaseCamelLower returns the field name in lower camel case format
func (f *TableField) NameCaseCamelLower() string {
return gstr.CaseCamelLower(f.Name)
}
// CaseSnake description
func (f *TableField) CaseSnake() string {
// NameCaseSnake returns the field name in snake case format
func (f *TableField) NameCaseSnake() string {
return gstr.CaseSnake(f.Name)
}
// CaseKebabScreaming description
func (f *TableField) CaseKebabScreaming() string {
// NameCaseKebabScreaming returns the field name in screaming kebab case format
func (f *TableField) NameCaseKebabScreaming() string {
return gstr.CaseKebabScreaming(f.Name)
}

View File

@ -4,7 +4,9 @@ import (
"context"
"encoding/json"
"fmt"
"regexp"
"sort"
"strings"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/text/gstr"
@ -12,12 +14,14 @@ import (
// Table description
type Table struct {
Name string
PackageName string
db gdb.DB
Fields TableFields
FieldsSource map[string]*gdb.TableField
Imports map[string]struct{}
Name string // 表名
OutputName string // 输出表名,用于生成文件名
OutputNameCase string // 输出表名的命名规则
PackageName string
db gdb.DB
Fields TableFields
FieldsSource map[string]*gdb.TableField
Imports map[string]struct{}
}
type Tables []*Table
@ -27,13 +31,14 @@ type Tables []*Table
// createTime: 2023-12-11 16:17:33
//
// author: hailaz
func NewTable(ctx context.Context, db gdb.DB, tableName string) (*Table, error) {
func NewTable(ctx context.Context, db gdb.DB, tableName string, tableOutputName string) (*Table, error) {
fields, err := db.TableFields(ctx, tableName)
if err != nil {
return nil, err
}
table := Table{
Name: tableName,
OutputName: tableOutputName,
FieldsSource: fields,
db: db,
Imports: make(map[string]struct{}),
@ -51,26 +56,36 @@ func (t *Table) Show() string {
return fmt.Sprintf("table name is %s", t.Name)
}
// CaseCamel description
func (t *Table) CaseCamel() string {
// NameCase description
func (t *Table) NameCase() string {
return gstr.CaseConvert(t.Name, gstr.CaseTypeMatch(t.OutputNameCase))
}
// NameCaseCamel description
func (t *Table) NameCaseCamel() string {
return gstr.CaseCamel(t.Name)
}
// CaseCamelLower description
func (t *Table) CaseCamelLower() string {
// NameCaseCamelLower description
func (t *Table) NameCaseCamelLower() string {
return gstr.CaseCamelLower(t.Name)
}
// CaseSnake description
func (t *Table) CaseSnake() string {
// NameCaseSnake description
func (t *Table) NameCaseSnake() string {
return gstr.CaseSnake(t.Name)
}
// CaseKebabScreaming description
func (t *Table) CaseKebabScreaming() string {
// NameCaseKebabScreaming description
func (t *Table) NameCaseKebabScreaming() string {
return gstr.CaseKebabScreaming(t.Name)
}
// FileName description
func (t *Table) FileName() string {
return gstr.CaseConvert(t.OutputName, gstr.CaseTypeMatch(t.OutputNameCase)) + ".go"
}
// toTableFields description
//
// createTime: 2023-10-23 17:22:40
@ -96,7 +111,7 @@ func (t *Table) toTableFields() {
}
}
// SortFields description
// SortFields 字段排序
//
// createTime: 2023-10-23 17:18:22
//
@ -109,7 +124,7 @@ func (t *Table) SortFields(isReverse bool) {
}
}
// JsonStr description
// FieldsJsonStr 表字段json字符串
//
// createTime: 2023-10-23 17:29:39
//
@ -117,7 +132,7 @@ func (t *Table) SortFields(isReverse bool) {
func (t *Table) FieldsJsonStr(caseName string) string {
mapStr := make(map[string]interface{}, len(t.Fields))
for _, v := range t.Fields {
mapStr[v.NameCase(caseName)] = v.Default
mapStr[v.NameCaseConvert(caseName)] = v.Default
}
b, err := json.MarshalIndent(mapStr, "", " ")
if err != nil {
@ -125,3 +140,106 @@ func (t *Table) FieldsJsonStr(caseName string) string {
}
return string(b)
}
// GetTables 获取数据库表结构信息
func (t *TplObj) GetTables() (Tables, error) {
nameList, err := t.db.Tables(t.ctx)
if err != nil {
return nil, err
}
// 过滤表名
nameList = filterTablesByName(nameList, t.in.TableNamePattern)
// 根据Tables参数过滤
nameList = filterTablesByInclude(nameList, t.in.Tables)
// 根据TablesEx参数过滤
nameList = filterTablesByExclude(nameList, t.in.TablesEx)
tables := make(Tables, 0, len(nameList))
for _, v := range nameList {
t, err := NewTable(t.ctx, t.db, v, t.TableOutputName(v))
if err != nil {
continue
}
t.SortFields(true)
tables = append(tables, t)
}
return tables, nil
}
// TableOutputName description
//
// createTime: 2025-01-25 17:20:46
func (t *TplObj) TableOutputName(name string) string {
if t.in.Prefix != "" {
name = t.in.Prefix + name
}
if t.in.RemovePrefix != "" {
name = strings.TrimPrefix(name, t.in.RemovePrefix)
}
return name
}
// 新增过滤函数
func filterTablesByName(tables []string, pattern string) []string {
if pattern == "" {
return tables
}
var result []string
re, err := regexp.Compile(pattern)
if err != nil {
return tables
}
for _, table := range tables {
if re.MatchString(table) {
result = append(result, table)
}
}
return result
}
// 根据包含表名过滤
func filterTablesByInclude(tables []string, include string) []string {
if include == "" {
return tables
}
includeTables := strings.Split(include, ",")
result := make([]string, 0, len(includeTables))
for _, table := range tables {
for _, includeTable := range includeTables {
if table == includeTable {
result = append(result, table)
break
}
}
}
return result
}
// 根据排除表名过滤
func filterTablesByExclude(tables []string, exclude string) []string {
if exclude == "" {
return tables
}
excludeTables := strings.Split(exclude, ",")
result := make([]string, 0, len(tables))
for _, table := range tables {
exclude := false
for _, excludeTable := range excludeTables {
if table == excludeTable {
exclude = true
break
}
}
if !exclude {
result = append(result, table)
}
}
return result
}

View File

@ -12,9 +12,11 @@ func TestTpl(t *testing.T) {
c := tpl.CGenTpl{}
t.Log(c)
out, err := c.Tpl(context.Background(), tpl.CGenTplInput{
Path: "./output",
TplPath: "./testdata",
Link: fmt.Sprintf("mysql:root:%s@tcp(127.0.0.1:3306)/focus?loc=Local&parseTime=true", "root123"),
Path: "./output",
TplPath: "./testdata",
Link: fmt.Sprintf("mysql:root:%s@tcp(127.0.0.1:3306)/focus?loc=Local&parseTime=true", "root123"),
Tables: "gf_user",
ImportPrefix: "github.com/gogf/gf/cmd/gf/v2/internal/cmd/gen/tpl/output",
})
if err != nil {
t.Error(err)