diff --git a/rest/base_model.go b/rest/base_model.go index dcf6aef..8a9370a 100644 --- a/rest/base_model.go +++ b/rest/base_model.go @@ -14,10 +14,10 @@ type IBase interface { } type Base struct { - Uuid string `gorm:"primary_key" json:"uuid"` - Sort int64 `json:"sort"` - UpdateTime time.Time `json:"updateTime"` - CreateTime time.Time `json:"createTime"` + Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"` + Sort int64 `json:"sort" gorm:"type:bigint(20) not null"` + UpdateTime time.Time `json:"updateTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"` + CreateTime time.Time `json:"createTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"` } //将 Struct 转换成map[string]interface{}类型 diff --git a/rest/dashboard_model.go b/rest/dashboard_model.go index f9dfa65..ec8b647 100644 --- a/rest/dashboard_model.go +++ b/rest/dashboard_model.go @@ -5,16 +5,16 @@ package rest */ type Dashboard struct { Base - InvokeNum int64 `json:"invokeNum"` - TotalInvokeNum int64 `json:"totalInvokeNum"` - Uv int64 `json:"uv"` - TotalUv int64 `json:"totalUv"` - MatterNum int64 `json:"matterNum"` - TotalMatterNum int64 `json:"totalMatterNum"` - FileSize int64 `json:"fileSize"` - TotalFileSize int64 `json:"totalFileSize"` - AvgCost int64 `json:"avgCost"` - Dt string `json:"dt"` + InvokeNum int64 `json:"invokeNum" gorm:"type:bigint(20) not null"` //当日访问量 + TotalInvokeNum int64 `json:"totalInvokeNum" gorm:"type:bigint(20) not null;default:0"` //截至目前总访问量 + Uv int64 `json:"uv" gorm:"type:bigint(20) not null;default:0"` //当日UV + TotalUv int64 `json:"totalUv" gorm:"type:bigint(20) not null;default:0"` //截至目前总UV + MatterNum int64 `json:"matterNum" gorm:"type:bigint(20) not null;default:0"` //文件数量 + TotalMatterNum int64 `json:"totalMatterNum" gorm:"type:bigint(20) not null;default:0"` //截至目前文件数量 + FileSize int64 `json:"fileSize" gorm:"type:bigint(20) not null;default:0"` //当日文件大小 + TotalFileSize int64 `json:"totalFileSize" gorm:"type:bigint(20) not null;default:0"` //截至目前文件总大小 + AvgCost int64 `json:"avgCost" gorm:"type:bigint(20) not null;default:0"` //请求平均耗时 ms + Dt string `json:"dt" gorm:"type:varchar(45) not null;index:idx_dt"` //日期 } // set File's table name to be `profiles` diff --git a/rest/download_token_model.go b/rest/download_token_model.go index b4678c9..694ac57 100644 --- a/rest/download_token_model.go +++ b/rest/download_token_model.go @@ -6,10 +6,10 @@ import ( type DownloadToken struct { Base - UserUuid string `json:"userUuid"` - MatterUuid string `json:"matterUuid"` - ExpireTime time.Time `json:"expireTime"` - Ip string `json:"ip"` + UserUuid string `json:"userUuid" gorm:"type:char(36) not null"` + MatterUuid string `json:"matterUuid" gorm:"type:char(36) not null;index:idx_mu"` + ExpireTime time.Time `json:"expireTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"` + Ip string `json:"ip" gorm:"type:varchar(128) not null"` } func (DownloadToken) TableName() string { diff --git a/rest/footprint_model.go b/rest/footprint_model.go index a9960d5..1751da1 100644 --- a/rest/footprint_model.go +++ b/rest/footprint_model.go @@ -5,13 +5,13 @@ package rest */ type Footprint struct { Base - UserUuid string `json:"userUuid"` - Ip string `json:"ip"` - Host string `json:"host"` - Uri string `json:"uri"` - Params string `json:"params"` - Cost int64 `json:"cost"` - Success bool `json:"success"` + UserUuid string `json:"userUuid" gorm:"type:char(36)"` + Ip string `json:"ip" gorm:"type:varchar(128) not null;index:idx_dt"` + Host string `json:"host" gorm:"type:varchar(45) not null"` + Uri string `json:"uri" gorm:"type:varchar(255) not null"` + Params string `json:"params" gorm:"type:text"` + Cost int64 `json:"cost" gorm:"type:bigint(20) not null;default:0"` + Success bool `json:"success" gorm:"type:tinyint(1) not null;default:0"` } // set File's table name to be `profiles` diff --git a/rest/image_cache_model.go b/rest/image_cache_model.go index a7adbd8..74cb1e7 100644 --- a/rest/image_cache_model.go +++ b/rest/image_cache_model.go @@ -5,13 +5,13 @@ package rest */ type ImageCache struct { Base - UserUuid string `json:"userUuid"` - MatterUuid string `json:"matterUuid"` - Mode string `json:"mode"` - Md5 string `json:"md5"` - Size int64 `json:"size"` - Path string `json:"path"` - Matter *Matter `gorm:"-" json:"matter"` + UserUuid string `json:"userUuid" gorm:"type:char(36)"` + MatterUuid string `json:"matterUuid" gorm:"type:char(36);index:idx_mu"` + Mode string `json:"mode" gorm:"type:varchar(512)"` + Md5 string `json:"md5" gorm:"type:varchar(45)"` + Size int64 `json:"size" gorm:"type:bigint(20) not null;default:0"` + Path string `json:"path" gorm:"type:varchar(512)"` + Matter *Matter `json:"matter" gorm:"-"` } // set File's table name to be `profiles` diff --git a/rest/install_controller.go b/rest/install_controller.go index 087f58a..cda6b66 100644 --- a/rest/install_controller.go +++ b/rest/install_controller.go @@ -3,10 +3,13 @@ package rest import ( "fmt" "github.com/jinzhu/gorm" + "github.com/nu7hatch/gouuid" "go/build" "io/ioutil" "net/http" + "regexp" "strconv" + "time" ) //安装程序的接口,只有安装阶段可以访问。 @@ -64,7 +67,9 @@ func (this *InstallController) RegisterRoutes() map[string]func(writer http.Resp //每个Controller需要主动注册自己的路由。 routeMap["/api/install/verify"] = this.Wrap(this.Verify, USER_ROLE_GUEST) - routeMap["/api/install/table/info/list"] = this.Wrap(this.InstallTableInfoList, USER_ROLE_GUEST) + routeMap["/api/install/table/info/list"] = this.Wrap(this.TableInfoList, USER_ROLE_GUEST) + routeMap["/api/install/create/table"] = this.Wrap(this.CreateTable, USER_ROLE_GUEST) + routeMap["/api/install/create/admin"] = this.Wrap(this.CreateAdmin, USER_ROLE_GUEST) return routeMap } @@ -143,27 +148,34 @@ func (this *InstallController) getCreateSQLFromFile(tableName string) string { } //根据表名获取建表SQL语句 -func (this *InstallController) getCreateSQLFromDb(db *gorm.DB, base IBase) (bool, string) { +func (this *InstallController) getTableMeta(gormDb *gorm.DB, entity IBase) (bool, []*gorm.StructField, []*gorm.StructField) { - var hasTable = true - var tableName = base.TableName() - hasTable = db.HasTable(base) - if !hasTable { - return false, "" + //挣扎一下,尝试获取建表语句。 + db := gormDb.Unscoped() + scope := db.NewScope(entity) + + tableName := scope.TableName() + modelStruct := scope.GetModelStruct() + allFields := modelStruct.StructFields + var missingFields = make([]*gorm.StructField, 0) + + if !scope.Dialect().HasTable(tableName) { + missingFields = append(missingFields, allFields...) + + return false, allFields, missingFields + } else { + + for _, field := range allFields { + if !scope.Dialect().HasColumn(tableName, field.DBName) { + if field.IsNormal { + missingFields = append(missingFields, field) + } + } + } + + return true, allFields, missingFields } - // Scan - type Result struct { - Table string - CreateTable string - } - - //读取建表语句。 - var result = &Result{} - db1 := db.Exec("SHOW CREATE TABLE " + tableName).Scan(result) - this.PanicError(db1.Error) - - return true, result.CreateTable } //验证数据库连接 @@ -180,7 +192,7 @@ func (this *InstallController) Verify(writer http.ResponseWriter, request *http. } //获取需要安装的数据库表 -func (this *InstallController) InstallTableInfoList(writer http.ResponseWriter, request *http.Request) *WebResult { +func (this *InstallController) TableInfoList(writer http.ResponseWriter, request *http.Request) *WebResult { var tableNames = []IBase{&Dashboard{}, &DownloadToken{}, &Footprint{}, &ImageCache{}, &Matter{}, &Preference{}, &Session{}, UploadToken{}, &User{}} var installTableInfos []*InstallTableInfo @@ -190,12 +202,12 @@ func (this *InstallController) InstallTableInfoList(writer http.ResponseWriter, for _, iBase := range tableNames { - exist, sql := this.getCreateSQLFromDb(db, iBase) + exist, allFields, missingFields := this.getTableMeta(db, iBase) installTableInfos = append(installTableInfos, &InstallTableInfo{ - Name: iBase.TableName(), - CreateSql: this.getCreateSQLFromFile(iBase.TableName()), - TableExist: exist, - ExistCreateSql: sql, + Name: iBase.TableName(), + TableExist: exist, + AllFields: allFields, + MissingFields: missingFields, }) } @@ -203,3 +215,78 @@ func (this *InstallController) InstallTableInfoList(writer http.ResponseWriter, return this.Success(installTableInfos) } + +//创建缺失数据库和表 +func (this *InstallController) CreateTable(writer http.ResponseWriter, request *http.Request) *WebResult { + + var tableNames = []IBase{&Dashboard{}, &DownloadToken{}, &Footprint{}, &ImageCache{}, &Matter{}, &Preference{}, &Session{}, UploadToken{}, &User{}} + var installTableInfos []*InstallTableInfo + + db := this.openDbConnection(writer, request) + defer this.closeDbConnection(db) + + for _, iBase := range tableNames { + + //补全缺失字段或者创建数据库表 + db1 := db.AutoMigrate(iBase) + this.PanicError(db1.Error) + + exist, allFields, missingFields := this.getTableMeta(db, iBase) + installTableInfos = append(installTableInfos, &InstallTableInfo{ + Name: iBase.TableName(), + TableExist: exist, + AllFields: allFields, + MissingFields: missingFields, + }) + + } + + return this.Success(installTableInfos) + +} + + +//创建管理员 +func (this *InstallController) CreateAdmin(writer http.ResponseWriter, request *http.Request) *WebResult { + + db := this.openDbConnection(writer, request) + defer this.closeDbConnection(db) + + adminUsername := request.FormValue("adminUsername") + adminEmail := request.FormValue("adminEmail") + adminPassword := request.FormValue("adminPassword") + + //验证超级管理员的信息 + if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, adminUsername); !m { + this.PanicBadRequest(`超级管理员用户名必填,且只能包含字母,数字和'_''`) + } + + if len(adminPassword) < 6 { + this.PanicBadRequest(`超级管理员密码长度至少为6位`) + } + + if adminEmail == "" { + this.PanicBadRequest(`超级管理员邮箱必填`) + } + + user := &User{} + timeUUID, _ := uuid.NewV4() + user.Uuid = string(timeUUID.String()) + user.CreateTime = time.Now() + user.UpdateTime = time.Now() + user.LastTime = time.Now() + user.Sort = time.Now().UnixNano() / 1e6 + user.Role = USER_ROLE_ADMINISTRATOR + user.Username = adminUsername + user.Password = GetBcrypt(adminPassword) + user.Email = adminEmail + user.Phone = "" + user.Gender = USER_GENDER_UNKNOWN + user.SizeLimit = -1 + user.Status = USER_STATUS_OK + + db.Create(user) + + return this.Success("OK") + +} diff --git a/rest/install_model.go b/rest/install_model.go index 0aa7f82..44f4a06 100644 --- a/rest/install_model.go +++ b/rest/install_model.go @@ -1,11 +1,13 @@ package rest +import "github.com/jinzhu/gorm" + /** * 表名对应的表结构 */ type InstallTableInfo struct { - Name string `json:"name"` - CreateSql string `json:"createSql"` - TableExist bool `json:"tableExist"` - ExistCreateSql string `json:"existCreateSql"` + Name string `json:"name"` + TableExist bool `json:"tableExist"` + AllFields []*gorm.StructField `json:"allFields"` + MissingFields []*gorm.StructField `json:"missingFields"` } diff --git a/rest/matter_model.go b/rest/matter_model.go index bcb2edb..40738b0 100644 --- a/rest/matter_model.go +++ b/rest/matter_model.go @@ -5,17 +5,17 @@ package rest */ type Matter struct { Base - Puuid string `json:"puuid"` - UserUuid string `json:"userUuid"` - Dir bool `json:"dir"` - Alien bool `json:"alien"` - Name string `json:"name"` - Md5 string `json:"md5"` - Size int64 `json:"size"` - Privacy bool `json:"privacy"` - Path string `json:"path"` - Times int64 `json:"times"` - Parent *Matter `gorm:"-" json:"parent"` + Puuid string `json:"puuid" gorm:"type:char(36);index:idx_puuid"` + UserUuid string `json:"userUuid" gorm:"type:char(36);index:idx_uu"` + Dir bool `json:"dir" gorm:"type:tinyint(1) not null;default:0"` + Alien bool `json:"alien" gorm:"type:tinyint(1) not null;default:0"` + Name string `json:"name" gorm:"type:varchar(255) not null"` + Md5 string `json:"md5" gorm:"type:varchar(45)"` + Size int64 `json:"size" gorm:"type:bigint(20) not null;default:0"` + Privacy bool `json:"privacy" gorm:"type:tinyint(1) not null;default:0"` + Path string `json:"path" gorm:"type:varchar(512)"` + Times int64 `json:"times" gorm:"type:bigint(20) not null;default:0"` + Parent *Matter `json:"parent" gorm:"-"` } // set File's table name to be `profiles` diff --git a/rest/preference_model.go b/rest/preference_model.go index 3a1a027..140aa1d 100644 --- a/rest/preference_model.go +++ b/rest/preference_model.go @@ -2,12 +2,12 @@ package rest type Preference struct { Base - Name string `json:"name"` - LogoUrl string `json:"logoUrl"` - FaviconUrl string `json:"faviconUrl"` - FooterLine1 string `json:"footerLine1"` - FooterLine2 string `json:"footerLine2"` - Version string `json:"version"` + Name string `json:"name" gorm:"type:varchar(45)"` + LogoUrl string `json:"logoUrl" gorm:"type:varchar(255)"` + FaviconUrl string `json:"faviconUrl" gorm:"type:varchar(255)"` + FooterLine1 string `json:"footerLine1" gorm:"type:varchar(1024)"` + FooterLine2 string `json:"footerLine2" gorm:"type:varchar(1024)"` + Version string `json:"version" gorm:"type:varchar(45)"` } // set File's table name to be `profiles` diff --git a/rest/session_model.go b/rest/session_model.go index 8d9ba1c..7eb71c2 100644 --- a/rest/session_model.go +++ b/rest/session_model.go @@ -6,9 +6,9 @@ import ( type Session struct { Base - UserUuid string `json:"userUuid"` - Ip string `json:"ip"` - ExpireTime time.Time `json:"expireTime"` + UserUuid string `json:"userUuid" gorm:"type:char(36)"` + Ip string `json:"ip" gorm:"type:varchar(128) not null"` + ExpireTime time.Time `json:"expireTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"` } // set User's table name to be `profiles` diff --git a/rest/user_model.go b/rest/user_model.go index 72d3b36..051ea3e 100644 --- a/rest/user_model.go +++ b/rest/user_model.go @@ -28,18 +28,18 @@ const ( type User struct { Base - Role string `json:"role"` - Username string `json:"username"` - Password string `json:"-"` - Email string `json:"email"` - Phone string `json:"phone"` - Gender string `json:"gender"` - City string `json:"city"` - AvatarUrl string `json:"avatarUrl"` - LastIp string `json:"lastIp"` - LastTime time.Time `json:"lastTime"` - SizeLimit int64 `json:"sizeLimit"` - Status string `json:"status"` + Role string `json:"role" gorm:"type:varchar(45)"` + Username string `json:"username" gorm:"type:varchar(45) not null;unique"` + Password string `json:"-" gorm:"type:varchar(255)"` + Email string `json:"email" gorm:"type:varchar(45) not null;unique"` + Phone string `json:"phone" gorm:"type:varchar(45)"` + Gender string `json:"gender" gorm:"type:varchar(45)"` + City string `json:"city" gorm:"type:varchar(45)"` + AvatarUrl string `json:"avatarUrl" gorm:"type:varchar(255)"` + LastIp string `json:"lastIp" gorm:"type:varchar(128)"` + LastTime time.Time `json:"lastTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"` + SizeLimit int64 `json:"sizeLimit" gorm:"type:bigint(20) not null;default:-1"` + Status string `json:"status" gorm:"type:varchar(45)"` } // set User's table name to be `profiles`