├── .gitignore ├── test.db ├── template └── column_option.go ├── go.mod ├── function.go ├── README.md ├── gen.yaml ├── gen_test.go ├── go.sum └── yaml_generator.go /.gitignore: -------------------------------------------------------------------------------- 1 | output/** 2 | .idea -------------------------------------------------------------------------------- /test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we7coreteam/gorm-gen-yaml/HEAD/test.db -------------------------------------------------------------------------------- /template/column_option.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | // Model used as a variable because it cannot load template file after packed, params still can pass file 4 | var ColumnOptionTemplate = map[string]string{"common": `package {{Package}} 5 | 6 | import ( 7 | "database/sql/driver" 8 | ) 9 | 10 | type {{OptionStructName}} struct { 11 | 12 | } 13 | 14 | func (c {{OptionStructName}}) Value() (driver.Value, error) { 15 | return c, nil 16 | } 17 | 18 | func (c *{{OptionStructName}}) Scan(value interface{}) error { 19 | return nil 20 | } 21 | 22 | `, 23 | "json": `package {{Package}} 24 | 25 | type {{OptionStructName}} struct { 26 | 27 | } 28 | `, 29 | } 30 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/we7coreteam/gorm-gen-yaml 2 | 3 | go 1.20 4 | 5 | require ( 6 | golang.org/x/text v0.14.0 7 | golang.org/x/tools v0.6.0 8 | gopkg.in/yaml.v3 v3.0.1 9 | gorm.io/driver/sqlite v1.4.3 10 | gorm.io/gen v0.3.23 11 | gorm.io/gorm v1.25.1-0.20230505075827-e61b98d69677 12 | gorm.io/plugin/dbresolver v1.3.0 13 | ) 14 | 15 | require ( 16 | github.com/go-sql-driver/mysql v1.7.0 // indirect 17 | github.com/jinzhu/inflection v1.0.0 // indirect 18 | github.com/jinzhu/now v1.1.5 // indirect 19 | github.com/mattn/go-sqlite3 v1.14.15 // indirect 20 | golang.org/x/mod v0.8.0 // indirect 21 | golang.org/x/sys v0.5.0 // indirect 22 | gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect 23 | gorm.io/driver/mysql v1.5.1-0.20230509030346-3715c134c25b // indirect 24 | gorm.io/hints v1.1.0 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /function.go: -------------------------------------------------------------------------------- 1 | package yamlgen 2 | 3 | import ( 4 | "golang.org/x/text/cases" 5 | "golang.org/x/text/language" 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | // CamelCaseToUnderscore 驼峰单词转下划线单词 11 | func CamelCaseToUnderscore(s string) string { 12 | var output []rune 13 | for i, r := range s { 14 | if i == 0 { 15 | output = append(output, unicode.ToLower(r)) 16 | } else { 17 | if unicode.IsUpper(r) { 18 | output = append(output, '_') 19 | } 20 | 21 | output = append(output, unicode.ToLower(r)) 22 | } 23 | } 24 | return string(output) 25 | } 26 | 27 | func UnderscoreToCamelCase(name string, isUpper bool) string { 28 | caser := cases.Title(language.Und) 29 | newName := strings.ReplaceAll(caser.String(strings.ReplaceAll(name, "_", " ")), " ", "") 30 | if isUpper { 31 | return newName 32 | } else { 33 | return strings.ToLower(string(newName[0])) + newName[1:] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gorm-gen-yaml 2 | 3 | 根据 yaml 配置文件生成数据库表的 model 文件及 dao 文件。 4 | 5 | 程序会根据配置文件中声明的表之间的依赖关系,按照顺序进行生成。让你摆锐复杂的依赖关系。 6 | 7 | output 目录为根据下面配置生成出的文件。 8 | 9 | # 配置 10 | 11 | 查看配置文件 12 | 13 | # 调用 14 | 15 | ```go 16 | g := gen.NewGenerator(gen.Config{ 17 | OutPath: "./output/dao", 18 | Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode 19 | ModelPkgPath: "entity", 20 | }) 21 | g.UseDB(db) 22 | fieldOpts := []gen.ModelOpt{} 23 | yamlgen.NewYamlGenerator("./gen.yaml").UseGormGenerator(g).Generate(fieldOpts...) 24 | //g.ApplyBasic(g.GenerateAllTable(fieldOpts...)...) 25 | //g.GenerateModel(tableName) 26 | g.Execute() 27 | ``` 28 | 29 | # 测试 30 | 31 | 运行 gen_test.go 文件中的 TestParse 方法。生成完成 dao 和 model 文件后,可以删除 TestSelect 注释进行测试 32 | 33 | # 交流群 34 | 35 | QQ群:364768550 36 | 37 | 38 | -------------------------------------------------------------------------------- /gen.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | tag_json_camel: lower # lower 首字母小写驼峰,upper 首字母大写驼峰 3 | relation: 4 | - table: club 5 | column: 6 | # 定义 club 表中的 content 字段为 json 类型,类型必须以 Option 结尾 7 | # 查询和插入时会自动做 json 数据解析 8 | # 指定 ClubContentOption 类型,必须事先定义在 accessor(与 dao 同级)目录中 9 | # 并且该类型必须可外部访问 10 | content: 11 | serializer: json 12 | type: ClubContentOption 13 | tag: 14 | gorm: 15 | default: "''" 16 | # 指定自定义类型字段,需要自己实现 scan 和 value 方法 17 | api_info: 18 | type: ClubApiInfoOption 19 | 20 | # club_user 表中的 club_id 对应 club 表中的 id 21 | # club 表中的 applicant_id 对应 user 表中的 id 22 | relate: 23 | - table: club_user 24 | foreign_key: club_id 25 | type: has_many 26 | - table: user 27 | references: applicant_id 28 | foreign_key: id 29 | type: belongs_to 30 | - table: user 31 | relate: 32 | - table: user_oauth 33 | foreign_key: user_id 34 | type: has_one 35 | - table: tag_formula 36 | - table: formula 37 | # formula表关联tag表,多对多关系,中间表为 tag_formula表。 38 | # formula表中的id对应tag_formula中的formula_id 39 | # tag表中的id对应 tag_formula表中的 tag_id 40 | relate: 41 | - table: tag 42 | many_2_many: tag_formula 43 | foreign_key: id 44 | join_foreign_key: formula_id 45 | join_references: tag_id 46 | references: id 47 | type: many_many 48 | - table: club_user 49 | column: 50 | delete_time: 51 | type: gorm.DeletedAt 52 | -------------------------------------------------------------------------------- /gen_test.go: -------------------------------------------------------------------------------- 1 | package yamlgen 2 | 3 | import ( 4 | "gorm.io/driver/sqlite" 5 | "gorm.io/gen" 6 | "gorm.io/gorm" 7 | "gorm.io/gorm/logger" 8 | "gorm.io/gorm/schema" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "testing" 13 | "time" 14 | ) 15 | 16 | var db *gorm.DB 17 | 18 | func init() { 19 | newLogger := logger.New( 20 | log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer 21 | logger.Config{ 22 | SlowThreshold: time.Second, // Slow SQL threshold 23 | LogLevel: logger.Info, // Log level 24 | IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger 25 | ParameterizedQueries: true, // Don't include params in the SQL log 26 | Colorful: false, // Disable color 27 | }, 28 | ) 29 | path, _ := filepath.Abs("./test.db") 30 | db, _ = gorm.Open(sqlite.Open(path), &gorm.Config{ 31 | NamingStrategy: schema.NamingStrategy{ 32 | TablePrefix: "ims_", 33 | SingularTable: true, 34 | }, 35 | Logger: newLogger, 36 | }) 37 | } 38 | 39 | func TestParse(t *testing.T) { 40 | os.RemoveAll("./output/dao") 41 | os.RemoveAll("./output/entity") 42 | 43 | g := gen.NewGenerator(gen.Config{ 44 | OutPath: "./output/dao", 45 | Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode 46 | ModelPkgPath: "entity", 47 | }) 48 | g.UseDB(db) 49 | fieldOpts := []gen.ModelOpt{} 50 | 51 | NewYamlGenerator("./gen.yaml").UseGormGenerator(g).Generate(fieldOpts...) 52 | //g.ApplyBasic(g.GenerateAllTable(fieldOpts...)...) 53 | 54 | //userModel := g.GenerateModel("user") 55 | //g.ApplyBasic(userModel) 56 | 57 | //userProfileModel := g.GenerateModel("user_oauth") 58 | //user := g.GenerateModel("user", gen.FieldRelate(field.HasOne, "UserProfile", userProfileModel, &field.RelateConfig{ 59 | // GORMTag: field.GormTag{"foreignKey": []string{"user_id"}}, 60 | //})) 61 | //g.ApplyBasic(user, userProfileModel) 62 | 63 | g.Execute() 64 | } 65 | 66 | // 生成完成 dao & model 文件时,删掉注释测试 67 | func TestSelect(t *testing.T) { 68 | // dao.SetDefault(db) 69 | // tester := assert.New(t) 70 | // row, _ := dao.Q.Club.Preload(dao.Q.Club.User, dao.Q.Club.ClubUser).Last() 71 | // fmt.Printf("%v \n", row) 72 | // fmt.Printf("%v \n", row.User) 73 | // tester.Equal(row.Name, "测试俱乐部") 74 | // tester.Equal(row.ClubUser[0].ClubID, row.ID) 75 | // tester.Equal(row.User.ID, row.ApplicantID) 76 | // 77 | // list, _ := dao.Q.Formula.Preload(dao.Q.Formula.Tag).Find() 78 | // for _, formula := range list { 79 | // fmt.Printf("%v \n", formula) 80 | // } 81 | // tester.Equal(len(list[0].Tag), 3) 82 | // tester.Equal(len(list[1].Tag), 2) 83 | //testDeleteClubUser := &entity.ClubUser{ 84 | // ClubID: 1, 85 | // Name: "测试删除", 86 | //} 87 | //dao.ClubUser.Create(testDeleteClubUser) 88 | //dao.ClubUser.Where(dao.ClubUser.ID.Eq(testDeleteClubUser.ID)).Delete() 89 | } 90 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 4 | github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= 5 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 6 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= 7 | github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= 8 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 9 | github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= 10 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 11 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 12 | github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= 13 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= 14 | github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= 15 | github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= 16 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 17 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 18 | github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 19 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 20 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 21 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 22 | github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 23 | github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= 24 | github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 25 | github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= 26 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 27 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 28 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 29 | golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0= 30 | golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= 31 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 32 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 33 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 34 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 36 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 37 | golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= 38 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 39 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 40 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 41 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 42 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 43 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 44 | gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c h1:jWdr7cHgl8c/ua5vYbR2WhSp+NQmzhsj0xoY3foTzW8= 45 | gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c/go.mod h1:SH2K9R+2RMjuX1CkCONrPwoe9JzVv2hkQvEu4bXGojE= 46 | gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= 47 | gorm.io/driver/mysql v1.5.1-0.20230509030346-3715c134c25b h1:O7DuK4tml7U+sG1NJmGXz3LXaO6Goblps5Gx4NBuxis= 48 | gorm.io/driver/mysql v1.5.1-0.20230509030346-3715c134c25b/go.mod h1:RpAr+f2lUtJUm0e2FAbttXiUKgAqKSUtzI1ulJfz9xU= 49 | gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc= 50 | gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= 51 | gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= 52 | gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= 53 | gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= 54 | gorm.io/gen v0.3.23 h1:TL+q3bXvOzeIXBRp9vqIaD4/iaEzdU1Kgy5QSHsxDEQ= 55 | gorm.io/gen v0.3.23/go.mod h1:G9uxGfkfNFxPoOrV5P6KQxRMgZsQSCyp9vJP8xiKTGg= 56 | gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 57 | gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= 58 | gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= 59 | gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= 60 | gorm.io/gorm v1.25.1-0.20230505075827-e61b98d69677 h1:+vFpygNj+UclZztgzy/5ZGvkag6EH6moMm/MNTxkShk= 61 | gorm.io/gorm v1.25.1-0.20230505075827-e61b98d69677/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= 62 | gorm.io/hints v1.1.0 h1:Lp4z3rxREufSdxn4qmkK3TLDltrM10FLTHiuqwDPvXw= 63 | gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y= 64 | gorm.io/plugin/dbresolver v1.3.0 h1:uFDX3bIuH9Lhj5LY2oyqR/bU6pqWuDgas35NAPF4X3M= 65 | gorm.io/plugin/dbresolver v1.3.0/go.mod h1:Pr7p5+JFlgDaiM6sOrli5olekJD16YRunMyA2S7ZfKk= 66 | -------------------------------------------------------------------------------- /yaml_generator.go: -------------------------------------------------------------------------------- 1 | package yamlgen 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/we7coreteam/gorm-gen-yaml/template" 7 | "golang.org/x/tools/go/packages" 8 | "gopkg.in/yaml.v3" 9 | "gorm.io/gen" 10 | "gorm.io/gen/field" 11 | "io" 12 | "os" 13 | "path" 14 | "strings" 15 | ) 16 | 17 | type GeneratorTable struct { 18 | Flag uint 19 | ModelName string 20 | } 21 | 22 | type YamlGenerator struct { 23 | yaml *DbTable 24 | gen *gen.Generator 25 | generatedTable map[string]*GeneratorTable 26 | columnOptionSaveDir string 27 | } 28 | 29 | func NewYamlGenerator(path string) *YamlGenerator { 30 | obj := &YamlGenerator{} 31 | err := obj.loadFromFile(path) 32 | if err != nil { 33 | panic(err) 34 | return nil 35 | } 36 | obj.generatedTable = make(map[string]*GeneratorTable) 37 | return obj 38 | } 39 | 40 | type DbTable struct { 41 | Config Config `yaml:"config"` 42 | Table []Table `yaml:"relation"` 43 | TableMap map[string]*Table 44 | RelateTableMap map[string]struct{} 45 | } 46 | 47 | type Table struct { 48 | Name string `yaml:"table"` 49 | Relate []Relate `yaml:"relate"` 50 | Column map[string]Column `yaml:"column"` 51 | Props map[string]Column `yaml:"props"` 52 | } 53 | 54 | type Column struct { 55 | Type string `yaml:"type"` 56 | Serializer string `yaml:"serializer"` 57 | Tag map[string]map[string]string `yaml:"tag"` 58 | Comment string `yaml:"comment"` 59 | Rename string `yaml:"rename"` 60 | } 61 | 62 | type Relate struct { 63 | Table string `yaml:"table"` 64 | ForeignKey string `yaml:"foreign_key"` 65 | References string `yaml:"references"` 66 | JoinForeignKey string `yaml:"join_foreign_key"` 67 | JoinReferences string `yaml:"join_references"` 68 | Many2many string `yaml:"many_2_many"` 69 | Type string `yaml:"type"` 70 | } 71 | 72 | type Config struct { 73 | TagJsonCamel string `yaml:"tag_json_camel"` 74 | } 75 | 76 | func (y *YamlGenerator) UseGormGenerator(g *gen.Generator) *YamlGenerator { 77 | y.gen = g 78 | 79 | y.SetColumnOptionSaveDir(g.Config.OutPath + "/../accessor") 80 | 81 | return y 82 | } 83 | 84 | func (y *YamlGenerator) loadFromFile(path string) error { 85 | file, err := os.OpenFile(path, os.O_RDWR, os.ModePerm) 86 | if err != nil { 87 | return errors.New(fmt.Sprintf("%s file not found", path)) 88 | } 89 | content, _ := io.ReadAll(file) 90 | y.yaml = &DbTable{} 91 | err = yaml.Unmarshal(content, y.yaml) 92 | if err != nil { 93 | return err 94 | } 95 | y.yaml.TableMap = make(map[string]*Table) 96 | y.yaml.RelateTableMap = make(map[string]struct{}) 97 | 98 | for _, table := range y.yaml.Table { 99 | t := table 100 | y.yaml.TableMap[table.Name] = &t 101 | 102 | for _, relateTable := range table.Relate { 103 | y.yaml.RelateTableMap[relateTable.Table] = struct{}{} 104 | } 105 | } 106 | 107 | return nil 108 | } 109 | 110 | func (y *YamlGenerator) SetColumnOptionSaveDir(columnOptionSaveDir string) { 111 | y.columnOptionSaveDir = columnOptionSaveDir 112 | 113 | if err := os.MkdirAll(y.columnOptionSaveDir, os.ModePerm); err != nil { 114 | panic(err) 115 | } 116 | } 117 | 118 | func (y *YamlGenerator) generateColumnOption(column Column) error { 119 | var columnOptionTemplate string 120 | var exists bool 121 | 122 | if column.Serializer == "json" || column.Serializer == "gob" || column.Serializer == "unixtime" { 123 | columnOptionTemplate, exists = template.ColumnOptionTemplate["json"] 124 | } else { 125 | columnOptionTemplate, exists = template.ColumnOptionTemplate["common"] 126 | } 127 | 128 | if !exists { 129 | return errors.New("serializer type not support") 130 | } 131 | columnOptionTemplate = strings.Replace(columnOptionTemplate, "{{Package}}", strings.TrimRight(path.Base(y.columnOptionSaveDir), "/"), 1) 132 | columnOptionTemplate = strings.Replace(columnOptionTemplate, "{{OptionStructName}}", column.Type, -1) 133 | 134 | p := y.columnOptionSaveDir + "/" + CamelCaseToUnderscore(column.Type) + ".go" 135 | _, err := os.Stat(p) 136 | if os.IsNotExist(err) { 137 | return os.WriteFile(p, []byte(columnOptionTemplate), 0640) 138 | } 139 | return nil 140 | } 141 | 142 | func (y *YamlGenerator) getTableRelateOpt(table *Table) []gen.ModelOpt { 143 | opt := make([]gen.ModelOpt, len(table.Relate)) 144 | for i, table := range table.Relate { 145 | relatePointer := false 146 | var fieldType field.RelationshipType 147 | switch table.Type { 148 | case "has_one": 149 | fieldType = field.HasOne 150 | relatePointer = true 151 | case "has_many": 152 | fieldType = field.HasMany 153 | case "many_many": 154 | fieldType = field.Many2Many 155 | case "belongs_to": 156 | fieldType = field.BelongsTo 157 | relatePointer = true 158 | } 159 | relateConfig := make(field.GormTag) 160 | 161 | if table.ForeignKey != "" { 162 | relateConfig.Append("foreignKey", table.ForeignKey) 163 | } 164 | if table.JoinForeignKey != "" { 165 | relateConfig.Append("joinForeignKey", table.JoinForeignKey) 166 | } 167 | if table.References != "" { 168 | relateConfig.Append("references", table.References) 169 | } 170 | if table.JoinReferences != "" { 171 | relateConfig.Append("joinReferences", table.JoinReferences) 172 | } 173 | if table.Many2many != "" { 174 | relateConfig.Append("many2many", table.Many2many) 175 | } 176 | opt[i] = gen.FieldRelate(fieldType, y.generatedTable[table.Table].ModelName, y.gen.Data[y.generatedTable[table.Table].ModelName].QueryStructMeta, &field.RelateConfig{ 177 | GORMTag: relateConfig, 178 | RelatePointer: relatePointer, 179 | }) 180 | } 181 | 182 | return opt 183 | } 184 | 185 | func (y *YamlGenerator) getTableColumnOpt(table *Table) ([]gen.ModelOpt, bool) { 186 | opt := make([]gen.ModelOpt, 0) 187 | //找到column生成自定义column类型 188 | hasOption := false 189 | for name, column := range table.Column { 190 | if column.Type != "" { 191 | if strings.Contains(strings.ToLower(column.Type), "option") { 192 | if column.Serializer == "json" || column.Serializer == "gob" || column.Serializer == "unixtime" { 193 | if column.Tag == nil { 194 | column.Tag = map[string]map[string]string{ 195 | "gorm": { 196 | "serializer": column.Serializer, 197 | }, 198 | } 199 | } else { 200 | column.Tag["gorm"]["serializer"] = column.Serializer 201 | } 202 | // 生成对应的类型文件 203 | err := y.generateColumnOption(column) 204 | if err != nil { 205 | panic(err) 206 | } 207 | } else { 208 | // 自定义生成 Scan Value 209 | err := y.generateColumnOption(column) 210 | if err != nil { 211 | panic(err) 212 | } 213 | } 214 | hasOption = true 215 | opt = append(opt, gen.FieldType(name, "*"+strings.TrimRight(path.Base(y.columnOptionSaveDir), "/")+"."+column.Type)) 216 | } else { 217 | opt = append(opt, gen.FieldType(name, column.Type)) 218 | } 219 | } 220 | if column.Tag != nil { 221 | for tagType, tags := range column.Tag { 222 | ttags := tags 223 | if tagType == "gorm" { 224 | opt = append(opt, gen.FieldGORMTag(name, func(tag field.GormTag) field.GormTag { 225 | for tagName, val := range ttags { 226 | tag = tag.Set(tagName, val) 227 | } 228 | return tag 229 | })) 230 | } else { 231 | tTagType := tagType 232 | opt = append(opt, gen.FieldTag(name, func(tag field.Tag) field.Tag { 233 | tagStr := "" 234 | for tagName, val := range ttags { 235 | if val == "" { 236 | tagStr += tagName + ";" 237 | } else { 238 | tagStr += tagName + ":" + val + ";" 239 | } 240 | } 241 | return tag.Set(tTagType, strings.TrimRight(tagStr, ";")) 242 | })) 243 | } 244 | } 245 | } 246 | if column.Rename != "" { 247 | opt = append(opt, gen.FieldRename(name, column.Rename)) 248 | } 249 | if column.Comment != "" { 250 | opt = append(opt, gen.FieldComment(name, column.Comment)) 251 | } 252 | } 253 | 254 | for name, column := range table.Props { 255 | tag := field.Tag{} 256 | tag.Set(field.TagKeyJson, UnderscoreToCamelCase(name, y.yaml.Config.TagJsonCamel == "upper")) 257 | opt = append(opt, gen.FieldNew(UnderscoreToCamelCase(name, true), "*"+strings.TrimRight(path.Base(y.columnOptionSaveDir), "/")+"."+column.Type, tag)) 258 | // 自定义生成 Scan Value 259 | column.Serializer = "common" 260 | err := y.generateColumnOption(column) 261 | if err != nil { 262 | panic(err) 263 | } 264 | hasOption = true 265 | } 266 | return opt, hasOption 267 | } 268 | 269 | func (y *YamlGenerator) generateFromTable(table *Table, opt ...gen.ModelOpt) { 270 | if generatedTable, exists := y.generatedTable[table.Name]; exists && generatedTable.Flag == 2 { 271 | return 272 | } 273 | 274 | for _, relate := range table.Relate { 275 | _, isInRelateTable := y.yaml.RelateTableMap[relate.Table] 276 | 277 | if !isInRelateTable { 278 | y.generateFromTable(y.yaml.TableMap[relate.Table], opt...) 279 | } else { 280 | if _, exists := y.generatedTable[relate.Table]; exists { 281 | continue 282 | } 283 | flag := 1 284 | _, isInTable := y.yaml.TableMap[relate.Table] 285 | if !isInTable { 286 | flag = 2 287 | } 288 | relateMate := y.gen.GenerateModel(relate.Table, opt...) 289 | y.gen.ApplyBasic(relateMate) 290 | y.generatedTable[relate.Table] = &GeneratorTable{ 291 | Flag: uint(flag), 292 | ModelName: relateMate.ModelStructName, 293 | } 294 | } 295 | } 296 | 297 | //找到所有relate,生成模型 298 | relateOpt := y.getTableRelateOpt(table) 299 | columnOpt, hasOption := y.getTableColumnOpt(table) 300 | if opt == nil { 301 | opt = make([]gen.ModelOpt, 0) 302 | } 303 | opt = append(opt, relateOpt...) 304 | opt = append(opt, columnOpt...) 305 | relateMate := y.gen.GenerateModel(table.Name, opt...) 306 | if hasOption { 307 | pkgs, err := packages.Load(&packages.Config{ 308 | Mode: packages.NeedName, 309 | Dir: y.columnOptionSaveDir, 310 | }) 311 | if err != nil { 312 | panic(err) 313 | } 314 | relateMate.ImportPkgPaths = append(relateMate.ImportPkgPaths, "\""+pkgs[0].PkgPath+"\"") 315 | } 316 | if _, exists := y.generatedTable[table.Name]; exists { 317 | delete(y.gen.Data, y.generatedTable[table.Name].ModelName) 318 | } 319 | y.gen.ApplyBasic(relateMate) 320 | y.generatedTable[table.Name] = &GeneratorTable{ 321 | Flag: 2, 322 | ModelName: relateMate.ModelStructName, 323 | } 324 | } 325 | 326 | func (y *YamlGenerator) Generate(opt ...gen.ModelOpt) { 327 | if y.yaml.Config.TagJsonCamel != "" { 328 | y.gen.WithJSONTagNameStrategy(func(columnName string) (tagContent string) { 329 | return UnderscoreToCamelCase(columnName, y.yaml.Config.TagJsonCamel == "upper") 330 | }) 331 | } 332 | for _, table := range y.yaml.TableMap { 333 | y.generateFromTable(table, opt...) 334 | } 335 | } 336 | --------------------------------------------------------------------------------