├── .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 |
--------------------------------------------------------------------------------