From 3a2a73c786535b5ff7dcd026fdafce5d2f2d88e8 Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Sat, 25 Jan 2025 18:01:25 +0800 Subject: [PATCH] up --- cmd/gf/internal/cmd/gen/tpl/readme.md | 8 + .../internal/cmd/gen/tpl/testdata/dao/dao.tpl | 18 +- .../testdata/dao/internal/dao_internal.tpl | 38 +-- cmd/gf/internal/cmd/gen/tpl/tpl.go | 274 +++++++++++++----- cmd/gf/internal/cmd/gen/tpl/tpl_field.go | 35 ++- cmd/gf/internal/cmd/gen/tpl/tpl_table.go | 154 ++++++++-- cmd/gf/internal/cmd/gen/tpl/tpl_test.go | 8 +- 7 files changed, 403 insertions(+), 132 deletions(-) diff --git a/cmd/gf/internal/cmd/gen/tpl/readme.md b/cmd/gf/internal/cmd/gen/tpl/readme.md index 2e2143681..5eabed165 100644 --- a/cmd/gf/internal/cmd/gen/tpl/readme.md +++ b/cmd/gf/internal/cmd/gen/tpl/readme.md @@ -3,6 +3,14 @@ ## 功能概述 基于数据库表结构,通过自定义模板生成Go代码的工具。 +## 功能设计 + +生成流程: +1. 读取数据库表结构 +2. 解析出表结构信息,包括表名、表注释、字段列表 +3. 根据规则裁切表数据,生成模板数据 +4. 根据模板生成代码 + ## 命令参数设计 ```shell diff --git a/cmd/gf/internal/cmd/gen/tpl/testdata/dao/dao.tpl b/cmd/gf/internal/cmd/gen/tpl/testdata/dao/dao.tpl index ee1ce14ca..e31a35977 100644 --- a/cmd/gf/internal/cmd/gen/tpl/testdata/dao/dao.tpl +++ b/cmd/gf/internal/cmd/gen/tpl/testdata/dao/dao.tpl @@ -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(), } ) diff --git a/cmd/gf/internal/cmd/gen/tpl/testdata/dao/internal/dao_internal.tpl b/cmd/gf/internal/cmd/gen/tpl/testdata/dao/internal/dao_internal.tpl index 05159e5b0..3c4533a25 100644 --- a/cmd/gf/internal/cmd/gen/tpl/testdata/dao/internal/dao_internal.tpl +++ b/cmd/gf/internal/cmd/gen/tpl/testdata/dao/internal/dao_internal.tpl @@ -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) } \ No newline at end of file diff --git a/cmd/gf/internal/cmd/gen/tpl/tpl.go b/cmd/gf/internal/cmd/gen/tpl/tpl.go index 9b0827a9d..3d61596b3 100644 --- a/cmd/gf/internal/cmd/gen/tpl/tpl.go +++ b/cmd/gf/internal/cmd/gen/tpl/tpl.go @@ -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 diff --git a/cmd/gf/internal/cmd/gen/tpl/tpl_field.go b/cmd/gf/internal/cmd/gen/tpl/tpl_field.go index e5828c8dd..c953fe7f5 100644 --- a/cmd/gf/internal/cmd/gen/tpl/tpl_field.go +++ b/cmd/gf/internal/cmd/gen/tpl/tpl_field.go @@ -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) } diff --git a/cmd/gf/internal/cmd/gen/tpl/tpl_table.go b/cmd/gf/internal/cmd/gen/tpl/tpl_table.go index 73d1f5587..823beed16 100644 --- a/cmd/gf/internal/cmd/gen/tpl/tpl_table.go +++ b/cmd/gf/internal/cmd/gen/tpl/tpl_table.go @@ -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 +} diff --git a/cmd/gf/internal/cmd/gen/tpl/tpl_test.go b/cmd/gf/internal/cmd/gen/tpl/tpl_test.go index c88193c9e..3876b48ff 100644 --- a/cmd/gf/internal/cmd/gen/tpl/tpl_test.go +++ b/cmd/gf/internal/cmd/gen/tpl/tpl_test.go @@ -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)