From fc9093a1aae36f36bb67fea7983be3345d58bb0e Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 26 Dec 2024 23:21:03 +0800 Subject: [PATCH] feat(cmd/gf): add `ShardingPattern` option for command `gf gen dao` to support generating dao files sharding tables (#4081) --- .github/workflows/ci-main.yml | 7 +- cmd/gf/internal/cmd/cmd_gen_dao.go | 4 +- .../cmd/cmd_z_unit_gen_dao_issue_test.go | 458 ++++++++++++++++++ .../cmd/cmd_z_unit_gen_dao_sharding_test.go | 77 +++ .../internal/cmd/cmd_z_unit_gen_dao_test.go | 437 +---------------- cmd/gf/internal/cmd/gendao/gendao.go | 285 ++++------- cmd/gf/internal/cmd/gendao/gendao_dao.go | 83 ++-- cmd/gf/internal/cmd/gendao/gendao_do.go | 29 +- cmd/gf/internal/cmd/gendao/gendao_entity.go | 29 +- cmd/gf/internal/cmd/gendao/gendao_gen_item.go | 4 +- cmd/gf/internal/cmd/gendao/gendao_tag.go | 149 ++++++ .../generated_user/dao/internal/table_user.go | 22 +- .../gendao/generated_user/dao/table_user.go | 9 +- .../dao/internal/table_user.go | 22 +- .../dao/table_user.go | 9 +- .../dao/internal/table_user.go | 22 +- .../generated_user_sqlite3/dao/table_user.go | 9 +- .../dao/internal/table_user.go | 22 +- .../dao/table_user.go | 9 +- .../cmd/testdata/gendao/sharding/sharding.sql | 45 ++ .../issue/3749/dao/internal/table_user.go | 22 +- .../cmd/testdata/issue/3749/dao/table_user.go | 9 +- .../consts/consts_gen_dao_template_dao.go | 93 ++-- .../consts/consts_gen_dao_template_do.go | 10 +- .../consts/consts_gen_dao_template_entity.go | 10 +- os/gview/gview.go | 24 +- os/gview/gview_config.go | 5 + os/gview/gview_parse.go | 93 ++-- util/gutil/gutil_map.go | 12 +- 29 files changed, 1143 insertions(+), 866 deletions(-) create mode 100644 cmd/gf/internal/cmd/cmd_z_unit_gen_dao_issue_test.go create mode 100644 cmd/gf/internal/cmd/cmd_z_unit_gen_dao_sharding_test.go create mode 100644 cmd/gf/internal/cmd/gendao/gendao_tag.go create mode 100644 cmd/gf/internal/cmd/testdata/gendao/sharding/sharding.sql diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index db0dea5de10..a2860760fff 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -83,8 +83,13 @@ jobs: - 3306:3306 # MariaDb backend server. + # docker run -d --name mariadb \ + # -p 3307:3306 \ + # -e MYSQL_DATABASE=test \ + # -e MYSQL_ROOT_PASSWORD=12345678 \ + # mariadb:11.4 mariadb: - image: mariadb:10.4 + image: mariadb:11.4 env: MARIADB_DATABASE: test MARIADB_ROOT_PASSWORD: 12345678 diff --git a/cmd/gf/internal/cmd/cmd_gen_dao.go b/cmd/gf/internal/cmd/cmd_gen_dao.go index 1d4d2e1068d..26edccfccd7 100644 --- a/cmd/gf/internal/cmd/cmd_gen_dao.go +++ b/cmd/gf/internal/cmd/cmd_gen_dao.go @@ -8,13 +8,15 @@ package cmd import ( _ "github.com/gogf/gf/contrib/drivers/clickhouse/v2" - // _ "github.com/gogf/gf/contrib/drivers/dm/v2" // precompilation does not support certain target platforms. _ "github.com/gogf/gf/contrib/drivers/mssql/v2" _ "github.com/gogf/gf/contrib/drivers/mysql/v2" _ "github.com/gogf/gf/contrib/drivers/oracle/v2" _ "github.com/gogf/gf/contrib/drivers/pgsql/v2" _ "github.com/gogf/gf/contrib/drivers/sqlite/v2" + // do not add dm in cli pre-compilation, + // the dm driver does not support certain target platforms. + // _ "github.com/gogf/gf/contrib/drivers/dm/v2" "github.com/gogf/gf/cmd/gf/v2/internal/cmd/gendao" ) diff --git a/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_issue_test.go b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_issue_test.go new file mode 100644 index 00000000000..55c30d1bb58 --- /dev/null +++ b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_issue_test.go @@ -0,0 +1,458 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package cmd + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gcfg" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/guid" + "github.com/gogf/gf/v2/util/gutil" + + "github.com/gogf/gf/cmd/gf/v2/internal/cmd/gendao" +) + +// https://github.com/gogf/gf/issues/2572 +func Test_Gen_Dao_Issue2572(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + db = testDB + table1 = "user1" + table2 = "user2" + issueDirPath = gtest.DataPath(`issue`, `2572`) + ) + t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2572`, `sql1.sql`))) + t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2572`, `sql2.sql`))) + defer dropTableWithDb(db, table1) + defer dropTableWithDb(db, table2) + + var ( + path = gfile.Temp(guid.S()) + group = "test" + in = gendao.CGenDaoInput{ + Path: path, + Link: "", + Tables: "", + TablesEx: "", + Group: group, + Prefix: "", + RemovePrefix: "", + JsonCase: "SnakeScreaming", + ImportPrefix: "", + DaoPath: "", + DoPath: "", + EntityPath: "", + TplDaoIndexPath: "", + TplDaoInternalPath: "", + TplDaoDoPath: "", + TplDaoEntityPath: "", + StdTime: false, + WithTime: false, + GJsonSupport: false, + OverwriteDao: false, + DescriptionTag: false, + NoJsonTag: false, + NoModelComment: false, + Clear: false, + TypeMapping: nil, + FieldMapping: nil, + } + ) + err = gutil.FillStructWithDefault(&in) + t.AssertNil(err) + + err = gfile.Copy(issueDirPath, path) + t.AssertNil(err) + + defer gfile.Remove(path) + + pwd := gfile.Pwd() + err = gfile.Chdir(path) + t.AssertNil(err) + + defer gfile.Chdir(pwd) + + _, err = gendao.CGenDao{}.Dao(ctx, in) + t.AssertNil(err) + + generatedFiles, err := gfile.ScanDir(path, "*.go", true) + t.AssertNil(err) + t.Assert(len(generatedFiles), 8) + for i, generatedFile := range generatedFiles { + generatedFiles[i] = gstr.TrimLeftStr(generatedFile, path) + } + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/dao/internal/user_1.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/dao/internal/user_2.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/dao/user_1.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/dao/user_2.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/model/do/user_1.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/model/do/user_2.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/model/entity/user_1.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/model/entity/user_2.go")), true) + }) +} + +// https://github.com/gogf/gf/issues/2616 +func Test_Gen_Dao_Issue2616(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + db = testDB + table1 = "user1" + table2 = "user2" + issueDirPath = gtest.DataPath(`issue`, `2616`) + ) + t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2616`, `sql1.sql`))) + t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2616`, `sql2.sql`))) + defer dropTableWithDb(db, table1) + defer dropTableWithDb(db, table2) + + var ( + path = gfile.Temp(guid.S()) + group = "test" + in = gendao.CGenDaoInput{ + Path: path, + Link: "", + Tables: "", + TablesEx: "", + Group: group, + Prefix: "", + RemovePrefix: "", + JsonCase: "SnakeScreaming", + ImportPrefix: "", + DaoPath: "", + DoPath: "", + EntityPath: "", + TplDaoIndexPath: "", + TplDaoInternalPath: "", + TplDaoDoPath: "", + TplDaoEntityPath: "", + StdTime: false, + WithTime: false, + GJsonSupport: false, + OverwriteDao: false, + DescriptionTag: false, + NoJsonTag: false, + NoModelComment: false, + Clear: false, + TypeMapping: nil, + FieldMapping: nil, + } + ) + err = gutil.FillStructWithDefault(&in) + t.AssertNil(err) + + err = gfile.Copy(issueDirPath, path) + t.AssertNil(err) + + defer gfile.Remove(path) + + pwd := gfile.Pwd() + err = gfile.Chdir(path) + t.AssertNil(err) + + defer gfile.Chdir(pwd) + + _, err = gendao.CGenDao{}.Dao(ctx, in) + t.AssertNil(err) + + generatedFiles, err := gfile.ScanDir(path, "*.go", true) + t.AssertNil(err) + t.Assert(len(generatedFiles), 8) + for i, generatedFile := range generatedFiles { + generatedFiles[i] = gstr.TrimLeftStr(generatedFile, path) + } + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/dao/internal/user_1.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/dao/internal/user_2.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/dao/user_1.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/dao/user_2.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/model/do/user_1.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/model/do/user_2.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/model/entity/user_1.go")), true) + t.Assert(gstr.InArray(generatedFiles, + filepath.FromSlash("/model/entity/user_2.go")), true) + + // Key string to check if overwrite the dao files. + // dao user1 is not be overwritten as configured in config.yaml. + // dao user2 is to be overwritten as configured in config.yaml. + var ( + keyStr = `// I am not overwritten.` + daoUser1Content = gfile.GetContents(path + "/dao/user_1.go") + daoUser2Content = gfile.GetContents(path + "/dao/user_2.go") + ) + t.Assert(gstr.Contains(daoUser1Content, keyStr), true) + t.Assert(gstr.Contains(daoUser2Content, keyStr), false) + }) +} + +// https://github.com/gogf/gf/issues/2746 +func Test_Gen_Dao_Issue2746(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + mdb gdb.DB + link2746 = "mariadb:root:12345678@tcp(127.0.0.1:3307)/test?loc=Local&parseTime=true" + table = "issue2746" + sqlContent = fmt.Sprintf( + gtest.DataContent(`issue`, `2746`, `sql.sql`), + table, + ) + ) + mdb, err = gdb.New(gdb.ConfigNode{ + Link: link2746, + }) + t.AssertNil(err) + + array := gstr.SplitAndTrim(sqlContent, ";") + for _, v := range array { + if _, err = mdb.Exec(ctx, v); err != nil { + t.AssertNil(err) + } + } + defer dropTableWithDb(mdb, table) + + var ( + path = gfile.Temp(guid.S()) + group = "test" + in = gendao.CGenDaoInput{ + Path: path, + Link: link2746, + Tables: "", + TablesEx: "", + Group: group, + Prefix: "", + RemovePrefix: "", + JsonCase: "SnakeScreaming", + ImportPrefix: "", + DaoPath: "", + DoPath: "", + EntityPath: "", + TplDaoIndexPath: "", + TplDaoInternalPath: "", + TplDaoDoPath: "", + TplDaoEntityPath: "", + StdTime: false, + WithTime: false, + GJsonSupport: true, + OverwriteDao: false, + DescriptionTag: false, + NoJsonTag: false, + NoModelComment: false, + Clear: false, + TypeMapping: nil, + FieldMapping: nil, + } + ) + err = gutil.FillStructWithDefault(&in) + t.AssertNil(err) + + err = gfile.Mkdir(path) + t.AssertNil(err) + + _, err = gendao.CGenDao{}.Dao(ctx, in) + t.AssertNil(err) + defer gfile.Remove(path) + + var ( + file = filepath.FromSlash(path + "/model/entity/issue_2746.go") + expectContent = gtest.DataContent(`issue`, `2746`, `issue_2746.go`) + ) + t.Assert(expectContent, gfile.GetContents(file)) + }) +} + +// https://github.com/gogf/gf/issues/3459 +func Test_Gen_Dao_Issue3459(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + db = testDB + table = "table_user" + sqlContent = fmt.Sprintf( + gtest.DataContent(`gendao`, `user.tpl.sql`), + table, + ) + ) + dropTableWithDb(db, table) + array := gstr.SplitAndTrim(sqlContent, ";") + for _, v := range array { + if _, err = db.Exec(ctx, v); err != nil { + t.AssertNil(err) + } + } + defer dropTableWithDb(db, table) + + var ( + confDir = gtest.DataPath("issue", "3459") + path = gfile.Temp(guid.S()) + group = "test" + in = gendao.CGenDaoInput{ + Path: path, + Link: link, + Tables: "", + TablesEx: "", + Group: group, + Prefix: "", + RemovePrefix: "", + JsonCase: "SnakeScreaming", + ImportPrefix: "", + DaoPath: "", + DoPath: "", + EntityPath: "", + TplDaoIndexPath: "", + TplDaoInternalPath: "", + TplDaoDoPath: "", + TplDaoEntityPath: "", + StdTime: false, + WithTime: false, + GJsonSupport: false, + OverwriteDao: false, + DescriptionTag: false, + NoJsonTag: false, + NoModelComment: false, + Clear: false, + TypeMapping: nil, + } + ) + err = g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetPath(confDir) + t.AssertNil(err) + + err = gutil.FillStructWithDefault(&in) + t.AssertNil(err) + + err = gfile.Mkdir(path) + t.AssertNil(err) + + // for go mod import path auto retrieve. + err = gfile.Copy( + gtest.DataPath("gendao", "go.mod.txt"), + gfile.Join(path, "go.mod"), + ) + t.AssertNil(err) + + _, err = gendao.CGenDao{}.Dao(ctx, in) + t.AssertNil(err) + defer gfile.Remove(path) + + // files + files, err := gfile.ScanDir(path, "*.go", true) + t.AssertNil(err) + t.Assert(files, []string{ + filepath.FromSlash(path + "/dao/internal/table_user.go"), + filepath.FromSlash(path + "/dao/table_user.go"), + filepath.FromSlash(path + "/model/do/table_user.go"), + filepath.FromSlash(path + "/model/entity/table_user.go"), + }) + // content + testPath := gtest.DataPath("gendao", "generated_user") + expectFiles := []string{ + filepath.FromSlash(testPath + "/dao/internal/table_user.go"), + filepath.FromSlash(testPath + "/dao/table_user.go"), + filepath.FromSlash(testPath + "/model/do/table_user.go"), + filepath.FromSlash(testPath + "/model/entity/table_user.go"), + } + for i, _ := range files { + //_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i])) + t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i])) + } + }) +} + +// https://github.com/gogf/gf/issues/3749 +func Test_Gen_Dao_Issue3749(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + db = testDB + table = "table_user" + sqlContent = fmt.Sprintf( + gtest.DataContent(`issue`, `3749`, `user.tpl.sql`), + table, + ) + ) + dropTableWithDb(db, table) + array := gstr.SplitAndTrim(sqlContent, ";") + for _, v := range array { + if _, err = db.Exec(ctx, v); err != nil { + t.AssertNil(err) + } + } + defer dropTableWithDb(db, table) + + var ( + path = gfile.Temp(guid.S()) + group = "test" + in = gendao.CGenDaoInput{ + Path: path, + Link: link, + Group: group, + } + ) + + err = gutil.FillStructWithDefault(&in) + t.AssertNil(err) + + err = gfile.Mkdir(path) + t.AssertNil(err) + + // for go mod import path auto retrieve. + err = gfile.Copy( + gtest.DataPath("gendao", "go.mod.txt"), + gfile.Join(path, "go.mod"), + ) + t.AssertNil(err) + + _, err = gendao.CGenDao{}.Dao(ctx, in) + t.AssertNil(err) + defer gfile.Remove(path) + + // files + files, err := gfile.ScanDir(path, "*.go", true) + t.AssertNil(err) + t.Assert(files, []string{ + filepath.FromSlash(path + "/dao/internal/table_user.go"), + filepath.FromSlash(path + "/dao/table_user.go"), + filepath.FromSlash(path + "/model/do/table_user.go"), + filepath.FromSlash(path + "/model/entity/table_user.go"), + }) + // content + testPath := gtest.DataPath(`issue`, `3749`) + expectFiles := []string{ + filepath.FromSlash(testPath + "/dao/internal/table_user.go"), + filepath.FromSlash(testPath + "/dao/table_user.go"), + filepath.FromSlash(testPath + "/model/do/table_user.go"), + filepath.FromSlash(testPath + "/model/entity/table_user.go"), + } + for i, _ := range files { + //_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i])) + t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i])) + } + }) +} diff --git a/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_sharding_test.go b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_sharding_test.go new file mode 100644 index 00000000000..a20e92e53c6 --- /dev/null +++ b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_sharding_test.go @@ -0,0 +1,77 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package cmd + +import ( + "testing" + + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/guid" + "github.com/gogf/gf/v2/util/gutil" + + "github.com/gogf/gf/cmd/gf/v2/internal/cmd/gendao" +) + +func Test_Gen_Dao_Sharding(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + db = testDB + tableSingle = "single_table" + table1 = "users_0001" + table2 = "users_0002" + table3 = "users_0003" + sqlFilePath = gtest.DataPath(`gendao`, `sharding`, `sharding.sql`) + ) + t.AssertNil(execSqlFile(db, sqlFilePath)) + defer dropTableWithDb(db, tableSingle) + defer dropTableWithDb(db, table1) + defer dropTableWithDb(db, table2) + defer dropTableWithDb(db, table3) + + var ( + path = gfile.Temp(guid.S()) + //path = "/Users/john/Temp/gen_dao_sharding" + group = "test" + in = gendao.CGenDaoInput{ + Path: path, + Link: link, + Group: group, + ShardingPattern: []string{ + `users_?`, + }, + } + ) + err = gutil.FillStructWithDefault(&in) + t.AssertNil(err) + + err = gfile.Mkdir(path) + t.AssertNil(err) + + pwd := gfile.Pwd() + err = gfile.Chdir(path) + t.AssertNil(err) + defer gfile.Chdir(pwd) + defer gfile.RemoveAll(path) + + _, err = gendao.CGenDao{}.Dao(ctx, in) + t.AssertNil(err) + + generatedFiles, err := gfile.ScanDir(path, "*.go", true) + t.AssertNil(err) + t.Assert(len(generatedFiles), 8) + var ( + daoSingleTableContent = gfile.GetContents(gfile.Join(path, "dao", "single_table.go")) + daoUsersContent = gfile.GetContents(gfile.Join(path, "dao", "users.go")) + ) + t.Assert(gstr.Contains(daoSingleTableContent, "SingleTable = singleTableDao{internal.NewSingleTableDao()}"), true) + t.Assert(gstr.Contains(daoUsersContent, "Users = usersDao{internal.NewUsersDao(userShardingHandler)}"), true) + t.Assert(gstr.Contains(daoUsersContent, "m.Sharding(gdb.ShardingConfig{"), true) + }) +} diff --git a/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go index fbefdb88fb7..8d20fe8c169 100644 --- a/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go +++ b/cmd/gf/internal/cmd/cmd_z_unit_gen_dao_test.go @@ -12,8 +12,6 @@ import ( "testing" "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gcfg" "github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/test/gtest" "github.com/gogf/gf/v2/text/gstr" @@ -211,6 +209,7 @@ func Test_Gen_Dao_TypeMapping(t *testing.T) { filepath.FromSlash(testPath + "/model/entity/table_user.go"), } for i, _ := range files { + //_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i])) t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i])) } }) @@ -313,6 +312,7 @@ func Test_Gen_Dao_FieldMapping(t *testing.T) { filepath.FromSlash(testPath + "/model/entity/table_user.go"), } for i, _ := range files { + //_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i])) t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i])) } }) @@ -332,438 +332,6 @@ func execSqlFile(db gdb.DB, filePath string, args ...any) error { return nil } -// https://github.com/gogf/gf/issues/2572 -func Test_Gen_Dao_Issue2572(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var ( - err error - db = testDB - table1 = "user1" - table2 = "user2" - issueDirPath = gtest.DataPath(`issue`, `2572`) - ) - t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2572`, `sql1.sql`))) - t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2572`, `sql2.sql`))) - defer dropTableWithDb(db, table1) - defer dropTableWithDb(db, table2) - - var ( - path = gfile.Temp(guid.S()) - group = "test" - in = gendao.CGenDaoInput{ - Path: path, - Link: "", - Tables: "", - TablesEx: "", - Group: group, - Prefix: "", - RemovePrefix: "", - JsonCase: "SnakeScreaming", - ImportPrefix: "", - DaoPath: "", - DoPath: "", - EntityPath: "", - TplDaoIndexPath: "", - TplDaoInternalPath: "", - TplDaoDoPath: "", - TplDaoEntityPath: "", - StdTime: false, - WithTime: false, - GJsonSupport: false, - OverwriteDao: false, - DescriptionTag: false, - NoJsonTag: false, - NoModelComment: false, - Clear: false, - TypeMapping: nil, - FieldMapping: nil, - } - ) - err = gutil.FillStructWithDefault(&in) - t.AssertNil(err) - - err = gfile.Copy(issueDirPath, path) - t.AssertNil(err) - - defer gfile.Remove(path) - - pwd := gfile.Pwd() - err = gfile.Chdir(path) - t.AssertNil(err) - - defer gfile.Chdir(pwd) - - _, err = gendao.CGenDao{}.Dao(ctx, in) - t.AssertNil(err) - - generatedFiles, err := gfile.ScanDir(path, "*.go", true) - t.AssertNil(err) - t.Assert(len(generatedFiles), 8) - for i, generatedFile := range generatedFiles { - generatedFiles[i] = gstr.TrimLeftStr(generatedFile, path) - } - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/dao/internal/user_1.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/dao/internal/user_2.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/dao/user_1.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/dao/user_2.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/model/do/user_1.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/model/do/user_2.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/model/entity/user_1.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/model/entity/user_2.go")), true) - }) -} - -// https://github.com/gogf/gf/issues/2616 -func Test_Gen_Dao_Issue2616(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var ( - err error - db = testDB - table1 = "user1" - table2 = "user2" - issueDirPath = gtest.DataPath(`issue`, `2616`) - ) - t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2616`, `sql1.sql`))) - t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2616`, `sql2.sql`))) - defer dropTableWithDb(db, table1) - defer dropTableWithDb(db, table2) - - var ( - path = gfile.Temp(guid.S()) - group = "test" - in = gendao.CGenDaoInput{ - Path: path, - Link: "", - Tables: "", - TablesEx: "", - Group: group, - Prefix: "", - RemovePrefix: "", - JsonCase: "SnakeScreaming", - ImportPrefix: "", - DaoPath: "", - DoPath: "", - EntityPath: "", - TplDaoIndexPath: "", - TplDaoInternalPath: "", - TplDaoDoPath: "", - TplDaoEntityPath: "", - StdTime: false, - WithTime: false, - GJsonSupport: false, - OverwriteDao: false, - DescriptionTag: false, - NoJsonTag: false, - NoModelComment: false, - Clear: false, - TypeMapping: nil, - FieldMapping: nil, - } - ) - err = gutil.FillStructWithDefault(&in) - t.AssertNil(err) - - err = gfile.Copy(issueDirPath, path) - t.AssertNil(err) - - defer gfile.Remove(path) - - pwd := gfile.Pwd() - err = gfile.Chdir(path) - t.AssertNil(err) - - defer gfile.Chdir(pwd) - - _, err = gendao.CGenDao{}.Dao(ctx, in) - t.AssertNil(err) - - generatedFiles, err := gfile.ScanDir(path, "*.go", true) - t.AssertNil(err) - t.Assert(len(generatedFiles), 8) - for i, generatedFile := range generatedFiles { - generatedFiles[i] = gstr.TrimLeftStr(generatedFile, path) - } - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/dao/internal/user_1.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/dao/internal/user_2.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/dao/user_1.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/dao/user_2.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/model/do/user_1.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/model/do/user_2.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/model/entity/user_1.go")), true) - t.Assert(gstr.InArray(generatedFiles, - filepath.FromSlash("/model/entity/user_2.go")), true) - - // Key string to check if overwrite the dao files. - // dao user1 is not be overwritten as configured in config.yaml. - // dao user2 is to be overwritten as configured in config.yaml. - var ( - keyStr = `// I am not overwritten.` - daoUser1Content = gfile.GetContents(path + "/dao/user_1.go") - daoUser2Content = gfile.GetContents(path + "/dao/user_2.go") - ) - t.Assert(gstr.Contains(daoUser1Content, keyStr), true) - t.Assert(gstr.Contains(daoUser2Content, keyStr), false) - }) -} - -// https://github.com/gogf/gf/issues/2746 -func Test_Gen_Dao_Issue2746(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var ( - err error - mdb gdb.DB - link2746 = "mariadb:root:12345678@tcp(127.0.0.1:3307)/test?loc=Local&parseTime=true" - table = "issue2746" - sqlContent = fmt.Sprintf( - gtest.DataContent(`issue`, `2746`, `sql.sql`), - table, - ) - ) - mdb, err = gdb.New(gdb.ConfigNode{ - Link: link2746, - }) - t.AssertNil(err) - - array := gstr.SplitAndTrim(sqlContent, ";") - for _, v := range array { - if _, err = mdb.Exec(ctx, v); err != nil { - t.AssertNil(err) - } - } - defer dropTableWithDb(mdb, table) - - var ( - path = gfile.Temp(guid.S()) - group = "test" - in = gendao.CGenDaoInput{ - Path: path, - Link: link2746, - Tables: "", - TablesEx: "", - Group: group, - Prefix: "", - RemovePrefix: "", - JsonCase: "SnakeScreaming", - ImportPrefix: "", - DaoPath: "", - DoPath: "", - EntityPath: "", - TplDaoIndexPath: "", - TplDaoInternalPath: "", - TplDaoDoPath: "", - TplDaoEntityPath: "", - StdTime: false, - WithTime: false, - GJsonSupport: true, - OverwriteDao: false, - DescriptionTag: false, - NoJsonTag: false, - NoModelComment: false, - Clear: false, - TypeMapping: nil, - FieldMapping: nil, - } - ) - err = gutil.FillStructWithDefault(&in) - t.AssertNil(err) - - err = gfile.Mkdir(path) - t.AssertNil(err) - - _, err = gendao.CGenDao{}.Dao(ctx, in) - t.AssertNil(err) - defer gfile.Remove(path) - - var ( - file = filepath.FromSlash(path + "/model/entity/issue_2746.go") - expectContent = gtest.DataContent(`issue`, `2746`, `issue_2746.go`) - ) - t.Assert(expectContent, gfile.GetContents(file)) - }) -} - -// https://github.com/gogf/gf/issues/3459 -func Test_Gen_Dao_Issue3459(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var ( - err error - db = testDB - table = "table_user" - sqlContent = fmt.Sprintf( - gtest.DataContent(`gendao`, `user.tpl.sql`), - table, - ) - ) - dropTableWithDb(db, table) - array := gstr.SplitAndTrim(sqlContent, ";") - for _, v := range array { - if _, err = db.Exec(ctx, v); err != nil { - t.AssertNil(err) - } - } - defer dropTableWithDb(db, table) - - var ( - confDir = gtest.DataPath("issue", "3459") - path = gfile.Temp(guid.S()) - group = "test" - in = gendao.CGenDaoInput{ - Path: path, - Link: link, - Tables: "", - TablesEx: "", - Group: group, - Prefix: "", - RemovePrefix: "", - JsonCase: "SnakeScreaming", - ImportPrefix: "", - DaoPath: "", - DoPath: "", - EntityPath: "", - TplDaoIndexPath: "", - TplDaoInternalPath: "", - TplDaoDoPath: "", - TplDaoEntityPath: "", - StdTime: false, - WithTime: false, - GJsonSupport: false, - OverwriteDao: false, - DescriptionTag: false, - NoJsonTag: false, - NoModelComment: false, - Clear: false, - TypeMapping: nil, - } - ) - err = g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetPath(confDir) - t.AssertNil(err) - - err = gutil.FillStructWithDefault(&in) - t.AssertNil(err) - - err = gfile.Mkdir(path) - t.AssertNil(err) - - // for go mod import path auto retrieve. - err = gfile.Copy( - gtest.DataPath("gendao", "go.mod.txt"), - gfile.Join(path, "go.mod"), - ) - t.AssertNil(err) - - _, err = gendao.CGenDao{}.Dao(ctx, in) - t.AssertNil(err) - defer gfile.Remove(path) - - // files - files, err := gfile.ScanDir(path, "*.go", true) - t.AssertNil(err) - t.Assert(files, []string{ - filepath.FromSlash(path + "/dao/internal/table_user.go"), - filepath.FromSlash(path + "/dao/table_user.go"), - filepath.FromSlash(path + "/model/do/table_user.go"), - filepath.FromSlash(path + "/model/entity/table_user.go"), - }) - // content - testPath := gtest.DataPath("gendao", "generated_user") - expectFiles := []string{ - filepath.FromSlash(testPath + "/dao/internal/table_user.go"), - filepath.FromSlash(testPath + "/dao/table_user.go"), - filepath.FromSlash(testPath + "/model/do/table_user.go"), - filepath.FromSlash(testPath + "/model/entity/table_user.go"), - } - for i, _ := range files { - t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i])) - } - }) -} - -// https://github.com/gogf/gf/issues/3749 -func Test_Gen_Dao_Issue3749(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var ( - err error - db = testDB - table = "table_user" - sqlContent = fmt.Sprintf( - gtest.DataContent(`issue`, `3749`, `user.tpl.sql`), - table, - ) - ) - dropTableWithDb(db, table) - array := gstr.SplitAndTrim(sqlContent, ";") - for _, v := range array { - if _, err = db.Exec(ctx, v); err != nil { - t.AssertNil(err) - } - } - defer dropTableWithDb(db, table) - - var ( - path = gfile.Temp(guid.S()) - group = "test" - in = gendao.CGenDaoInput{ - Path: path, - Link: link, - Group: group, - } - ) - - err = gutil.FillStructWithDefault(&in) - t.AssertNil(err) - - err = gfile.Mkdir(path) - t.AssertNil(err) - - // for go mod import path auto retrieve. - err = gfile.Copy( - gtest.DataPath("gendao", "go.mod.txt"), - gfile.Join(path, "go.mod"), - ) - t.AssertNil(err) - - _, err = gendao.CGenDao{}.Dao(ctx, in) - t.AssertNil(err) - defer gfile.Remove(path) - - // files - files, err := gfile.ScanDir(path, "*.go", true) - t.AssertNil(err) - t.Assert(files, []string{ - filepath.FromSlash(path + "/dao/internal/table_user.go"), - filepath.FromSlash(path + "/dao/table_user.go"), - filepath.FromSlash(path + "/model/do/table_user.go"), - filepath.FromSlash(path + "/model/entity/table_user.go"), - }) - // content - testPath := gtest.DataPath(`issue`, `3749`) - expectFiles := []string{ - filepath.FromSlash(testPath + "/dao/internal/table_user.go"), - filepath.FromSlash(testPath + "/dao/table_user.go"), - filepath.FromSlash(testPath + "/model/do/table_user.go"), - filepath.FromSlash(testPath + "/model/entity/table_user.go"), - } - for i, _ := range files { - t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i])) - } - }) -} - func Test_Gen_Dao_Sqlite3(t *testing.T) { gtest.C(t, func(t *gtest.T) { var ( @@ -836,6 +404,7 @@ func Test_Gen_Dao_Sqlite3(t *testing.T) { filepath.FromSlash(testPath + "/model/entity/table_user.go"), } for i, _ := range files { + //_ = gfile.PutContents(expectFiles[i], gfile.GetContents(files[i])) t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i])) } }) diff --git a/cmd/gf/internal/cmd/gendao/gendao.go b/cmd/gf/internal/cmd/gendao/gendao.go index d571aada6fb..ff190a401cf 100644 --- a/cmd/gf/internal/cmd/gendao/gendao.go +++ b/cmd/gf/internal/cmd/gendao/gendao.go @@ -14,199 +14,50 @@ import ( "golang.org/x/mod/modfile" "github.com/gogf/gf/v2/container/garray" + "github.com/gogf/gf/v2/container/gset" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/os/gproc" "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/os/gview" + "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" - "github.com/gogf/gf/v2/util/gtag" "github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog" "github.com/gogf/gf/cmd/gf/v2/internal/utility/utils" ) -const ( - CGenDaoConfig = `gfcli.gen.dao` - CGenDaoUsage = `gf gen dao [OPTION]` - CGenDaoBrief = `automatically generate go files for dao/do/entity` - CGenDaoEg = ` -gf gen dao -gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test" -gf gen dao -p ./model -g user-center -t user,user_detail,user_login -gf gen dao -r user_ -` - - CGenDaoAd = ` -CONFIGURATION SUPPORT - Options are also supported by configuration file. - 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: - 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" - 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 -` - CGenDaoBriefPath = `directory path for generated files` - CGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame` - CGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','` - CGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','` - CGenDaoBriefPrefix = `add prefix for all table of specified link/database tables` - CGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','` - CGenDaoBriefRemoveFieldPrefix = `remove specified prefix of the field, multiple prefix separated with ','` - CGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables` - CGenDaoBriefWithTime = `add created time for auto produced go files` - CGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables` - CGenDaoBriefImportPrefix = `custom import prefix for generated go files` - CGenDaoBriefDaoPath = `directory path for storing generated dao files under path` - CGenDaoBriefDoPath = `directory path for storing generated do files under path` - CGenDaoBriefEntityPath = `directory path for storing generated entity files under path` - CGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder` - CGenDaoBriefModelFile = `custom file name for storing generated model content` - CGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default` - CGenDaoBriefDescriptionTag = `add comment to description tag for each field` - CGenDaoBriefNoJsonTag = `no json tag will be added for each field` - CGenDaoBriefNoModelComment = `no model comment will be added for each field` - CGenDaoBriefClear = `delete all generated go files that do not exist in database` - CGenDaoBriefTypeMapping = `custom local type mapping for generated struct attributes relevant to fields of table` - CGenDaoBriefFieldMapping = `custom local type mapping for generated struct attributes relevant to specific fields of table` - CGenDaoBriefGroup = ` -specifying the configuration group name of database for generated ORM instance, -it's not necessary and the default value is "default" -` - CGenDaoBriefJsonCase = ` -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 | -` - CGenDaoBriefTplDaoIndexPath = `template file path for dao index file` - CGenDaoBriefTplDaoInternalPath = `template file path for dao internal file` - CGenDaoBriefTplDaoDoPathPath = `template file path for dao do file` - CGenDaoBriefTplDaoEntityPath = `template file path for dao entity file` - - tplVarTableName = `{TplTableName}` - tplVarTableNameCamelCase = `{TplTableNameCamelCase}` - tplVarTableNameCamelLowerCase = `{TplTableNameCamelLowerCase}` - tplVarPackageImports = `{TplPackageImports}` - tplVarImportPrefix = `{TplImportPrefix}` - tplVarStructDefine = `{TplStructDefine}` - tplVarColumnDefine = `{TplColumnDefine}` - tplVarColumnNames = `{TplColumnNames}` - tplVarGroupName = `{TplGroupName}` - tplVarDatetimeStr = `{TplDatetimeStr}` - tplVarCreatedAtDatetimeStr = `{TplCreatedAtDatetimeStr}` - tplVarPackageName = `{TplPackageName}` -) - -var ( - createdAt = gtime.Now() - defaultTypeMapping = map[DBFieldTypeName]CustomAttributeType{ - "decimal": { - Type: "float64", - }, - "money": { - Type: "float64", - }, - "numeric": { - Type: "float64", - }, - "smallmoney": { - Type: "float64", - }, - } -) - -func init() { - gtag.Sets(g.MapStrStr{ - `CGenDaoConfig`: CGenDaoConfig, - `CGenDaoUsage`: CGenDaoUsage, - `CGenDaoBrief`: CGenDaoBrief, - `CGenDaoEg`: CGenDaoEg, - `CGenDaoAd`: CGenDaoAd, - `CGenDaoBriefPath`: CGenDaoBriefPath, - `CGenDaoBriefLink`: CGenDaoBriefLink, - `CGenDaoBriefTables`: CGenDaoBriefTables, - `CGenDaoBriefTablesEx`: CGenDaoBriefTablesEx, - `CGenDaoBriefPrefix`: CGenDaoBriefPrefix, - `CGenDaoBriefRemovePrefix`: CGenDaoBriefRemovePrefix, - `CGenDaoBriefRemoveFieldPrefix`: CGenDaoBriefRemoveFieldPrefix, - `CGenDaoBriefStdTime`: CGenDaoBriefStdTime, - `CGenDaoBriefWithTime`: CGenDaoBriefWithTime, - `CGenDaoBriefDaoPath`: CGenDaoBriefDaoPath, - `CGenDaoBriefDoPath`: CGenDaoBriefDoPath, - `CGenDaoBriefEntityPath`: CGenDaoBriefEntityPath, - `CGenDaoBriefGJsonSupport`: CGenDaoBriefGJsonSupport, - `CGenDaoBriefImportPrefix`: CGenDaoBriefImportPrefix, - `CGenDaoBriefOverwriteDao`: CGenDaoBriefOverwriteDao, - `CGenDaoBriefModelFile`: CGenDaoBriefModelFile, - `CGenDaoBriefModelFileForDao`: CGenDaoBriefModelFileForDao, - `CGenDaoBriefDescriptionTag`: CGenDaoBriefDescriptionTag, - `CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag, - `CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment, - `CGenDaoBriefClear`: CGenDaoBriefClear, - `CGenDaoBriefTypeMapping`: CGenDaoBriefTypeMapping, - `CGenDaoBriefFieldMapping`: CGenDaoBriefFieldMapping, - `CGenDaoBriefGroup`: CGenDaoBriefGroup, - `CGenDaoBriefJsonCase`: CGenDaoBriefJsonCase, - `CGenDaoBriefTplDaoIndexPath`: CGenDaoBriefTplDaoIndexPath, - `CGenDaoBriefTplDaoInternalPath`: CGenDaoBriefTplDaoInternalPath, - `CGenDaoBriefTplDaoDoPathPath`: CGenDaoBriefTplDaoDoPathPath, - `CGenDaoBriefTplDaoEntityPath`: CGenDaoBriefTplDaoEntityPath, - }) -} - type ( CGenDao struct{} CGenDaoInput struct { g.Meta `name:"dao" config:"{CGenDaoConfig}" usage:"{CGenDaoUsage}" brief:"{CGenDaoBrief}" eg:"{CGenDaoEg}" ad:"{CGenDaoAd}"` - Path string `name:"path" short:"p" brief:"{CGenDaoBriefPath}" d:"internal"` - 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"` + Path string `name:"path" short:"p" brief:"{CGenDaoBriefPath}" d:"internal"` + Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"` + Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"` + TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"` + ShardingPattern []string `name:"ShardingPattern" short:"sp" brief:"{CGenDaoBriefShardingPattern}"` + 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[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"` FieldMapping map[DBTableFieldName]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenDaoBriefFieldMapping}" orphan:"true"` @@ -218,9 +69,10 @@ type ( CGenDaoInternalInput struct { CGenDaoInput - DB gdb.DB - TableNames []string - NewTableNames []string + DB gdb.DB + TableNames []string + NewTableNames []string + ShardingTableSet *gset.StrSet } DBTableFieldName = string DBFieldTypeName = string @@ -230,6 +82,25 @@ type ( } ) +var ( + createdAt = gtime.Now() + tplView = gview.New() + defaultTypeMapping = map[DBFieldTypeName]CustomAttributeType{ + "decimal": { + Type: "float64", + }, + "money": { + Type: "float64", + }, + "numeric": { + Type: "float64", + }, + "smallmoney": { + Type: "float64", + }, + } +) + func (c CGenDao) Dao(ctx context.Context, in CGenDaoInput) (out *CGenDaoOutput, err error) { in.genItems = newCGenDaoInternalGenItems() if in.Link != "" { @@ -274,9 +145,12 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) { // It uses user passed database configuration. if in.Link != "" { var tempGroup = gtime.TimestampNanoStr() - gdb.AddConfigNode(tempGroup, gdb.ConfigNode{ + err = gdb.AddConfigNode(tempGroup, gdb.ConfigNode{ Link: in.Link, }) + if err != nil { + mlog.Fatalf(`database configuration failed: %+v`, err) + } if db, err = gdb.Instance(tempGroup); err != nil { mlog.Fatalf(`database initialization failed: %+v`, err) } @@ -317,24 +191,51 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) { } // Generating dao & model go files one by one according to given table name. - newTableNames := make([]string, len(tableNames)) + var ( + newTableNames = make([]string, len(tableNames)) + shardingNewTableSet = gset.NewStrSet() + ) for i, tableName := range tableNames { newTableName := tableName for _, v := range removePrefixArray { newTableName = gstr.TrimLeftStr(newTableName, v, 1) } + if len(in.ShardingPattern) > 0 { + for _, pattern := range in.ShardingPattern { + var ( + match []string + regPattern = gstr.Replace(pattern, "?", `(.+)`) + ) + match, err = gregex.MatchString(regPattern, newTableName) + if err != nil { + mlog.Fatalf(`invalid sharding pattern "%s": %+v`, pattern, err) + } + if len(match) < 2 { + continue + } + newTableName = gstr.Replace(pattern, "?", "") + newTableName = gstr.Trim(newTableName, `_.-`) + if shardingNewTableSet.Contains(newTableName) { + tableNames[i] = "" + continue + } + shardingNewTableSet.Add(newTableName) + } + } newTableName = in.Prefix + newTableName newTableNames[i] = newTableName } + tableNames = garray.NewStrArrayFrom(tableNames).FilterEmpty().Slice() in.genItems.Scale() // Dao: index and internal. generateDao(ctx, CGenDaoInternalInput{ - CGenDaoInput: in, - DB: db, - TableNames: tableNames, - NewTableNames: newTableNames, + CGenDaoInput: in, + DB: db, + TableNames: tableNames, + NewTableNames: newTableNames, + ShardingTableSet: shardingNewTableSet, }) // Do. generateDo(ctx, CGenDaoInternalInput{ @@ -407,13 +308,15 @@ func getImportPartContent(ctx context.Context, source string, isDo bool, appendI return packageImportsStr } -func replaceDefaultVar(in CGenDaoInternalInput, origin string) string { - var tplCreatedAtDatetimeStr string - var tplDatetimeStr string = createdAt.String() +func assignDefaultVar(view *gview.View, in CGenDaoInternalInput) { + var ( + tplCreatedAtDatetimeStr string + tplDatetimeStr = createdAt.String() + ) if in.WithTime { tplCreatedAtDatetimeStr = fmt.Sprintf(`Created at %s`, tplDatetimeStr) } - return gstr.ReplaceByMap(origin, g.MapStrStr{ + view.Assigns(g.Map{ tplVarDatetimeStr: tplDatetimeStr, tplVarCreatedAtDatetimeStr: tplCreatedAtDatetimeStr, }) diff --git a/cmd/gf/internal/cmd/gendao/gendao_dao.go b/cmd/gf/internal/cmd/gendao/gendao_dao.go index 1c2363347b7..83b4c5a4616 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_dao.go +++ b/cmd/gf/internal/cmd/gendao/gendao_dao.go @@ -18,6 +18,7 @@ import ( "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gview" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/cmd/gf/v2/internal/consts" @@ -32,22 +33,30 @@ func generateDao(ctx context.Context, in CGenDaoInternalInput) { ) in.genItems.AppendDirPath(dirPathDao) for i := 0; i < len(in.TableNames); i++ { + var ( + realTableName = in.TableNames[i] + newTableName = in.NewTableNames[i] + ) generateDaoSingle(ctx, generateDaoSingleInput{ CGenDaoInternalInput: in, - TableName: in.TableNames[i], - NewTableName: in.NewTableNames[i], + TableName: realTableName, + NewTableName: newTableName, DirPathDao: dirPathDao, DirPathDaoInternal: dirPathDaoInternal, + IsSharding: in.ShardingTableSet.Contains(newTableName), }) } } type generateDaoSingleInput struct { CGenDaoInternalInput - TableName string // TableName specifies the table name of the table. - NewTableName string // NewTableName specifies the prefix-stripped name of the table. + // TableName specifies the table name of the table. + TableName string + // NewTableName specifies the prefix-stripped or custom edited name of the table. + NewTableName string DirPathDao string DirPathDaoInternal string + IsSharding bool } // generateDaoSingle generates the dao and model content of given table. @@ -109,17 +118,26 @@ func generateDaoIndex(in generateDaoIndexInput) { // It should add path to result slice whenever it would generate the path file or not. in.genItems.AppendGeneratedFilePath(path) if in.OverwriteDao || !gfile.Exists(path) { - indexContent := gstr.ReplaceByMap( - getTemplateFromPathOrDefault(in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent), - g.MapStrStr{ - tplVarImportPrefix: in.ImportPrefix, - tplVarTableName: in.TableName, - tplVarTableNameCamelCase: in.TableNameCamelCase, - tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase, - tplVarPackageName: filepath.Base(in.DaoPath), - }) - indexContent = replaceDefaultVar(in.CGenDaoInternalInput, indexContent) - if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil { + var ( + ctx = context.Background() + tplContent = getTemplateFromPathOrDefault( + in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent, + ) + ) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarTableSharding: in.IsSharding, + tplVarImportPrefix: in.ImportPrefix, + tplVarTableName: in.TableName, + tplVarTableNameCamelCase: in.TableNameCamelCase, + tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase, + tplVarPackageName: filepath.Base(in.DaoPath), + }) + indexContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } + if err = gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil { mlog.Fatalf("writing content to '%s' failed: %v", path, err) } else { utils.GoFmt(path) @@ -138,20 +156,29 @@ type generateDaoInternalInput struct { } func generateDaoInternal(in generateDaoInternalInput) { + var ( + ctx = context.Background() + removeFieldPrefixArray = gstr.SplitAndTrim(in.RemoveFieldPrefix, ",") + tplContent = getTemplateFromPathOrDefault( + in.TplDaoInternalPath, consts.TemplateGenDaoInternalContent, + ) + ) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarImportPrefix: in.ImportPrefix, + tplVarTableName: in.TableName, + tplVarGroupName: in.Group, + tplVarTableNameCamelCase: in.TableNameCamelCase, + tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase, + tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(in.FieldMap, removeFieldPrefixArray)), + tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(in.FieldMap, removeFieldPrefixArray)), + }) + assignDefaultVar(tplView, in.CGenDaoInternalInput) + modelContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } path := filepath.FromSlash(gfile.Join(in.DirPathDaoInternal, in.FileName+".go")) - removeFieldPrefixArray := gstr.SplitAndTrim(in.RemoveFieldPrefix, ",") - modelContent := gstr.ReplaceByMap( - getTemplateFromPathOrDefault(in.TplDaoInternalPath, consts.TemplateGenDaoInternalContent), - g.MapStrStr{ - tplVarImportPrefix: in.ImportPrefix, - tplVarTableName: in.TableName, - tplVarGroupName: in.Group, - tplVarTableNameCamelCase: in.TableNameCamelCase, - tplVarTableNameCamelLowerCase: in.TableNameCamelLowerCase, - tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(in.FieldMap, removeFieldPrefixArray)), - tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(in.FieldMap, removeFieldPrefixArray)), - }) - modelContent = replaceDefaultVar(in.CGenDaoInternalInput, modelContent) in.genItems.AppendGeneratedFilePath(path) if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil { mlog.Fatalf("writing content to '%s' failed: %v", path, err) diff --git a/cmd/gf/internal/cmd/gendao/gendao_do.go b/cmd/gf/internal/cmd/gendao/gendao_do.go index 7a3cb542c0e..98afd6d7837 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_do.go +++ b/cmd/gf/internal/cmd/gendao/gendao_do.go @@ -12,8 +12,8 @@ import ( "path/filepath" "strings" - "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gview" "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" @@ -78,16 +78,23 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) { func generateDoContent( ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string, ) string { - doContent := gstr.ReplaceByMap( - getTemplateFromPathOrDefault(in.TplDaoDoPath, consts.TemplateGenDaoDoContent), - g.MapStrStr{ - tplVarTableName: tableName, - tplVarPackageImports: getImportPartContent(ctx, structDefine, true, nil), - tplVarTableNameCamelCase: tableNameCamelCase, - tplVarStructDefine: structDefine, - tplVarPackageName: filepath.Base(in.DoPath), - }, + var ( + tplContent = getTemplateFromPathOrDefault( + in.TplDaoDoPath, consts.TemplateGenDaoDoContent, + ) ) - doContent = replaceDefaultVar(in, doContent) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarTableName: tableName, + tplVarPackageImports: getImportPartContent(ctx, structDefine, true, nil), + tplVarTableNameCamelCase: tableNameCamelCase, + tplVarStructDefine: structDefine, + tplVarPackageName: filepath.Base(in.DoPath), + }) + assignDefaultVar(tplView, in) + doContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } return doContent } diff --git a/cmd/gf/internal/cmd/gendao/gendao_entity.go b/cmd/gf/internal/cmd/gendao/gendao_entity.go index 9d38db428ec..8b059e8108c 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_entity.go +++ b/cmd/gf/internal/cmd/gendao/gendao_entity.go @@ -11,8 +11,8 @@ import ( "path/filepath" "strings" - "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gview" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/cmd/gf/v2/internal/consts" @@ -63,16 +63,23 @@ func generateEntity(ctx context.Context, in CGenDaoInternalInput) { func generateEntityContent( ctx context.Context, in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string, appendImports []string, ) string { - entityContent := gstr.ReplaceByMap( - getTemplateFromPathOrDefault(in.TplDaoEntityPath, consts.TemplateGenDaoEntityContent), - g.MapStrStr{ - tplVarTableName: tableName, - tplVarPackageImports: getImportPartContent(ctx, structDefine, false, appendImports), - tplVarTableNameCamelCase: tableNameCamelCase, - tplVarStructDefine: structDefine, - tplVarPackageName: filepath.Base(in.EntityPath), - }, + var ( + tplContent = getTemplateFromPathOrDefault( + in.TplDaoEntityPath, consts.TemplateGenDaoEntityContent, + ) ) - entityContent = replaceDefaultVar(in, entityContent) + tplView.ClearAssigns() + tplView.Assigns(gview.Params{ + tplVarTableName: tableName, + tplVarPackageImports: getImportPartContent(ctx, structDefine, false, appendImports), + tplVarTableNameCamelCase: tableNameCamelCase, + tplVarStructDefine: structDefine, + tplVarPackageName: filepath.Base(in.EntityPath), + }) + assignDefaultVar(tplView, in) + entityContent, err := tplView.ParseContent(ctx, tplContent) + if err != nil { + mlog.Fatalf("parsing template content failed: %v", err) + } return entityContent } diff --git a/cmd/gf/internal/cmd/gendao/gendao_gen_item.go b/cmd/gf/internal/cmd/gendao/gendao_gen_item.go index 09fa7fe125b..fcb7bf9beeb 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_gen_item.go +++ b/cmd/gf/internal/cmd/gendao/gendao_gen_item.go @@ -38,14 +38,14 @@ func (i *CGenDaoInternalGenItems) SetClear(clear bool) { i.Items[i.index].Clear = clear } -func (i CGenDaoInternalGenItems) AppendDirPath(storageDirPath string) { +func (i *CGenDaoInternalGenItems) AppendDirPath(storageDirPath string) { i.Items[i.index].StorageDirPaths = append( i.Items[i.index].StorageDirPaths, storageDirPath, ) } -func (i CGenDaoInternalGenItems) AppendGeneratedFilePath(generatedFilePath string) { +func (i *CGenDaoInternalGenItems) AppendGeneratedFilePath(generatedFilePath string) { i.Items[i.index].GeneratedFilePaths = append( i.Items[i.index].GeneratedFilePaths, generatedFilePath, diff --git a/cmd/gf/internal/cmd/gendao/gendao_tag.go b/cmd/gf/internal/cmd/gendao/gendao_tag.go new file mode 100644 index 00000000000..0487184e0d9 --- /dev/null +++ b/cmd/gf/internal/cmd/gendao/gendao_tag.go @@ -0,0 +1,149 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gendao + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gtag" +) + +const ( + CGenDaoConfig = `gfcli.gen.dao` + CGenDaoUsage = `gf gen dao [OPTION]` + CGenDaoBrief = `automatically generate go files for dao/do/entity` + CGenDaoEg = ` +gf gen dao +gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test" +gf gen dao -p ./model -g user-center -t user,user_detail,user_login +gf gen dao -r user_ +` + + CGenDaoAd = ` +CONFIGURATION SUPPORT + Options are also supported by configuration file. + 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: + 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" + 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 +` + CGenDaoBriefPath = `directory path for generated files` + CGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame` + CGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','` + CGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','` + CGenDaoBriefPrefix = `add prefix for all table of specified link/database tables` + CGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','` + CGenDaoBriefRemoveFieldPrefix = `remove specified prefix of the field, multiple prefix separated with ','` + CGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables` + CGenDaoBriefWithTime = `add created time for auto produced go files` + CGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables` + CGenDaoBriefImportPrefix = `custom import prefix for generated go files` + CGenDaoBriefDaoPath = `directory path for storing generated dao files under path` + CGenDaoBriefDoPath = `directory path for storing generated do files under path` + CGenDaoBriefEntityPath = `directory path for storing generated entity files under path` + CGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder` + CGenDaoBriefModelFile = `custom file name for storing generated model content` + CGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default` + CGenDaoBriefDescriptionTag = `add comment to description tag for each field` + CGenDaoBriefNoJsonTag = `no json tag will be added for each field` + CGenDaoBriefNoModelComment = `no model comment will be added for each field` + CGenDaoBriefClear = `delete all generated go files that do not exist in database` + CGenDaoBriefTypeMapping = `custom local type mapping for generated struct attributes relevant to fields of table` + CGenDaoBriefFieldMapping = `custom local type mapping for generated struct attributes relevant to specific fields of table` + CGenDaoBriefShardingPattern = `sharding pattern for table name, e.g. "users_?" will be replace tables "users_001,users_002,..." to "users" dao` + CGenDaoBriefGroup = ` +specifying the configuration group name of database for generated ORM instance, +it's not necessary and the default value is "default" +` + CGenDaoBriefJsonCase = ` +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 | +` + CGenDaoBriefTplDaoIndexPath = `template file path for dao index file` + CGenDaoBriefTplDaoInternalPath = `template file path for dao internal file` + CGenDaoBriefTplDaoDoPathPath = `template file path for dao do file` + CGenDaoBriefTplDaoEntityPath = `template file path for dao entity file` + + tplVarTableName = `TplTableName` + tplVarTableNameCamelCase = `TplTableNameCamelCase` + tplVarTableNameCamelLowerCase = `TplTableNameCamelLowerCase` + tplVarTableSharding = `TplTableSharding` + tplVarPackageImports = `TplPackageImports` + tplVarImportPrefix = `TplImportPrefix` + tplVarStructDefine = `TplStructDefine` + tplVarColumnDefine = `TplColumnDefine` + tplVarColumnNames = `TplColumnNames` + tplVarGroupName = `TplGroupName` + tplVarDatetimeStr = `TplDatetimeStr` + tplVarCreatedAtDatetimeStr = `TplCreatedAtDatetimeStr` + tplVarPackageName = `TplPackageName` +) + +func init() { + gtag.Sets(g.MapStrStr{ + `CGenDaoConfig`: CGenDaoConfig, + `CGenDaoUsage`: CGenDaoUsage, + `CGenDaoBrief`: CGenDaoBrief, + `CGenDaoEg`: CGenDaoEg, + `CGenDaoAd`: CGenDaoAd, + `CGenDaoBriefPath`: CGenDaoBriefPath, + `CGenDaoBriefLink`: CGenDaoBriefLink, + `CGenDaoBriefTables`: CGenDaoBriefTables, + `CGenDaoBriefTablesEx`: CGenDaoBriefTablesEx, + `CGenDaoBriefPrefix`: CGenDaoBriefPrefix, + `CGenDaoBriefRemovePrefix`: CGenDaoBriefRemovePrefix, + `CGenDaoBriefRemoveFieldPrefix`: CGenDaoBriefRemoveFieldPrefix, + `CGenDaoBriefStdTime`: CGenDaoBriefStdTime, + `CGenDaoBriefWithTime`: CGenDaoBriefWithTime, + `CGenDaoBriefDaoPath`: CGenDaoBriefDaoPath, + `CGenDaoBriefDoPath`: CGenDaoBriefDoPath, + `CGenDaoBriefEntityPath`: CGenDaoBriefEntityPath, + `CGenDaoBriefGJsonSupport`: CGenDaoBriefGJsonSupport, + `CGenDaoBriefImportPrefix`: CGenDaoBriefImportPrefix, + `CGenDaoBriefOverwriteDao`: CGenDaoBriefOverwriteDao, + `CGenDaoBriefModelFile`: CGenDaoBriefModelFile, + `CGenDaoBriefModelFileForDao`: CGenDaoBriefModelFileForDao, + `CGenDaoBriefDescriptionTag`: CGenDaoBriefDescriptionTag, + `CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag, + `CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment, + `CGenDaoBriefClear`: CGenDaoBriefClear, + `CGenDaoBriefTypeMapping`: CGenDaoBriefTypeMapping, + `CGenDaoBriefFieldMapping`: CGenDaoBriefFieldMapping, + `CGenDaoBriefShardingPattern`: CGenDaoBriefShardingPattern, + `CGenDaoBriefGroup`: CGenDaoBriefGroup, + `CGenDaoBriefJsonCase`: CGenDaoBriefJsonCase, + `CGenDaoBriefTplDaoIndexPath`: CGenDaoBriefTplDaoIndexPath, + `CGenDaoBriefTplDaoInternalPath`: CGenDaoBriefTplDaoInternalPath, + `CGenDaoBriefTplDaoDoPathPath`: CGenDaoBriefTplDaoDoPathPath, + `CGenDaoBriefTplDaoEntityPath`: CGenDaoBriefTplDaoEntityPath, + }) +} diff --git a/cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/internal/table_user.go b/cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/internal/table_user.go index 68d96fb0619..ea8bec0c63a 100644 --- a/cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/internal/table_user.go +++ b/cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/internal/table_user.go @@ -13,9 +13,10 @@ import ( // TableUserDao is the data access object for the table table_user. type TableUserDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of the current DAO. - columns TableUserColumns // columns contains all the column names of Table for convenient usage. + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns TableUserColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. } // TableUserColumns defines and stores column names for the table table_user. @@ -41,11 +42,12 @@ var tableUserColumns = TableUserColumns{ } // NewTableUserDao creates and returns a new DAO object for table data access. -func NewTableUserDao() *TableUserDao { +func NewTableUserDao(handlers ...gdb.ModelHandler) *TableUserDao { return &TableUserDao{ - group: "test", - table: "table_user", - columns: tableUserColumns, + group: "test", + table: "table_user", + columns: tableUserColumns, + handlers: handlers, } } @@ -71,7 +73,11 @@ func (dao *TableUserDao) Group() string { // Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. func (dao *TableUserDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) } // Transaction wraps the transaction logic using function f. diff --git a/cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/table_user.go b/cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/table_user.go index 28d2d47973d..22fe176f871 100644 --- a/cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/table_user.go +++ b/cmd/gf/internal/cmd/testdata/gendao/generated_user/dao/table_user.go @@ -8,20 +8,15 @@ import ( "for-gendao-test/pkg/dao/internal" ) -// internalTableUserDao is an internal type for wrapping the internal DAO implementation. -type internalTableUserDao = *internal.TableUserDao - // tableUserDao is the data access object for the table table_user. // You can define custom methods on it to extend its functionality as needed. type tableUserDao struct { - internalTableUserDao + *internal.TableUserDao } var ( // TableUser is a globally accessible object for table table_user operations. - TableUser = tableUserDao{ - internal.NewTableUserDao(), - } + TableUser = tableUserDao{internal.NewTableUserDao()} ) // Add your custom methods and functionality below. diff --git a/cmd/gf/internal/cmd/testdata/gendao/generated_user_field_mapping/dao/internal/table_user.go b/cmd/gf/internal/cmd/testdata/gendao/generated_user_field_mapping/dao/internal/table_user.go index 68d96fb0619..ea8bec0c63a 100644 --- a/cmd/gf/internal/cmd/testdata/gendao/generated_user_field_mapping/dao/internal/table_user.go +++ b/cmd/gf/internal/cmd/testdata/gendao/generated_user_field_mapping/dao/internal/table_user.go @@ -13,9 +13,10 @@ import ( // TableUserDao is the data access object for the table table_user. type TableUserDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of the current DAO. - columns TableUserColumns // columns contains all the column names of Table for convenient usage. + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns TableUserColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. } // TableUserColumns defines and stores column names for the table table_user. @@ -41,11 +42,12 @@ var tableUserColumns = TableUserColumns{ } // NewTableUserDao creates and returns a new DAO object for table data access. -func NewTableUserDao() *TableUserDao { +func NewTableUserDao(handlers ...gdb.ModelHandler) *TableUserDao { return &TableUserDao{ - group: "test", - table: "table_user", - columns: tableUserColumns, + group: "test", + table: "table_user", + columns: tableUserColumns, + handlers: handlers, } } @@ -71,7 +73,11 @@ func (dao *TableUserDao) Group() string { // Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. func (dao *TableUserDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) } // Transaction wraps the transaction logic using function f. diff --git a/cmd/gf/internal/cmd/testdata/gendao/generated_user_field_mapping/dao/table_user.go b/cmd/gf/internal/cmd/testdata/gendao/generated_user_field_mapping/dao/table_user.go index 28d2d47973d..22fe176f871 100644 --- a/cmd/gf/internal/cmd/testdata/gendao/generated_user_field_mapping/dao/table_user.go +++ b/cmd/gf/internal/cmd/testdata/gendao/generated_user_field_mapping/dao/table_user.go @@ -8,20 +8,15 @@ import ( "for-gendao-test/pkg/dao/internal" ) -// internalTableUserDao is an internal type for wrapping the internal DAO implementation. -type internalTableUserDao = *internal.TableUserDao - // tableUserDao is the data access object for the table table_user. // You can define custom methods on it to extend its functionality as needed. type tableUserDao struct { - internalTableUserDao + *internal.TableUserDao } var ( // TableUser is a globally accessible object for table table_user operations. - TableUser = tableUserDao{ - internal.NewTableUserDao(), - } + TableUser = tableUserDao{internal.NewTableUserDao()} ) // Add your custom methods and functionality below. diff --git a/cmd/gf/internal/cmd/testdata/gendao/generated_user_sqlite3/dao/internal/table_user.go b/cmd/gf/internal/cmd/testdata/gendao/generated_user_sqlite3/dao/internal/table_user.go index 8484c101957..6bdfc918360 100644 --- a/cmd/gf/internal/cmd/testdata/gendao/generated_user_sqlite3/dao/internal/table_user.go +++ b/cmd/gf/internal/cmd/testdata/gendao/generated_user_sqlite3/dao/internal/table_user.go @@ -13,9 +13,10 @@ import ( // TableUserDao is the data access object for the table table_user. type TableUserDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of the current DAO. - columns TableUserColumns // columns contains all the column names of Table for convenient usage. + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns TableUserColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. } // TableUserColumns defines and stores column names for the table table_user. @@ -39,11 +40,12 @@ var tableUserColumns = TableUserColumns{ } // NewTableUserDao creates and returns a new DAO object for table data access. -func NewTableUserDao() *TableUserDao { +func NewTableUserDao(handlers ...gdb.ModelHandler) *TableUserDao { return &TableUserDao{ - group: "test", - table: "table_user", - columns: tableUserColumns, + group: "test", + table: "table_user", + columns: tableUserColumns, + handlers: handlers, } } @@ -69,7 +71,11 @@ func (dao *TableUserDao) Group() string { // Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. func (dao *TableUserDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) } // Transaction wraps the transaction logic using function f. diff --git a/cmd/gf/internal/cmd/testdata/gendao/generated_user_sqlite3/dao/table_user.go b/cmd/gf/internal/cmd/testdata/gendao/generated_user_sqlite3/dao/table_user.go index 28d2d47973d..22fe176f871 100644 --- a/cmd/gf/internal/cmd/testdata/gendao/generated_user_sqlite3/dao/table_user.go +++ b/cmd/gf/internal/cmd/testdata/gendao/generated_user_sqlite3/dao/table_user.go @@ -8,20 +8,15 @@ import ( "for-gendao-test/pkg/dao/internal" ) -// internalTableUserDao is an internal type for wrapping the internal DAO implementation. -type internalTableUserDao = *internal.TableUserDao - // tableUserDao is the data access object for the table table_user. // You can define custom methods on it to extend its functionality as needed. type tableUserDao struct { - internalTableUserDao + *internal.TableUserDao } var ( // TableUser is a globally accessible object for table table_user operations. - TableUser = tableUserDao{ - internal.NewTableUserDao(), - } + TableUser = tableUserDao{internal.NewTableUserDao()} ) // Add your custom methods and functionality below. diff --git a/cmd/gf/internal/cmd/testdata/gendao/generated_user_type_mapping/dao/internal/table_user.go b/cmd/gf/internal/cmd/testdata/gendao/generated_user_type_mapping/dao/internal/table_user.go index 68d96fb0619..ea8bec0c63a 100644 --- a/cmd/gf/internal/cmd/testdata/gendao/generated_user_type_mapping/dao/internal/table_user.go +++ b/cmd/gf/internal/cmd/testdata/gendao/generated_user_type_mapping/dao/internal/table_user.go @@ -13,9 +13,10 @@ import ( // TableUserDao is the data access object for the table table_user. type TableUserDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of the current DAO. - columns TableUserColumns // columns contains all the column names of Table for convenient usage. + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns TableUserColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. } // TableUserColumns defines and stores column names for the table table_user. @@ -41,11 +42,12 @@ var tableUserColumns = TableUserColumns{ } // NewTableUserDao creates and returns a new DAO object for table data access. -func NewTableUserDao() *TableUserDao { +func NewTableUserDao(handlers ...gdb.ModelHandler) *TableUserDao { return &TableUserDao{ - group: "test", - table: "table_user", - columns: tableUserColumns, + group: "test", + table: "table_user", + columns: tableUserColumns, + handlers: handlers, } } @@ -71,7 +73,11 @@ func (dao *TableUserDao) Group() string { // Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. func (dao *TableUserDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) } // Transaction wraps the transaction logic using function f. diff --git a/cmd/gf/internal/cmd/testdata/gendao/generated_user_type_mapping/dao/table_user.go b/cmd/gf/internal/cmd/testdata/gendao/generated_user_type_mapping/dao/table_user.go index 28d2d47973d..22fe176f871 100644 --- a/cmd/gf/internal/cmd/testdata/gendao/generated_user_type_mapping/dao/table_user.go +++ b/cmd/gf/internal/cmd/testdata/gendao/generated_user_type_mapping/dao/table_user.go @@ -8,20 +8,15 @@ import ( "for-gendao-test/pkg/dao/internal" ) -// internalTableUserDao is an internal type for wrapping the internal DAO implementation. -type internalTableUserDao = *internal.TableUserDao - // tableUserDao is the data access object for the table table_user. // You can define custom methods on it to extend its functionality as needed. type tableUserDao struct { - internalTableUserDao + *internal.TableUserDao } var ( // TableUser is a globally accessible object for table table_user operations. - TableUser = tableUserDao{ - internal.NewTableUserDao(), - } + TableUser = tableUserDao{internal.NewTableUserDao()} ) // Add your custom methods and functionality below. diff --git a/cmd/gf/internal/cmd/testdata/gendao/sharding/sharding.sql b/cmd/gf/internal/cmd/testdata/gendao/sharding/sharding.sql new file mode 100644 index 00000000000..2c5f70bab35 --- /dev/null +++ b/cmd/gf/internal/cmd/testdata/gendao/sharding/sharding.sql @@ -0,0 +1,45 @@ +CREATE TABLE `single_table` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID', + `passport` varchar(45) NOT NULL COMMENT 'User Passport', + `password` varchar(45) NOT NULL COMMENT 'User Password', + `nickname` varchar(45) NOT NULL COMMENT 'User Nickname', + `score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.', + `create_at` datetime DEFAULT NULL COMMENT 'Created Time', + `update_at` datetime DEFAULT NULL COMMENT 'Updated Time', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE `users_0001` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID', + `passport` varchar(45) NOT NULL COMMENT 'User Passport', + `password` varchar(45) NOT NULL COMMENT 'User Password', + `nickname` varchar(45) NOT NULL COMMENT 'User Nickname', + `score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.', + `create_at` datetime DEFAULT NULL COMMENT 'Created Time', + `update_at` datetime DEFAULT NULL COMMENT 'Updated Time', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `users_0002` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID', + `passport` varchar(45) NOT NULL COMMENT 'User Passport', + `password` varchar(45) NOT NULL COMMENT 'User Password', + `nickname` varchar(45) NOT NULL COMMENT 'User Nickname', + `score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.', + `create_at` datetime DEFAULT NULL COMMENT 'Created Time', + `update_at` datetime DEFAULT NULL COMMENT 'Updated Time', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE `users_0003` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID', + `passport` varchar(45) NOT NULL COMMENT 'User Passport', + `password` varchar(45) NOT NULL COMMENT 'User Password', + `nickname` varchar(45) NOT NULL COMMENT 'User Nickname', + `score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.', + `create_at` datetime DEFAULT NULL COMMENT 'Created Time', + `update_at` datetime DEFAULT NULL COMMENT 'Updated Time', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/cmd/gf/internal/cmd/testdata/issue/3749/dao/internal/table_user.go b/cmd/gf/internal/cmd/testdata/issue/3749/dao/internal/table_user.go index bb7bf30a875..9a3958bc9ef 100644 --- a/cmd/gf/internal/cmd/testdata/issue/3749/dao/internal/table_user.go +++ b/cmd/gf/internal/cmd/testdata/issue/3749/dao/internal/table_user.go @@ -13,9 +13,10 @@ import ( // TableUserDao is the data access object for the table table_user. type TableUserDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of the current DAO. - columns TableUserColumns // columns contains all the column names of Table for convenient usage. + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns TableUserColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. } // TableUserColumns defines and stores column names for the table table_user. @@ -41,11 +42,12 @@ var tableUserColumns = TableUserColumns{ } // NewTableUserDao creates and returns a new DAO object for table data access. -func NewTableUserDao() *TableUserDao { +func NewTableUserDao(handlers ...gdb.ModelHandler) *TableUserDao { return &TableUserDao{ - group: "test", - table: "table_user", - columns: tableUserColumns, + group: "test", + table: "table_user", + columns: tableUserColumns, + handlers: handlers, } } @@ -71,7 +73,11 @@ func (dao *TableUserDao) Group() string { // Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. func (dao *TableUserDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) } // Transaction wraps the transaction logic using function f. diff --git a/cmd/gf/internal/cmd/testdata/issue/3749/dao/table_user.go b/cmd/gf/internal/cmd/testdata/issue/3749/dao/table_user.go index 28d2d47973d..22fe176f871 100644 --- a/cmd/gf/internal/cmd/testdata/issue/3749/dao/table_user.go +++ b/cmd/gf/internal/cmd/testdata/issue/3749/dao/table_user.go @@ -8,20 +8,15 @@ import ( "for-gendao-test/pkg/dao/internal" ) -// internalTableUserDao is an internal type for wrapping the internal DAO implementation. -type internalTableUserDao = *internal.TableUserDao - // tableUserDao is the data access object for the table table_user. // You can define custom methods on it to extend its functionality as needed. type tableUserDao struct { - internalTableUserDao + *internal.TableUserDao } var ( // TableUser is a globally accessible object for table table_user operations. - TableUser = tableUserDao{ - internal.NewTableUserDao(), - } + TableUser = tableUserDao{internal.NewTableUserDao()} ) // Add your custom methods and functionality below. diff --git a/cmd/gf/internal/consts/consts_gen_dao_template_dao.go b/cmd/gf/internal/consts/consts_gen_dao_template_dao.go index c1e20fbe770..d1a82bc03d2 100644 --- a/cmd/gf/internal/consts/consts_gen_dao_template_dao.go +++ b/cmd/gf/internal/consts/consts_gen_dao_template_dao.go @@ -11,35 +11,54 @@ const TemplateGenDaoIndexContent = ` // This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. // ================================================================================= -package {TplPackageName} +package {{.TplPackageName}} import ( - "{TplImportPrefix}/internal" + "{{.TplImportPrefix}}/internal" ) -// internal{TplTableNameCamelCase}Dao is an internal type for wrapping the internal DAO implementation. -type internal{TplTableNameCamelCase}Dao = *internal.{TplTableNameCamelCase}Dao - -// {TplTableNameCamelLowerCase}Dao is the data access object for the table {TplTableName}. +// {{.TplTableNameCamelLowerCase}}Dao is the data access object for the table {{.TplTableName}}. // You can define custom methods on it to extend its functionality as needed. -type {TplTableNameCamelLowerCase}Dao struct { - internal{TplTableNameCamelCase}Dao +type {{.TplTableNameCamelLowerCase}}Dao struct { + *internal.{{.TplTableNameCamelCase}}Dao } var ( - // {TplTableNameCamelCase} is a globally accessible object for table {TplTableName} operations. - {TplTableNameCamelCase} = {TplTableNameCamelLowerCase}Dao{ - internal.New{TplTableNameCamelCase}Dao(), + // {{.TplTableNameCamelCase}} is a globally accessible object for table {{.TplTableName}} operations. + {{.TplTableNameCamelCase}} = {{.TplTableNameCamelLowerCase}}Dao{ +{{- if .TplTableSharding -}} + internal.New{{.TplTableNameCamelCase}}Dao(userShardingHandler), +{{- else -}} + internal.New{{.TplTableNameCamelCase}}Dao(), +{{- end -}} } ) +{{if .TplTableSharding -}} +// userShardingHandler is the handler for sharding operations. +// You can fill this sharding handler with your custom implementation. +func userShardingHandler(m *gdb.Model) *gdb.Model { + m = m.Sharding(gdb.ShardingConfig{ + Table: gdb.ShardingTableConfig{ + Enable: true, + Prefix: "", + // Replace Rule field with your custom sharding rule. + // Or you can use "&gdb.DefaultShardingRule{}" for default sharding rule. + Rule: nil, + }, + Schema: gdb.ShardingSchemaConfig{}, + }) + return m +} +{{- end}} + // Add your custom methods and functionality below. ` const TemplateGenDaoInternalContent = ` // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr} +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} // ========================================================================== package internal @@ -51,55 +70,61 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// {TplTableNameCamelCase}Dao is the data access object for the table {TplTableName}. -type {TplTableNameCamelCase}Dao struct { +// {{.TplTableNameCamelCase}}Dao is the data access object for the table {{.TplTableName}}. +type {{.TplTableNameCamelCase}}Dao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. - columns {TplTableNameCamelCase}Columns // columns contains all the column names of Table for convenient usage. + columns {{.TplTableNameCamelCase}}Columns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. } -// {TplTableNameCamelCase}Columns defines and stores column names for the table {TplTableName}. -type {TplTableNameCamelCase}Columns struct { - {TplColumnDefine} +// {{.TplTableNameCamelCase}}Columns defines and stores column names for the table {{.TplTableName}}. +type {{.TplTableNameCamelCase}}Columns struct { + {{.TplColumnDefine}} } -// {TplTableNameCamelLowerCase}Columns holds the columns for the table {TplTableName}. -var {TplTableNameCamelLowerCase}Columns = {TplTableNameCamelCase}Columns{ - {TplColumnNames} +// {{.TplTableNameCamelLowerCase}}Columns holds the columns for the table {{.TplTableName}}. +var {{.TplTableNameCamelLowerCase}}Columns = {{.TplTableNameCamelCase}}Columns{ + {{.TplColumnNames}} } -// New{TplTableNameCamelCase}Dao creates and returns a new DAO object for table data access. -func New{TplTableNameCamelCase}Dao() *{TplTableNameCamelCase}Dao { - return &{TplTableNameCamelCase}Dao{ - group: "{TplGroupName}", - table: "{TplTableName}", - columns: {TplTableNameCamelLowerCase}Columns, +// New{{.TplTableNameCamelCase}}Dao creates and returns a new DAO object for table data access. +func New{{.TplTableNameCamelCase}}Dao(handlers ...gdb.ModelHandler) *{{.TplTableNameCamelCase}}Dao { + return &{{.TplTableNameCamelCase}}Dao{ + group: "{{.TplGroupName}}", + table: "{{.TplTableName}}", + columns: {{.TplTableNameCamelLowerCase}}Columns, + handlers: handlers, } } // DB retrieves and returns the underlying raw database management object of the current DAO. -func (dao *{TplTableNameCamelCase}Dao) DB() gdb.DB { +func (dao *{{.TplTableNameCamelCase}}Dao) DB() gdb.DB { return g.DB(dao.group) } // Table returns the table name of the current DAO. -func (dao *{TplTableNameCamelCase}Dao) Table() string { +func (dao *{{.TplTableNameCamelCase}}Dao) Table() string { return dao.table } // Columns returns all column names of the current DAO. -func (dao *{TplTableNameCamelCase}Dao) Columns() {TplTableNameCamelCase}Columns { +func (dao *{{.TplTableNameCamelCase}}Dao) Columns() {{.TplTableNameCamelCase}}Columns { return dao.columns } // Group returns the database configuration group name of the current DAO. -func (dao *{TplTableNameCamelCase}Dao) Group() string { +func (dao *{{.TplTableNameCamelCase}}Dao) Group() string { return dao.group } // Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. -func (dao *{TplTableNameCamelCase}Dao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) +func (dao *{{.TplTableNameCamelCase}}Dao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) } // Transaction wraps the transaction logic using function f. @@ -108,7 +133,7 @@ func (dao *{TplTableNameCamelCase}Dao) Ctx(ctx context.Context) *gdb.Model { // // Note: Do not commit or roll back the transaction in function f, // as it is automatically handled by this function. -func (dao *{TplTableNameCamelCase}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { +func (dao *{{.TplTableNameCamelCase}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } ` diff --git a/cmd/gf/internal/consts/consts_gen_dao_template_do.go b/cmd/gf/internal/consts/consts_gen_dao_template_do.go index 320aaa9e2a3..97a404e267c 100644 --- a/cmd/gf/internal/consts/consts_gen_dao_template_do.go +++ b/cmd/gf/internal/consts/consts_gen_dao_template_do.go @@ -8,13 +8,13 @@ package consts const TemplateGenDaoDoContent = ` // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr} +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} // ================================================================================= -package {TplPackageName} +package {{.TplPackageName}} -{TplPackageImports} +{{.TplPackageImports}} -// {TplTableNameCamelCase} is the golang structure of table {TplTableName} for DAO operations like Where/Data. -{TplStructDefine} +// {{.TplTableNameCamelCase}} is the golang structure of table {{.TplTableName}} for DAO operations like Where/Data. +{{.TplStructDefine}} ` diff --git a/cmd/gf/internal/consts/consts_gen_dao_template_entity.go b/cmd/gf/internal/consts/consts_gen_dao_template_entity.go index 67a868ec700..c54713ae148 100644 --- a/cmd/gf/internal/consts/consts_gen_dao_template_entity.go +++ b/cmd/gf/internal/consts/consts_gen_dao_template_entity.go @@ -8,13 +8,13 @@ package consts const TemplateGenDaoEntityContent = ` // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr} +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}} // ================================================================================= -package {TplPackageName} +package {{.TplPackageName}} -{TplPackageImports} +{{.TplPackageImports}} -// {TplTableNameCamelCase} is the golang structure for table {TplTableName}. -{TplStructDefine} +// {{.TplTableNameCamelCase}} is the golang structure for table {{.TplTableName}}. +{{.TplStructDefine}} ` diff --git a/os/gview/gview.go b/os/gview/gview.go index 3b60e8a1174..acd395b4828 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -13,7 +13,6 @@ package gview import ( "context" - "github.com/gogf/gf/v2" "github.com/gogf/gf/v2/container/garray" "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/internal/intlog" @@ -24,16 +23,16 @@ import ( // View object for template engine. type View struct { - searchPaths *garray.StrArray // Searching array for path, NOT concurrent-safe for performance purpose. - data map[string]interface{} // Global template variables. - funcMap map[string]interface{} // Global template function map. - fileCacheMap *gmap.StrAnyMap // File cache map. - config Config // Extra configuration for the view. + searchPaths *garray.StrArray // Searching array for path, NOT concurrent-safe for performance purpose. + data map[string]any // Global template variables. + funcMap map[string]any // Global template function map. + fileCacheMap *gmap.StrAnyMap // File cache map. + config Config // Extra configuration for the view. } type ( - Params = map[string]interface{} // Params is type for template params. - FuncMap = map[string]interface{} // FuncMap is type for custom template functions. + Params = map[string]any // Params is type for template params. + FuncMap = map[string]any // FuncMap is type for custom template functions. ) const ( @@ -68,8 +67,8 @@ func New(path ...string) *View { ) view := &View{ searchPaths: garray.NewStrArray(), - data: make(map[string]interface{}), - funcMap: make(map[string]interface{}), + data: make(map[string]any), + funcMap: make(map[string]any), fileCacheMap: gmap.NewStrAnyMap(true), config: DefaultConfig(), } @@ -110,11 +109,8 @@ func New(path ...string) *View { } } } + // set default delimiters. view.SetDelimiters("{{", "}}") - // default build-in variables. - view.data["GF"] = map[string]interface{}{ - "version": gf.VERSION, - } // default build-in functions. view.BindFuncMap(FuncMap{ "eq": view.buildInFuncEq, diff --git a/os/gview/gview_config.go b/os/gview/gview_config.go index 9323f798b39..58d6108a206 100644 --- a/os/gview/gview_config.go +++ b/os/gview/gview_config.go @@ -223,6 +223,11 @@ func (view *View) Assign(key string, value interface{}) { view.data[key] = value } +// ClearAssigns trunk all global template variables assignments. +func (view *View) ClearAssigns() { + view.data = make(map[string]interface{}) +} + // SetDefaultFile sets default template file for parsing. func (view *View) SetDefaultFile(file string) { view.config.DefaultFile = file diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index 0b20e7772d9..364a065add4 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -15,6 +15,7 @@ import ( "strings" texttpl "text/template" + "github.com/gogf/gf/v2" "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/encoding/ghash" "github.com/gogf/gf/v2/errors/gcode" @@ -101,7 +102,11 @@ func (view *View) ParseContent(ctx context.Context, content string, params ...Pa } // Option for template parsing. -type Option struct { +// Deprecated: use Options instead. +type Option = Options + +// Options for template parsing. +type Options struct { File string // Template file path in absolute or relative to searching paths. Content string // Template content, it ignores `File` if `Content` is given. Orphan bool // If true, the `File` is considered as a single file parsing without files recursively parsing from its folder. @@ -109,15 +114,21 @@ type Option struct { } // ParseOption implements template parsing using Option. +// Deprecated: use ParseWithOptions instead. func (view *View) ParseOption(ctx context.Context, option Option) (result string, err error) { - if option.Content != "" { - return view.doParseContent(ctx, option.Content, option.Params) + return view.ParseWithOptions(ctx, option) +} + +// ParseWithOptions implements template parsing using Option. +func (view *View) ParseWithOptions(ctx context.Context, opts Options) (result string, err error) { + if opts.Content != "" { + return view.doParseContent(ctx, opts.Content, opts.Params) } - if option.File == "" { + if opts.File == "" { return "", gerror.New(`template file cannot be empty`) } // It caches the file, folder, and content to enhance performance. - r := view.fileCacheMap.GetOrSetFuncLock(option.File, func() interface{} { + r := view.fileCacheMap.GetOrSetFuncLock(opts.File, func() any { var ( path string folder string @@ -125,7 +136,7 @@ func (view *View) ParseOption(ctx context.Context, option Option) (result string resource *gres.File ) // Searching the absolute file path for `file`. - path, folder, resource, err = view.searchFile(ctx, option.File) + path, folder, resource, err = view.searchFile(ctx, opts.File) if err != nil { return nil } @@ -136,12 +147,14 @@ func (view *View) ParseOption(ctx context.Context, option Option) (result string } // Monitor template files changes using fsnotify asynchronously. if resource == nil { - if _, err = gfsnotify.AddOnce("gview.Parse:"+folder, folder, func(event *gfsnotify.Event) { - // CLEAR THEM ALL. - view.fileCacheMap.Clear() - templates.Clear() - gfsnotify.Exit() - }); err != nil { + if _, err = gfsnotify.AddOnce( + "gview.Parse:"+folder, folder, func(event *gfsnotify.Event) { + // CLEAR THEM ALL. + view.fileCacheMap.Clear() + templates.Clear() + gfsnotify.Exit() + }, + ); err != nil { intlog.Errorf(ctx, `%+v`, err) } } @@ -160,11 +173,11 @@ func (view *View) ParseOption(ctx context.Context, option Option) (result string return "", nil } // If it's an Orphan option, it just parses the single file by ParseContent. - if option.Orphan { - return view.doParseContent(ctx, item.content, option.Params) + if opts.Orphan { + return view.doParseContent(ctx, item.content, opts.Params) } // Get the template object instance for `folder`. - var tpl interface{} + var tpl any tpl, err = view.getTemplate(item.path, item.folder, fmt.Sprintf(`*%s`, gfile.Ext(item.path))) if err != nil { return "", err @@ -183,34 +196,8 @@ func (view *View) ParseOption(ctx context.Context, option Option) (result string if err != nil { return "", err } - // Note that the template variable assignment cannot change the value - // of the existing `params` or view.data because both variables are pointers. - // It needs to merge the values of the two maps into a new map. - variables := gutil.MapMergeCopy(option.Params) - if len(view.data) > 0 { - gutil.MapMerge(variables, view.data) - } - view.setI18nLanguageFromCtx(ctx, variables) - buffer := bytes.NewBuffer(nil) - if view.config.AutoEncode { - newTpl, err := tpl.(*htmltpl.Template).Clone() - if err != nil { - return "", err - } - if err = newTpl.Execute(buffer, variables); err != nil { - return "", err - } - } else { - if err = tpl.(*texttpl.Template).Execute(buffer, variables); err != nil { - return "", err - } - } - - // TODO any graceful plan to replace ""? - result = gstr.Replace(buffer.String(), "", "") - result = view.i18nTranslate(ctx, result, variables) - return result, nil + return view.doParseContentWithStdTemplate(ctx, tpl, opts.Params) } // doParseContent parses given template content `content` with template variables `params` @@ -223,7 +210,7 @@ func (view *View) doParseContent(ctx context.Context, content string, params Par var ( err error key = fmt.Sprintf("%s_%v_%v", templateNameForContentParsing, view.config.Delimiters, view.config.AutoEncode) - tpl = templates.GetOrSetFuncLock(key, func() interface{} { + tpl = templates.GetOrSetFuncLock(key, func() any { if view.config.AutoEncode { return htmltpl.New(templateNameForContentParsing).Delims( view.config.Delimiters[0], @@ -249,10 +236,14 @@ func (view *View) doParseContent(ctx context.Context, content string, params Par err = gerror.Wrapf(err, `template parsing failed`) return "", err } + return view.doParseContentWithStdTemplate(ctx, tpl, params) +} + +func (view *View) doParseContentWithStdTemplate(ctx context.Context, tpl any, params Params) (string, error) { // Note that the template variable assignment cannot change the value // of the existing `params` or view.data because both variables are pointers. // It needs to merge the values of the two maps into a new map. - variables := gutil.MapMergeCopy(params) + variables := gutil.MapMergeCopy(params, view.getBuiltInParams()) if len(view.data) > 0 { gutil.MapMerge(variables, view.data) } @@ -261,7 +252,7 @@ func (view *View) doParseContent(ctx context.Context, content string, params Par buffer := bytes.NewBuffer(nil) if view.config.AutoEncode { var newTpl *htmltpl.Template - newTpl, err = tpl.(*htmltpl.Template).Clone() + newTpl, err := tpl.(*htmltpl.Template).Clone() if err != nil { err = gerror.Wrapf(err, `template clone failed`) return "", err @@ -271,7 +262,7 @@ func (view *View) doParseContent(ctx context.Context, content string, params Par return "", err } } else { - if err = tpl.(*texttpl.Template).Execute(buffer, variables); err != nil { + if err := tpl.(*texttpl.Template).Execute(buffer, variables); err != nil { err = gerror.Wrapf(err, `template parsing failed`) return "", err } @@ -282,14 +273,20 @@ func (view *View) doParseContent(ctx context.Context, content string, params Par return result, nil } +func (view *View) getBuiltInParams() map[string]any { + return map[string]any{ + "version": gf.VERSION, + } +} + // getTemplate returns the template object associated with given template file `path`. // It uses template cache to enhance performance, that is, it will return the same template object // with the same given `path`. It will also automatically refresh the template cache // if the template files under `path` changes (recursively). -func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interface{}, err error) { +func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl any, err error) { var ( mapKey = fmt.Sprintf("%s_%v", filePath, view.config.Delimiters) - mapFunc = func() interface{} { + mapFunc = func() any { tplName := filePath if view.config.AutoEncode { tpl = htmltpl.New(tplName).Delims( diff --git a/util/gutil/gutil_map.go b/util/gutil/gutil_map.go index 92e470434c5..854471b1c63 100644 --- a/util/gutil/gutil_map.go +++ b/util/gutil/gutil_map.go @@ -42,21 +42,21 @@ func MapDelete(data map[string]interface{}, keys ...string) { } // MapMerge merges all map from `src` to map `dst`. -func MapMerge(dst map[string]interface{}, src ...map[string]interface{}) { - if dst == nil { +func MapMerge(dstMap map[string]interface{}, srcMaps ...map[string]interface{}) { + if dstMap == nil { return } - for _, m := range src { + for _, m := range srcMaps { for k, v := range m { - dst[k] = v + dstMap[k] = v } } } // MapMergeCopy creates and returns a new map which merges all map from `src`. -func MapMergeCopy(src ...map[string]interface{}) (copy map[string]interface{}) { +func MapMergeCopy(maps ...map[string]interface{}) (copy map[string]interface{}) { copy = make(map[string]interface{}) - for _, m := range src { + for _, m := range maps { for k, v := range m { copy[k] = v }