Finish the second step of work.

This commit is contained in:
zicla
2018-12-05 21:22:31 +08:00
parent ea1bd27aa8
commit b7e8a3c1a1
11 changed files with 182 additions and 93 deletions

View File

@ -14,10 +14,10 @@ type IBase interface {
} }
type Base struct { type Base struct {
Uuid string `gorm:"primary_key" json:"uuid"` Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"`
Sort int64 `json:"sort"` Sort int64 `json:"sort" gorm:"type:bigint(20) not null"`
UpdateTime time.Time `json:"updateTime"` UpdateTime time.Time `json:"updateTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"`
CreateTime time.Time `json:"createTime"` CreateTime time.Time `json:"createTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
} }
//将 Struct 转换成map[string]interface{}类型 //将 Struct 转换成map[string]interface{}类型

View File

@ -5,16 +5,16 @@ package rest
*/ */
type Dashboard struct { type Dashboard struct {
Base Base
InvokeNum int64 `json:"invokeNum"` InvokeNum int64 `json:"invokeNum" gorm:"type:bigint(20) not null"` //当日访问量
TotalInvokeNum int64 `json:"totalInvokeNum"` TotalInvokeNum int64 `json:"totalInvokeNum" gorm:"type:bigint(20) not null;default:0"` //截至目前总访问量
Uv int64 `json:"uv"` Uv int64 `json:"uv" gorm:"type:bigint(20) not null;default:0"` //当日UV
TotalUv int64 `json:"totalUv"` TotalUv int64 `json:"totalUv" gorm:"type:bigint(20) not null;default:0"` //截至目前总UV
MatterNum int64 `json:"matterNum"` MatterNum int64 `json:"matterNum" gorm:"type:bigint(20) not null;default:0"` //文件数量
TotalMatterNum int64 `json:"totalMatterNum"` TotalMatterNum int64 `json:"totalMatterNum" gorm:"type:bigint(20) not null;default:0"` //截至目前文件数量
FileSize int64 `json:"fileSize"` FileSize int64 `json:"fileSize" gorm:"type:bigint(20) not null;default:0"` //当日文件大小
TotalFileSize int64 `json:"totalFileSize"` TotalFileSize int64 `json:"totalFileSize" gorm:"type:bigint(20) not null;default:0"` //截至目前文件总大小
AvgCost int64 `json:"avgCost"` AvgCost int64 `json:"avgCost" gorm:"type:bigint(20) not null;default:0"` //请求平均耗时 ms
Dt string `json:"dt"` Dt string `json:"dt" gorm:"type:varchar(45) not null;index:idx_dt"` //日期
} }
// set File's table name to be `profiles` // set File's table name to be `profiles`

View File

@ -6,10 +6,10 @@ import (
type DownloadToken struct { type DownloadToken struct {
Base Base
UserUuid string `json:"userUuid"` UserUuid string `json:"userUuid" gorm:"type:char(36) not null"`
MatterUuid string `json:"matterUuid"` MatterUuid string `json:"matterUuid" gorm:"type:char(36) not null;index:idx_mu"`
ExpireTime time.Time `json:"expireTime"` ExpireTime time.Time `json:"expireTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
Ip string `json:"ip"` Ip string `json:"ip" gorm:"type:varchar(128) not null"`
} }
func (DownloadToken) TableName() string { func (DownloadToken) TableName() string {

View File

@ -5,13 +5,13 @@ package rest
*/ */
type Footprint struct { type Footprint struct {
Base Base
UserUuid string `json:"userUuid"` UserUuid string `json:"userUuid" gorm:"type:char(36)"`
Ip string `json:"ip"` Ip string `json:"ip" gorm:"type:varchar(128) not null;index:idx_dt"`
Host string `json:"host"` Host string `json:"host" gorm:"type:varchar(45) not null"`
Uri string `json:"uri"` Uri string `json:"uri" gorm:"type:varchar(255) not null"`
Params string `json:"params"` Params string `json:"params" gorm:"type:text"`
Cost int64 `json:"cost"` Cost int64 `json:"cost" gorm:"type:bigint(20) not null;default:0"`
Success bool `json:"success"` Success bool `json:"success" gorm:"type:tinyint(1) not null;default:0"`
} }
// set File's table name to be `profiles` // set File's table name to be `profiles`

View File

@ -5,13 +5,13 @@ package rest
*/ */
type ImageCache struct { type ImageCache struct {
Base Base
UserUuid string `json:"userUuid"` UserUuid string `json:"userUuid" gorm:"type:char(36)"`
MatterUuid string `json:"matterUuid"` MatterUuid string `json:"matterUuid" gorm:"type:char(36);index:idx_mu"`
Mode string `json:"mode"` Mode string `json:"mode" gorm:"type:varchar(512)"`
Md5 string `json:"md5"` Md5 string `json:"md5" gorm:"type:varchar(45)"`
Size int64 `json:"size"` Size int64 `json:"size" gorm:"type:bigint(20) not null;default:0"`
Path string `json:"path"` Path string `json:"path" gorm:"type:varchar(512)"`
Matter *Matter `gorm:"-" json:"matter"` Matter *Matter `json:"matter" gorm:"-"`
} }
// set File's table name to be `profiles` // set File's table name to be `profiles`

View File

@ -3,10 +3,13 @@ package rest
import ( import (
"fmt" "fmt"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/nu7hatch/gouuid"
"go/build" "go/build"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"regexp"
"strconv" "strconv"
"time"
) )
//安装程序的接口,只有安装阶段可以访问。 //安装程序的接口,只有安装阶段可以访问。
@ -64,7 +67,9 @@ func (this *InstallController) RegisterRoutes() map[string]func(writer http.Resp
//每个Controller需要主动注册自己的路由。 //每个Controller需要主动注册自己的路由。
routeMap["/api/install/verify"] = this.Wrap(this.Verify, USER_ROLE_GUEST) 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 return routeMap
} }
@ -143,27 +148,34 @@ func (this *InstallController) getCreateSQLFromFile(tableName string) string {
} }
//根据表名获取建表SQL语句 //根据表名获取建表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() db := gormDb.Unscoped()
hasTable = db.HasTable(base) scope := db.NewScope(entity)
if !hasTable {
return false, "" 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 tableNames = []IBase{&Dashboard{}, &DownloadToken{}, &Footprint{}, &ImageCache{}, &Matter{}, &Preference{}, &Session{}, UploadToken{}, &User{}}
var installTableInfos []*InstallTableInfo var installTableInfos []*InstallTableInfo
@ -190,12 +202,12 @@ func (this *InstallController) InstallTableInfoList(writer http.ResponseWriter,
for _, iBase := range tableNames { for _, iBase := range tableNames {
exist, sql := this.getCreateSQLFromDb(db, iBase) exist, allFields, missingFields := this.getTableMeta(db, iBase)
installTableInfos = append(installTableInfos, &InstallTableInfo{ installTableInfos = append(installTableInfos, &InstallTableInfo{
Name: iBase.TableName(), Name: iBase.TableName(),
CreateSql: this.getCreateSQLFromFile(iBase.TableName()), TableExist: exist,
TableExist: exist, AllFields: allFields,
ExistCreateSql: sql, MissingFields: missingFields,
}) })
} }
@ -203,3 +215,78 @@ func (this *InstallController) InstallTableInfoList(writer http.ResponseWriter,
return this.Success(installTableInfos) 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")
}

View File

@ -1,11 +1,13 @@
package rest package rest
import "github.com/jinzhu/gorm"
/** /**
* 表名对应的表结构 * 表名对应的表结构
*/ */
type InstallTableInfo struct { type InstallTableInfo struct {
Name string `json:"name"` Name string `json:"name"`
CreateSql string `json:"createSql"` TableExist bool `json:"tableExist"`
TableExist bool `json:"tableExist"` AllFields []*gorm.StructField `json:"allFields"`
ExistCreateSql string `json:"existCreateSql"` MissingFields []*gorm.StructField `json:"missingFields"`
} }

View File

@ -5,17 +5,17 @@ package rest
*/ */
type Matter struct { type Matter struct {
Base Base
Puuid string `json:"puuid"` Puuid string `json:"puuid" gorm:"type:char(36);index:idx_puuid"`
UserUuid string `json:"userUuid"` UserUuid string `json:"userUuid" gorm:"type:char(36);index:idx_uu"`
Dir bool `json:"dir"` Dir bool `json:"dir" gorm:"type:tinyint(1) not null;default:0"`
Alien bool `json:"alien"` Alien bool `json:"alien" gorm:"type:tinyint(1) not null;default:0"`
Name string `json:"name"` Name string `json:"name" gorm:"type:varchar(255) not null"`
Md5 string `json:"md5"` Md5 string `json:"md5" gorm:"type:varchar(45)"`
Size int64 `json:"size"` Size int64 `json:"size" gorm:"type:bigint(20) not null;default:0"`
Privacy bool `json:"privacy"` Privacy bool `json:"privacy" gorm:"type:tinyint(1) not null;default:0"`
Path string `json:"path"` Path string `json:"path" gorm:"type:varchar(512)"`
Times int64 `json:"times"` Times int64 `json:"times" gorm:"type:bigint(20) not null;default:0"`
Parent *Matter `gorm:"-" json:"parent"` Parent *Matter `json:"parent" gorm:"-"`
} }
// set File's table name to be `profiles` // set File's table name to be `profiles`

View File

@ -2,12 +2,12 @@ package rest
type Preference struct { type Preference struct {
Base Base
Name string `json:"name"` Name string `json:"name" gorm:"type:varchar(45)"`
LogoUrl string `json:"logoUrl"` LogoUrl string `json:"logoUrl" gorm:"type:varchar(255)"`
FaviconUrl string `json:"faviconUrl"` FaviconUrl string `json:"faviconUrl" gorm:"type:varchar(255)"`
FooterLine1 string `json:"footerLine1"` FooterLine1 string `json:"footerLine1" gorm:"type:varchar(1024)"`
FooterLine2 string `json:"footerLine2"` FooterLine2 string `json:"footerLine2" gorm:"type:varchar(1024)"`
Version string `json:"version"` Version string `json:"version" gorm:"type:varchar(45)"`
} }
// set File's table name to be `profiles` // set File's table name to be `profiles`

View File

@ -6,9 +6,9 @@ import (
type Session struct { type Session struct {
Base Base
UserUuid string `json:"userUuid"` UserUuid string `json:"userUuid" gorm:"type:char(36)"`
Ip string `json:"ip"` Ip string `json:"ip" gorm:"type:varchar(128) not null"`
ExpireTime time.Time `json:"expireTime"` ExpireTime time.Time `json:"expireTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
} }
// set User's table name to be `profiles` // set User's table name to be `profiles`

View File

@ -28,18 +28,18 @@ const (
type User struct { type User struct {
Base Base
Role string `json:"role"` Role string `json:"role" gorm:"type:varchar(45)"`
Username string `json:"username"` Username string `json:"username" gorm:"type:varchar(45) not null;unique"`
Password string `json:"-"` Password string `json:"-" gorm:"type:varchar(255)"`
Email string `json:"email"` Email string `json:"email" gorm:"type:varchar(45) not null;unique"`
Phone string `json:"phone"` Phone string `json:"phone" gorm:"type:varchar(45)"`
Gender string `json:"gender"` Gender string `json:"gender" gorm:"type:varchar(45)"`
City string `json:"city"` City string `json:"city" gorm:"type:varchar(45)"`
AvatarUrl string `json:"avatarUrl"` AvatarUrl string `json:"avatarUrl" gorm:"type:varchar(255)"`
LastIp string `json:"lastIp"` LastIp string `json:"lastIp" gorm:"type:varchar(128)"`
LastTime time.Time `json:"lastTime"` LastTime time.Time `json:"lastTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
SizeLimit int64 `json:"sizeLimit"` SizeLimit int64 `json:"sizeLimit" gorm:"type:bigint(20) not null;default:-1"`
Status string `json:"status"` Status string `json:"status" gorm:"type:varchar(45)"`
} }
// set User's table name to be `profiles` // set User's table name to be `profiles`