From 33b51f5d14df513f2bfb100a2808be17fdb7bc6c Mon Sep 17 00:00:00 2001 From: lwnmengjing Date: Fri, 15 Sep 2023 17:36:02 +0800 Subject: [PATCH] :sparkles: feat: support table migrate --- virtual/model/field.go | 17 +++++++++-- virtual/model/model.go | 57 +++++++++++++++++++++++++------------ virtual/model/model_test.go | 38 ++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/virtual/model/field.go b/virtual/model/field.go index ccbca07..3e6635e 100644 --- a/virtual/model/field.go +++ b/virtual/model/field.go @@ -30,7 +30,7 @@ type Field struct { DefaultValue string `json:"defaultValue" yaml:"defaultValue"` DefaultValueFN func() string `json:"-" yaml:"-"` NotNull bool `json:"notNull" yaml:"notNull"` - Unique bool `json:"unique" yaml:"unique"` + Unique string `json:"unique" yaml:"unique"` Index string `json:"index" yaml:"index"` Comment string `json:"comment" yaml:"comment"` Size int `json:"size" yaml:"size"` @@ -71,9 +71,22 @@ func (f *Field) GetName() string { } func (f *Field) MakeField() reflect.StructField { + gormTag := fmt.Sprintf(`gorm:"column:%s`, f.Name) + if f.Size > 0 { + gormTag = fmt.Sprintf(`%s;size:%d`, gormTag, f.Size) + } + if f.Index != "" { + gormTag = fmt.Sprintf(`%s;index:%s`, gormTag, f.Index) + } + if f.NotNull { + gormTag = fmt.Sprintf(`%s;not null`, gormTag) + } + if f.Unique != "" { + gormTag = fmt.Sprintf(`%s;unique:%s`, gormTag, f.Unique) + } field := reflect.StructField{ Name: f.GetName(), - Tag: reflect.StructTag(fmt.Sprintf(`json:"%s" gorm:"column:%s"`, f.JsonTag, f.Name)), + Tag: reflect.StructTag(fmt.Sprintf(`json:"%s,omitempty" %s"`, f.JsonTag, gormTag)), } switch f.DataType { case schema.Bool: diff --git a/virtual/model/model.go b/virtual/model/model.go index 5138f8b..e7b0c1f 100644 --- a/virtual/model/model.go +++ b/virtual/model/model.go @@ -4,10 +4,10 @@ import ( "fmt" "reflect" "strings" + "time" "github.com/gin-gonic/gin" "gorm.io/gorm" - "gorm.io/gorm/schema" ) /* @@ -18,11 +18,9 @@ import ( */ type Model struct { - Table string `json:"tableName" yaml:"tableName" binding:"required"` - AutoCreateTime schema.TimeType `json:"autoCreateTime" yaml:"autoCreateTime"` - AutoUpdateTime schema.TimeType `json:"autoUpdateTime" yaml:"autoUpdateTime"` - HardDeleted bool `json:"hardDeleted" yaml:"hardDeleted"` - Fields []*Field `json:"fields" yaml:"fields" binding:"required"` + Table string `json:"tableName" yaml:"tableName" binding:"required"` + HardDeleted bool `json:"hardDeleted" yaml:"hardDeleted"` + Fields []*Field `json:"fields" yaml:"fields" binding:"required"` } // TableName get table name @@ -42,12 +40,6 @@ func (m *Model) PrimaryKeys() []string { } func (m *Model) Init() { - if m.AutoCreateTime == 0 { - m.AutoCreateTime = schema.UnixSecond - } - if m.AutoUpdateTime == 0 { - m.AutoUpdateTime = schema.UnixSecond - } for i := range m.Fields { m.Fields[i].Init() } @@ -68,20 +60,49 @@ func (m *Model) Default(data any) { // MakeModel make virtual model func (m *Model) MakeModel() any { - fieldTypes := make([]reflect.StructField, 0) - for i := range m.Fields { - fieldTypes = append(fieldTypes, m.Fields[i].MakeField()) - } - return reflect.New(reflect.StructOf(fieldTypes)).Interface() + m.Init() + s := reflect.New(reflect.StructOf(m.MakeField())) + return s.Interface() } func (m *Model) MakeList() any { m.Init() + return reflect.New(reflect.SliceOf(reflect.StructOf(m.MakeField()))).Interface() +} + +func (m *Model) Migrate(db *gorm.DB) error { + return db.Table(m.TableName()).AutoMigrate(m.MakeModel()) +} + +func (m *Model) MakeField() []reflect.StructField { fieldTypes := make([]reflect.StructField, 0) for i := range m.Fields { fieldTypes = append(fieldTypes, m.Fields[i].MakeField()) } - return reflect.New(reflect.SliceOf(reflect.StructOf(fieldTypes))).Interface() + fieldTypes = append(fieldTypes, m.MakeTimeField()...) + return fieldTypes +} + +func (m *Model) MakeTimeField() []reflect.StructField { + fieldTypes := []reflect.StructField{ + { + Name: "CreatedAt", + Type: reflect.TypeOf(time.Time{}), + Tag: "json:\"createdAt,omitempty\"", + }, { + Name: "UpdatedAt", + Type: reflect.TypeOf(time.Time{}), + Tag: "json:\"updatedAt,omitempty\"", + }, + } + if !m.HardDeleted { + fieldTypes = append(fieldTypes, reflect.StructField{ + Name: "DeletedAt", + Type: reflect.TypeOf(gorm.DeletedAt{}), + Tag: "json:\"deletedAt,omitempty\" gorm:\"index\"", + }) + } + return fieldTypes } func (m *Model) TableScope(db *gorm.DB) *gorm.DB { diff --git a/virtual/model/model_test.go b/virtual/model/model_test.go index d775df1..33424cf 100644 --- a/virtual/model/model_test.go +++ b/virtual/model/model_test.go @@ -87,6 +87,42 @@ func TestModel_TableName(t *testing.T) { } } +func TestModel_Migrate(t *testing.T) { + tests := []struct { + name string + path string + dsn string + wantError bool + }{ + { + name: "test0", + path: "../../testdata/test.yml", + dsn: "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local", + wantError: false, + }, + } + for _, tt := range tests { + rb, err := os.ReadFile(tt.path) + if err != nil { + t.Fatalf("ReadFile() error = %v", err) + } + m := &Model{} + err = yaml.Unmarshal(rb, m) + if err != nil { + t.Fatalf("Unmarshal() error = %v", err) + } + db, err := gorm.Open(mysql.New(mysql.Config{ + DSN: tt.dsn, + })) + if err != nil { + t.Fatalf("Open() error = %v", err) + } + if err = m.Migrate(db); (err != nil) != tt.wantError { + t.Errorf("Migrate() error = %v, wantError %v", err, tt.wantError) + } + } +} + func TestModel_Create(t *testing.T) { tests := []struct { name string @@ -254,7 +290,7 @@ func TestModel_Delete(t *testing.T) { req, _ := http.NewRequest(http.MethodDelete, "/test/"+id, nil) r := gin.Default() r.DELETE("/test/:id", func(ctx *gin.Context) { - if err = db.Scopes(m.URI(ctx)).Delete(nil).Error; err != nil { + if err = db.Scopes(m.URI(ctx)).Delete(m.MakeModel()).Error; err != nil { ctx.Status(http.StatusInternalServerError) t.Fatalf("Delete() error = %v", err) }