Finish half translation work.

This commit is contained in:
zicla 2019-05-04 23:36:05 +08:00
parent 900924d196
commit 5625149766
52 changed files with 613 additions and 950 deletions

View File

@ -1,9 +1,6 @@
package core package core
/**
* 从命令行输入的相关信息
*/
type Application interface { type Application interface {
//启动整个应用 //start the application
Start() Start()
} }

View File

@ -1,15 +1,15 @@
package core package core
/** /**
* 系统中的Bean接口即系统中单例模式 * bean interface means singleton in application
*/ */
type Bean interface { type Bean interface {
//初始化方法 //init the bean when constructing
Init() Init()
//系统清理方法 //cleanup the bean when system's cleanup
Cleanup() Cleanup()
//所有配置都加载完成后调用的方法,包括数据库加载完毕 //when everything(including db's connection) loaded, this method will be invoked.
Bootstrap() Bootstrap()
//快速的Panic方法 //shortcut for panic check.
PanicError(err error) PanicError(err error)
} }

View File

@ -1,33 +1,27 @@
package core package core
const ( const (
//用户身份的cookie字段名 //authentication key of cookie
COOKIE_AUTH_KEY = "_ak" COOKIE_AUTH_KEY = "_ak"
//使用用户名密码给接口授权key
USERNAME_KEY = "_username" USERNAME_KEY = "_username"
PASSWORD_KEY = "_password" PASSWORD_KEY = "_password"
//默认端口号
DEFAULT_SERVER_PORT = 6010 DEFAULT_SERVER_PORT = 6010
//数据库表前缀 tank30_表示当前应用版本是tank:3.0.x版数据库结构发生变化必然是中型升级 //db table's prefix. tank30_ means current version is tank:3.0.x
TABLE_PREFIX = "tank30_" TABLE_PREFIX = "tank30_"
//当前版本
VERSION = "3.0.0.beta1" VERSION = "3.0.0.beta1"
) )
type Config interface { type Config interface {
//是否已经安装
Installed() bool Installed() bool
//启动端口
ServerPort() int ServerPort() int
//获取mysql链接 //get the mysql url. eg. tank:tank123@tcp(127.0.0.1:3306)/tank?charset=utf8&parseTime=True&loc=Local
MysqlUrl() string MysqlUrl() string
//files storage location.
//文件存放路径
MatterPath() string MatterPath() string
//完成安装过程,主要是要将配置写入到文件中 //when installed by user. Write configs to tank.json
FinishInstall(mysqlPort int, mysqlHost string, mysqlSchema string, mysqlUsername string, mysqlPassword string) FinishInstall(mysqlPort int, mysqlHost string, mysqlSchema string, mysqlUsername string, mysqlPassword string)
} }

View File

@ -7,24 +7,21 @@ import (
) )
type Context interface { type Context interface {
//具备响应http请求的能力
http.Handler http.Handler
//获取数据库链接 //get the gorm.DB. all the db connection will use this
GetDB() *gorm.DB GetDB() *gorm.DB
//获取一个Bean
GetBean(bean Bean) Bean GetBean(bean Bean) Bean
//获取全局的Session缓存 //get the global session cache
GetSessionCache() *cache.Table GetSessionCache() *cache.Table
//获取全局的ControllerMap
GetControllerMap() map[string]Controller GetControllerMap() map[string]Controller
//系统安装成功 //when application installed. this method will invoke every bean's Bootstrap method
InstallOk() InstallOk()
//清空系统 //this method will invoke every bean's Cleanup method
Cleanup() Cleanup()
} }

View File

@ -4,8 +4,8 @@ import "net/http"
type Controller interface { type Controller interface {
Bean Bean
//注册自己固定的路由。 //register self's fixed routes
RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request)
//处理一些特殊的路由。 //handle some special routes, eg. params in the url.
HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool)
} }

View File

@ -1,16 +1,15 @@
package core package core
//该文件中记录的是应用系统中全局变量。主要有日志LOGGER和上下文CONTEXT //the global variables in the application.
//命令行输入等相关信息 //application
var APPLICATION Application var APPLICATION Application
//日志系统必须高保 //logger
//全局唯一的日志对象(在main函数中初始化)
var LOGGER Logger var LOGGER Logger
//全局唯一配置 //config
var CONFIG Config var CONFIG Config
//全局唯一的上下文(在main函数中初始化) //context
var CONTEXT Context var CONTEXT Context

View File

@ -1,18 +1,18 @@
package core package core
//带有panic恢复的方法 //run a method with panic recovery.
func RunWithRecovery(f func()) { func RunWithRecovery(f func()) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
LOGGER.Error("异步任务错误: %v", err) LOGGER.Error("error in async method: %v", err)
} }
}() }()
//执行函数 //execute the method
f() f()
} }
//处理错误的统一方法 可以省去if err!=nil 这段代码 //shortcut for panic check
func PanicError(err error) { func PanicError(err error) {
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -2,10 +2,10 @@ package core
type Logger interface { type Logger interface {
//处理日志的统一方法。 //basic log method
Log(prefix string, format string, v ...interface{}) Log(prefix string, format string, v ...interface{})
//不同级别的日志处理 //log with different level.
Debug(format string, v ...interface{}) Debug(format string, v ...interface{})
Info(format string, v ...interface{}) Info(format string, v ...interface{})
Warn(format string, v ...interface{}) Warn(format string, v ...interface{})

View File

@ -1,7 +1,6 @@
package rest package rest
import ( import (
"fmt"
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/i18n" "github.com/eyebluecn/tank/code/tool/i18n"
"github.com/eyebluecn/tank/code/tool/result" "github.com/eyebluecn/tank/code/tool/result"
@ -24,11 +23,9 @@ type AlienController struct {
shareService *ShareService shareService *ShareService
} }
//初始化方法
func (this *AlienController) Init() { func (this *AlienController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean.
b := core.CONTEXT.GetBean(this.uploadTokenDao) b := core.CONTEXT.GetBean(this.uploadTokenDao)
if c, ok := b.(*UploadTokenDao); ok { if c, ok := b.(*UploadTokenDao); ok {
this.uploadTokenDao = c this.uploadTokenDao = c
@ -70,28 +67,26 @@ func (this *AlienController) Init() {
} }
} }
//注册自己的路由。
func (this *AlienController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *AlienController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
//每个Controller需要主动注册自己的路由。 routeMap["/api/alien/fetch/upload/token"] = this.Wrap(this.FetchUploadToken, USER_ROLE_USER)
routeMap["/api/alien/fetch/upload/token"] = this.Wrap(this.FetchUploadToken, USER_ROLE_GUEST) routeMap["/api/alien/fetch/download/token"] = this.Wrap(this.FetchDownloadToken, USER_ROLE_USER)
routeMap["/api/alien/fetch/download/token"] = this.Wrap(this.FetchDownloadToken, USER_ROLE_GUEST) routeMap["/api/alien/confirm"] = this.Wrap(this.Confirm, USER_ROLE_USER)
routeMap["/api/alien/confirm"] = this.Wrap(this.Confirm, USER_ROLE_GUEST)
routeMap["/api/alien/upload"] = this.Wrap(this.Upload, USER_ROLE_GUEST) routeMap["/api/alien/upload"] = this.Wrap(this.Upload, USER_ROLE_GUEST)
routeMap["/api/alien/crawl/token"] = this.Wrap(this.CrawlToken, USER_ROLE_GUEST) routeMap["/api/alien/crawl/token"] = this.Wrap(this.CrawlToken, USER_ROLE_GUEST)
routeMap["/api/alien/crawl/direct"] = this.Wrap(this.CrawlDirect, USER_ROLE_GUEST) routeMap["/api/alien/crawl/direct"] = this.Wrap(this.CrawlDirect, USER_ROLE_USER)
return routeMap return routeMap
} }
//处理一些特殊的接口,比如参数包含在路径中,一般情况下controller不将参数放在url路径中 //handle some special routes, eg. params in the url.
func (this *AlienController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) { func (this *AlienController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
path := request.URL.Path path := request.URL.Path
//匹配 /api/alien/preview/{uuid}/{filename} (响应头不包含 content-disposition) //match /api/alien/preview/{uuid}/{filename} (response header not contain content-disposition)
reg := regexp.MustCompile(`^/api/alien/preview/([^/]+)/([^/]+)$`) reg := regexp.MustCompile(`^/api/alien/preview/([^/]+)/([^/]+)$`)
strs := reg.FindStringSubmatch(path) strs := reg.FindStringSubmatch(path)
if len(strs) == 3 { if len(strs) == 3 {
@ -101,7 +96,7 @@ func (this *AlienController) HandleRoutes(writer http.ResponseWriter, request *h
return f, true return f, true
} }
//匹配 /api/alien/download/{uuid}/{filename} (响应头包含 content-disposition) //match /api/alien/download/{uuid}/{filename} (response header contain content-disposition)
reg = regexp.MustCompile(`^/api/alien/download/([^/]+)/([^/]+)$`) reg = regexp.MustCompile(`^/api/alien/download/([^/]+)/([^/]+)$`)
strs = reg.FindStringSubmatch(path) strs = reg.FindStringSubmatch(path)
if len(strs) == 3 { if len(strs) == 3 {
@ -114,76 +109,60 @@ func (this *AlienController) HandleRoutes(writer http.ResponseWriter, request *h
return nil, false return nil, false
} }
//系统中的用户x要获取一个UploadToken用于提供给x信任的用户上传文件。 //fetch a upload token for guest. Guest can upload file with this token.
func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
//文件名。
filename := request.FormValue("filename") filename := request.FormValue("filename")
expireTimeStr := request.FormValue("expireTime")
privacyStr := request.FormValue("privacy")
sizeStr := request.FormValue("size")
//store dir path
dirPath := request.FormValue("dirPath")
if filename == "" { if filename == "" {
panic("文件名必填") panic(result.BadRequest("filename cannot be null"))
} else if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, filename); m { } else if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, filename); m {
panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars)) panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars))
} }
//什么时间后过期默认24h var expireTime time.Time
expireStr := request.FormValue("expire") if expireTimeStr == "" {
expire := 24 * 60 * 60 panic(result.BadRequest("time format error"))
if expireStr != "" {
var err error
expire, err = strconv.Atoi(expireStr)
if err != nil {
panic(`过期时间不符合规范`)
}
if expire < 1 {
panic(`过期时间不符合规范`)
}
}
//文件公有或私有
privacyStr := request.FormValue("privacy")
var privacy bool
if privacyStr == "" {
panic(`文件公有性必填`)
} else { } else {
expireTime = util.ConvertDateTimeStringToTime(expireTimeStr)
}
if expireTime.Before(time.Now()) {
panic(result.BadRequest("expire time cannot before now"))
}
var privacy = false
if privacyStr == TRUE { if privacyStr == TRUE {
privacy = true privacy = true
} else if privacyStr == "false" {
privacy = false
} else {
panic(`文件公有性不符合规范`)
}
} }
//文件大小
sizeStr := request.FormValue("size")
var size int64 var size int64
if sizeStr == "" { if sizeStr == "" {
panic(`文件大小必填`) panic(result.BadRequest("file size cannot be null"))
} else { } else {
var err error var err error
size, err = strconv.ParseInt(sizeStr, 10, 64) size, err = strconv.ParseInt(sizeStr, 10, 64)
if err != nil { if err != nil {
panic(`文件大小不符合规范`) panic(result.BadRequest("file size error"))
} }
if size < 1 { if size < 1 {
panic(`文件大小不符合规范`) panic(result.BadRequest("file size error"))
} }
} }
//文件夹路径,以 / 开头。
dir := request.FormValue("dir")
user := this.checkUser(request) user := this.checkUser(request)
dirMatter := this.matterService.CreateDirectories(request, user, dir) dirMatter := this.matterService.CreateDirectories(request, user, dirPath)
mm, _ := time.ParseDuration(fmt.Sprintf("%ds", expire))
uploadToken := &UploadToken{ uploadToken := &UploadToken{
UserUuid: user.Uuid, UserUuid: user.Uuid,
FolderUuid: dirMatter.Uuid, FolderUuid: dirMatter.Uuid,
MatterUuid: "", MatterUuid: "",
ExpireTime: time.Now().Add(mm), ExpireTime: expireTime,
Filename: filename, Filename: filename,
Privacy: privacy, Privacy: privacy,
Size: size, Size: size,
@ -196,51 +175,34 @@ func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, reques
} }
//系统中的用户x 拿着某个文件的uuid来确认是否其信任的用户已经上传好了。 //user confirm a file whether uploaded successfully.
func (this *AlienController) Confirm(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *AlienController) Confirm(writer http.ResponseWriter, request *http.Request) *result.WebResult {
matterUuid := request.FormValue("matterUuid") matterUuid := request.FormValue("matterUuid")
if matterUuid == "" { if matterUuid == "" {
panic("matterUuid必填") panic(result.BadRequest("matterUuid cannot be null"))
} }
user := this.checkUser(request) user := this.checkUser(request)
matter := this.matterDao.CheckByUuid(matterUuid) matter := this.matterDao.CheckByUuid(matterUuid)
if matter.UserUuid != user.Uuid { if matter.UserUuid != user.Uuid {
panic("文件不属于你") panic(result.BadRequest("matter not belong to you"))
} }
return this.Success(matter) return this.Success(matter)
} }
//系统中的用户x 信任的用户上传文件。这个接口需要支持跨域。 //a guest upload a file with a upload token.
func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Request) *result.WebResult {
//允许跨域请求。 //allow cors.
this.allowCORS(writer) this.allowCORS(writer)
if request.Method == "OPTIONS" { if request.Method == "OPTIONS" {
return this.Success("OK") //nil means empty response body.
return nil
} }
uploadTokenUuid := request.FormValue("uploadTokenUuid") uploadTokenUuid := request.FormValue("uploadTokenUuid")
if uploadTokenUuid == "" {
panic("uploadTokenUuid必填")
}
uploadToken := this.uploadTokenDao.FindByUuid(uploadTokenUuid)
if uploadToken == nil {
panic("uploadTokenUuid无效")
}
if uploadToken.ExpireTime.Before(time.Now()) {
panic("uploadToken已失效")
}
user := this.userDao.CheckByUuid(uploadToken.UserUuid)
err := request.ParseMultipartForm(32 << 20)
this.PanicError(err)
file, handler, err := request.FormFile("file") file, handler, err := request.FormFile("file")
this.PanicError(err) this.PanicError(err)
defer func() { defer func() {
@ -248,47 +210,61 @@ func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Re
this.PanicError(e) this.PanicError(e)
}() }()
if uploadTokenUuid == "" {
panic(result.BadRequest("uploadTokenUuid cannot be null"))
}
uploadToken := this.uploadTokenDao.CheckByUuid(uploadTokenUuid)
if uploadToken.ExpireTime.Before(time.Now()) {
panic(result.BadRequest("uploadToken has expired"))
}
user := this.userDao.CheckByUuid(uploadToken.UserUuid)
err = request.ParseMultipartForm(32 << 20)
this.PanicError(err)
if handler.Filename != uploadToken.Filename { if handler.Filename != uploadToken.Filename {
panic("文件名称不正确") panic(result.BadRequest("filename doesn't the one in uploadToken"))
} }
if handler.Size != uploadToken.Size { if handler.Size != uploadToken.Size {
panic("文件大小不正确") panic(result.BadRequest("file size doesn't the one in uploadToken"))
} }
dirMatter := this.matterDao.CheckWithRootByUuid(uploadToken.FolderUuid, user) dirMatter := this.matterDao.CheckWithRootByUuid(uploadToken.FolderUuid, user)
matter := this.matterService.AtomicUpload(request, file, user, dirMatter, uploadToken.Filename, uploadToken.Privacy) matter := this.matterService.Upload(request, file, user, dirMatter, uploadToken.Filename, uploadToken.Privacy)
//更新这个uploadToken的信息. //expire the upload token.
uploadToken.ExpireTime = time.Now() uploadToken.ExpireTime = time.Now()
this.uploadTokenDao.Save(uploadToken) this.uploadTokenDao.Save(uploadToken)
return this.Success(matter) return this.Success(matter)
} }
//给一个指定的url从该url中去拉取文件回来。此处采用uploadToken的模式。 //crawl a url with uploadToken. guest can visit this method.
func (this *AlienController) CrawlToken(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *AlienController) CrawlToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
//允许跨域请求。
//allow cors.
this.allowCORS(writer) this.allowCORS(writer)
if request.Method == "OPTIONS" { if request.Method == "OPTIONS" {
return this.Success("OK") //nil means empty response body.
return nil
} }
uploadTokenUuid := request.FormValue("uploadTokenUuid") uploadTokenUuid := request.FormValue("uploadTokenUuid")
url := request.FormValue("url") url := request.FormValue("url")
if uploadTokenUuid == "" { if uploadTokenUuid == "" {
panic("uploadTokenUuid必填") panic(result.BadRequest("uploadTokenUuid cannot be null"))
} }
uploadToken := this.uploadTokenDao.FindByUuid(uploadTokenUuid) uploadToken := this.uploadTokenDao.CheckByUuid(uploadTokenUuid)
if uploadToken == nil {
panic("uploadTokenUuid无效")
}
if uploadToken.ExpireTime.Before(time.Now()) { if uploadToken.ExpireTime.Before(time.Now()) {
panic("uploadToken已失效") panic(result.BadRequest("uploadToken has expired"))
} }
user := this.userDao.CheckByUuid(uploadToken.UserUuid) user := this.userDao.CheckByUuid(uploadToken.UserUuid)
@ -297,89 +273,71 @@ func (this *AlienController) CrawlToken(writer http.ResponseWriter, request *htt
matter := this.matterService.AtomicCrawl(request, url, uploadToken.Filename, user, dirMatter, uploadToken.Privacy) matter := this.matterService.AtomicCrawl(request, url, uploadToken.Filename, user, dirMatter, uploadToken.Privacy)
//更新这个uploadToken的信息. //expire the upload token.
uploadToken.ExpireTime = time.Now() uploadToken.ExpireTime = time.Now()
this.uploadTokenDao.Save(uploadToken) this.uploadTokenDao.Save(uploadToken)
return this.Success(matter) return this.Success(matter)
} }
//通过一个url直接上传无需借助uploadToken. //crawl a url directly. only user can visit this method.
func (this *AlienController) CrawlDirect(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *AlienController) CrawlDirect(writer http.ResponseWriter, request *http.Request) *result.WebResult {
//文件名。
filename := request.FormValue("filename") filename := request.FormValue("filename")
//文件公有或私有
privacyStr := request.FormValue("privacy") privacyStr := request.FormValue("privacy")
//文件夹路径,以 / 开头。 dirPath := request.FormValue("dirPath")
dir := request.FormValue("dir")
url := request.FormValue("url") url := request.FormValue("url")
if filename == "" { if filename == "" {
panic("文件名必填") panic(result.BadRequest("filename cannot be null."))
} else if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, filename); m { } else if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, filename); m {
panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars)) panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars))
} }
var privacy bool var privacy bool
if privacyStr == "" {
panic(`文件公有性必填`)
} else {
if privacyStr == TRUE { if privacyStr == TRUE {
privacy = true privacy = true
} else if privacyStr == FALSE {
privacy = false
} else {
panic(`文件公有性不符合规范`)
}
} }
user := this.checkUser(request) user := this.checkUser(request)
dirMatter := this.matterService.CreateDirectories(request, user, dir) dirMatter := this.matterService.CreateDirectories(request, user, dirPath)
matter := this.matterService.AtomicCrawl(request, url, filename, user, dirMatter, privacy) matter := this.matterService.AtomicCrawl(request, url, filename, user, dirMatter, privacy)
return this.Success(matter) return this.Success(matter)
} }
//系统中的用户x要获取一个DownloadToken用于提供给x信任的用户下载文件。 //fetch a download token for guest. Guest can download file with this token.
func (this *AlienController) FetchDownloadToken(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *AlienController) FetchDownloadToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
matterUuid := request.FormValue("matterUuid") matterUuid := request.FormValue("matterUuid")
expireTimeStr := request.FormValue("expireTime")
if matterUuid == "" { if matterUuid == "" {
panic("matterUuid必填") panic(result.BadRequest("matterUuid cannot be null."))
} }
user := this.checkUser(request) user := this.checkUser(request)
matter := this.matterDao.CheckByUuid(matterUuid) matter := this.matterDao.CheckByUuid(matterUuid)
if matter.UserUuid != user.Uuid { if matter.UserUuid != user.Uuid {
panic("文件不属于你") panic(result.BadRequest("matter not belong to you"))
}
if matter.Dir {
panic("不支持下载文件夹")
} }
//什么时间后过期默认24h var expireTime time.Time
expireStr := request.FormValue("expire") if expireTimeStr == "" {
expire := 24 * 60 * 60 panic(result.BadRequest("time format error"))
if expireStr != "" { } else {
var err error expireTime = util.ConvertDateTimeStringToTime(expireTimeStr)
expire, err = strconv.Atoi(expireStr)
if err != nil {
panic(`过期时间不符合规范`)
} }
if expire < 1 { if expireTime.Before(time.Now()) {
panic(`过期时间不符合规范`) panic(result.BadRequest("expire time cannot before now"))
} }
}
mm, _ := time.ParseDuration(fmt.Sprintf("%ds", expire))
downloadToken := &DownloadToken{ downloadToken := &DownloadToken{
UserUuid: user.Uuid, UserUuid: user.Uuid,
MatterUuid: matterUuid, MatterUuid: matterUuid,
ExpireTime: time.Now().Add(mm), ExpireTime: expireTime,
Ip: util.GetIpAddress(request), Ip: util.GetIpAddress(request),
} }
@ -389,13 +347,13 @@ func (this *AlienController) FetchDownloadToken(writer http.ResponseWriter, requ
} }
//预览一个文件。既可以使用登录的方式,也可以使用授权的方式 //preview a file.
func (this *AlienController) Preview(writer http.ResponseWriter, request *http.Request, uuid string, filename string) { func (this *AlienController) Preview(writer http.ResponseWriter, request *http.Request, uuid string, filename string) {
this.alienService.PreviewOrDownload(writer, request, uuid, filename, false) this.alienService.PreviewOrDownload(writer, request, uuid, filename, false)
} }
//下载一个文件。既可以使用登录的方式,也可以使用授权的方式 //download a file.
func (this *AlienController) Download(writer http.ResponseWriter, request *http.Request, uuid string, filename string) { func (this *AlienController) Download(writer http.ResponseWriter, request *http.Request, uuid string, filename string) {
this.alienService.PreviewOrDownload(writer, request, uuid, filename, true) this.alienService.PreviewOrDownload(writer, request, uuid, filename, true)

View File

@ -21,11 +21,9 @@ type AlienService struct {
imageCacheService *ImageCacheService imageCacheService *ImageCacheService
} }
//初始化方法
func (this *AlienService) Init() { func (this *AlienService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.matterDao) b := core.CONTEXT.GetBean(this.matterDao)
if b, ok := b.(*MatterDao); ok { if b, ok := b.(*MatterDao); ok {
this.matterDao = b this.matterDao = b
@ -67,7 +65,6 @@ func (this *AlienService) Init() {
} }
} }
//预览或者下载的统一处理.
func (this *AlienService) PreviewOrDownload( func (this *AlienService) PreviewOrDownload(
writer http.ResponseWriter, writer http.ResponseWriter,
request *http.Request, request *http.Request,
@ -78,23 +75,22 @@ func (this *AlienService) PreviewOrDownload(
matter := this.matterDao.CheckByUuid(uuid) matter := this.matterDao.CheckByUuid(uuid)
if matter.Name != filename { if matter.Name != filename {
panic("文件信息错误") panic(result.BadRequest("filename in url incorrect"))
} }
//验证用户的权限问题。 //only private file need auth.
//文件如果是私有的才需要权限
if matter.Privacy { if matter.Privacy {
//1.如果带有downloadTokenUuid那么就按照token的信息去获取。 //1.use downloadToken to auth.
downloadTokenUuid := request.FormValue("downloadTokenUuid") downloadTokenUuid := request.FormValue("downloadTokenUuid")
if downloadTokenUuid != "" { if downloadTokenUuid != "" {
downloadToken := this.downloadTokenDao.CheckByUuid(downloadTokenUuid) downloadToken := this.downloadTokenDao.CheckByUuid(downloadTokenUuid)
if downloadToken.ExpireTime.Before(time.Now()) { if downloadToken.ExpireTime.Before(time.Now()) {
panic("downloadToken已失效") panic(result.BadRequest("downloadToken has expired"))
} }
if downloadToken.MatterUuid != uuid { if downloadToken.MatterUuid != uuid {
panic("token和文件信息不一致") panic(result.BadRequest("token and file info not match"))
} }
tokenUser := this.userDao.CheckByUuid(downloadToken.UserUuid) tokenUser := this.userDao.CheckByUuid(downloadToken.UserUuid)
@ -102,16 +98,15 @@ func (this *AlienService) PreviewOrDownload(
panic(result.UNAUTHORIZED) panic(result.UNAUTHORIZED)
} }
//下载之后立即过期掉。如果是分块下载的,必须以最终获取到完整的数据为准。 //TODO: expire the download token. If download by chunk, do this later.
downloadToken.ExpireTime = time.Now() downloadToken.ExpireTime = time.Now()
this.downloadTokenDao.Save(downloadToken) this.downloadTokenDao.Save(downloadToken)
} else { } else {
//判断文件的所属人是否正确
operator := this.findUser(request) operator := this.findUser(request)
//可以使用分享码的形式授权。 //use share code to auth.
shareUuid := request.FormValue("shareUuid") shareUuid := request.FormValue("shareUuid")
shareCode := request.FormValue("shareCode") shareCode := request.FormValue("shareCode")
shareRootUuid := request.FormValue("shareRootUuid") shareRootUuid := request.FormValue("shareRootUuid")
@ -121,27 +116,25 @@ func (this *AlienService) PreviewOrDownload(
} }
} }
//文件夹下载 //download directory
if matter.Dir { if matter.Dir {
this.logger.Info("准备下载文件夹 %s", matter.Name)
//目标地点
this.matterService.DownloadZip(writer, request, []*Matter{matter}) this.matterService.DownloadZip(writer, request, []*Matter{matter})
} else { } else {
//对图片处理。 //handle the image operation.
needProcess, imageResizeM, imageResizeW, imageResizeH := this.imageCacheService.ResizeParams(request) needProcess, imageResizeM, imageResizeW, imageResizeH := this.imageCacheService.ResizeParams(request)
if needProcess { if needProcess {
//如果是图片,那么能用缓存就用缓存 //if image, try to use cache.
imageCache := this.imageCacheDao.FindByMatterUuidAndMode(matter.Uuid, fmt.Sprintf("%s_%d_%d", imageResizeM, imageResizeW, imageResizeH)) mode := fmt.Sprintf("%s_%d_%d", imageResizeM, imageResizeW, imageResizeH)
imageCache := this.imageCacheDao.FindByMatterUuidAndMode(matter.Uuid, mode)
if imageCache == nil { if imageCache == nil {
imageCache = this.imageCacheService.cacheImage(writer, request, matter) imageCache = this.imageCacheService.cacheImage(writer, request, matter)
} }
//直接使用缓存中的信息 //download the cache image file.
this.matterService.DownloadFile(writer, request, GetUserCacheRootDir(imageCache.Username)+imageCache.Path, imageCache.Name, withContentDisposition) this.matterService.DownloadFile(writer, request, GetUserCacheRootDir(imageCache.Username)+imageCache.Path, imageCache.Name, withContentDisposition)
} else { } else {
@ -150,7 +143,7 @@ func (this *AlienService) PreviewOrDownload(
} }
//文件下载次数加一,为了加快访问速度,异步进行 //async increase the download times.
go core.RunWithRecovery(func() { go core.RunWithRecovery(func() {
this.matterDao.TimesIncrement(uuid) this.matterDao.TimesIncrement(uuid)
}) })

View File

@ -19,50 +19,48 @@ func (this *BaseBean) Bootstrap() {
} }
//系统大清理,一般时产品即将上线时,清除脏数据,只执行一次。 //clean up the application.
func (this *BaseBean) Cleanup() { func (this *BaseBean) Cleanup() {
} }
//处理错误的统一方法 可以省去if err!=nil 这段代码 //shortcut for panic check.
func (this *BaseBean) PanicError(err error) { func (this *BaseBean) PanicError(err error) {
core.PanicError(err) core.PanicError(err)
} }
//能找到一个user就找到一个 //find the current user from request.
func (this *BaseBean) findUser(request *http.Request) *User { func (this *BaseBean) findUser(request *http.Request) *User {
//验证用户是否已经登录。 //try to find from SessionCache.
//登录身份有效期以数据库中记录的为准
sessionId := util.GetSessionUuidFromRequest(request, core.COOKIE_AUTH_KEY) sessionId := util.GetSessionUuidFromRequest(request, core.COOKIE_AUTH_KEY)
if sessionId == "" { if sessionId == "" {
return nil return nil
} }
//去缓存中捞取看看
cacheItem, err := core.CONTEXT.GetSessionCache().Value(sessionId) cacheItem, err := core.CONTEXT.GetSessionCache().Value(sessionId)
if err != nil { if err != nil {
this.logger.Warn("获取缓存时出错了" + err.Error()) this.logger.Warn("error while get from session cache. sessionId = %s, error = %v", sessionId, err)
return nil return nil
} }
if cacheItem == nil || cacheItem.Data() == nil { if cacheItem == nil || cacheItem.Data() == nil {
this.logger.Warn("cache item中已经不存在了 ") this.logger.Warn("cache item doesn't exist with sessionId = %s", sessionId)
return nil return nil
} }
if value, ok := cacheItem.Data().(*User); ok { if value, ok := cacheItem.Data().(*User); ok {
return value return value
} else { } else {
this.logger.Error("cache item中的类型不是*User ") this.logger.Error("cache item not store the *User")
} }
return nil return nil
} }
//获取当前登录的用户,找不到就返回登录错误 //find current error. If not found, panic the LOGIN error.
func (this *BaseBean) checkUser(request *http.Request) *User { func (this *BaseBean) checkUser(request *http.Request) *User {
if this.findUser(request) == nil { if this.findUser(request) == nil {
panic(result.LOGIN) panic(result.LOGIN)

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/result" "github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util"
"github.com/json-iterator/go" "github.com/json-iterator/go"
"go/types" "go/types"
"net/http" "net/http"
@ -19,7 +20,6 @@ func (this *BaseController) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean.
b := core.CONTEXT.GetBean(this.userDao) b := core.CONTEXT.GetBean(this.userDao)
if b, ok := b.(*UserDao); ok { if b, ok := b.(*UserDao); ok {
this.userDao = b this.userDao = b
@ -32,32 +32,29 @@ func (this *BaseController) Init() {
} }
//注册自己的路由。
func (this *BaseController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *BaseController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
//每个Controller需要主动注册自己的路由。
return make(map[string]func(writer http.ResponseWriter, request *http.Request)) return make(map[string]func(writer http.ResponseWriter, request *http.Request))
} }
//处理一些特殊的接口,比如参数包含在路径中,一般情况下controller不将参数放在url路径中 //handle some special routes, eg. params in the url.
func (this *BaseController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) { func (this *BaseController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
return nil, false return nil, false
} }
//需要进行登录验证的wrap包装 //wrap the handle method.
func (this *BaseController) Wrap(f func(writer http.ResponseWriter, request *http.Request) *result.WebResult, qualifiedRole string) func(w http.ResponseWriter, r *http.Request) { func (this *BaseController) Wrap(f func(writer http.ResponseWriter, request *http.Request) *result.WebResult, qualifiedRole string) func(w http.ResponseWriter, r *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) { return func(writer http.ResponseWriter, request *http.Request) {
//writer和request赋值给自己。
var webResult *result.WebResult = nil var webResult *result.WebResult = nil
//只有游客接口不需要登录 //if the api not annotated with GUEST. login is required.
if qualifiedRole != USER_ROLE_GUEST { if qualifiedRole != USER_ROLE_GUEST {
user := this.checkUser(request) user := this.checkUser(request)
if user.Status == USER_STATUS_DISABLED { if user.Status == USER_STATUS_DISABLED {
//判断用户是否被禁用。 //check user's status
webResult = result.ConstWebResult(result.USER_DISABLED) webResult = result.ConstWebResult(result.USER_DISABLED)
} else { } else {
if qualifiedRole == USER_ROLE_ADMINISTRATOR && user.Role != USER_ROLE_ADMINISTRATOR { if qualifiedRole == USER_ROLE_ADMINISTRATOR && user.Role != USER_ROLE_ADMINISTRATOR {
@ -71,12 +68,11 @@ func (this *BaseController) Wrap(f func(writer http.ResponseWriter, request *htt
webResult = f(writer, request) webResult = f(writer, request)
} }
//输出的是json格式 //if webResult not nil. response a json. if webResult is nil, return empty body or binary content.
if webResult != nil { if webResult != nil {
//返回的内容申明是jsonutf-8
writer.Header().Set("Content-Type", "application/json;charset=UTF-8") writer.Header().Set("Content-Type", "application/json;charset=UTF-8")
//用json的方式输出返回值。
b, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(webResult) b, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(webResult)
this.PanicError(err) this.PanicError(err)
@ -85,36 +81,31 @@ func (this *BaseController) Wrap(f func(writer http.ResponseWriter, request *htt
_, err = fmt.Fprintf(writer, string(b)) _, err = fmt.Fprintf(writer, string(b))
this.PanicError(err) this.PanicError(err)
} else {
//输出的内容是二进制的。
} }
} }
} }
//返回成功的结果。支持放置三种类型 1.字符串 2. WebResult对象 3.空指针 4.任意类型 //response a success result. 1.string 2. WebResult 3.nil pointer 4.any type
func (this *BaseController) Success(data interface{}) *result.WebResult { func (this *BaseController) Success(data interface{}) *result.WebResult {
var webResult *result.WebResult = nil var webResult *result.WebResult = nil
if value, ok := data.(string); ok { if value, ok := data.(string); ok {
//返回一句普通的消息 //a simple message
webResult = &result.WebResult{Code: result.OK.Code, Msg: value} webResult = &result.WebResult{Code: result.OK.Code, Msg: value}
} else if value, ok := data.(*result.WebResult); ok { } else if value, ok := data.(*result.WebResult); ok {
//返回一个webResult对象 //a webResult
webResult = value webResult = value
} else if _, ok := data.(types.Nil); ok { } else if _, ok := data.(types.Nil); ok {
//返回一个空指针 //nil pointer means OK.
webResult = result.ConstWebResult(result.OK) webResult = result.ConstWebResult(result.OK)
} else { } else {
//返回的类型不明确。 //other type.
webResult = &result.WebResult{Code: result.OK.Code, Data: data} webResult = &result.WebResult{Code: result.OK.Code, Data: data}
} }
return webResult return webResult
} }
//允许跨域请求 //allow cors.
func (this *BaseController) allowCORS(writer http.ResponseWriter) { func (this *BaseController) allowCORS(writer http.ResponseWriter) {
writer.Header().Add("Access-Control-Allow-Origin", "*") util.AllowCORS(writer)
writer.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
writer.Header().Add("Access-Control-Max-Age", "3600")
} }

View File

@ -1,12 +1,14 @@
package rest package rest
import "github.com/eyebluecn/tank/code/tool/builder" import (
"github.com/eyebluecn/tank/code/tool/builder"
)
type BaseDao struct { type BaseDao struct {
BaseBean BaseBean
} }
//根据一个sortMap获取到order字符串 //get an order string by sortMap
func (this *BaseDao) GetSortString(sortArray []builder.OrderPair) string { func (this *BaseDao) GetSortString(sortArray []builder.OrderPair) string {
if sortArray == nil || len(sortArray) == 0 { if sortArray == nil || len(sortArray) == 0 {
@ -14,7 +16,7 @@ func (this *BaseDao) GetSortString(sortArray []builder.OrderPair) string {
} }
str := "" str := ""
for _, pair := range sortArray { for _, pair := range sortArray {
if pair.Value == "DESC" || pair.Value == "ASC" { if pair.Value == DIRECTION_DESC || pair.Value == DIRECTION_ASC {
if str != "" { if str != "" {
str = str + "," str = str + ","
} }

View File

@ -1,9 +1,7 @@
package rest package rest
import ( import (
"github.com/eyebluecn/tank/code/core"
"math" "math"
"reflect"
"time" "time"
) )
@ -16,11 +14,12 @@ const (
) )
type IBase interface { type IBase interface {
//返回其对应的数据库表名 //name of db table
TableName() string TableName() string
} }
//Mysql 5.5只支持一个CURRENT_TIMESTAMP的默认值因此时间的默认值都使用蓝眼云盘第一个发布版本时间 2018-01-01 00:00:00 // Mysql 5.5 only support one CURRENT_TIMESTAMP
// so we use 2018-01-01 00:00:00 as default, which is the first release date of EyeblueTank
type Base struct { type Base struct {
Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"` Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"`
Sort int64 `json:"sort" gorm:"type:bigint(20) not null"` Sort int64 `json:"sort" gorm:"type:bigint(20) not null"`
@ -28,23 +27,11 @@ type Base struct {
CreateTime time.Time `json:"createTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"` CreateTime time.Time `json:"createTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
} }
//将 Struct 转换成map[string]interface{}类型
func (this *Base) Map() map[string]interface{} {
t := reflect.TypeOf(this)
v := reflect.ValueOf(this)
var data = make(map[string]interface{})
for i := 0; i < t.NumField(); i++ {
data[t.Field(i).Name] = v.Field(i).Interface()
}
return data
}
func (this *Base) TableName() string { func (this *Base) TableName() string {
return core.TABLE_PREFIX + "base" panic("you should overwrite TableName()")
} }
//分页类 //pager
type Pager struct { type Pager struct {
Page int `json:"page"` Page int `json:"page"`
PageSize int `json:"pageSize"` PageSize int `json:"pageSize"`

View File

@ -14,43 +14,51 @@ type BridgeDao struct {
BaseDao BaseDao
} }
//按照Id查询文件 //find by uuid. if not found return nil.
func (this *BridgeDao) FindByUuid(uuid string) *Bridge { func (this *BridgeDao) FindByUuid(uuid string) *Bridge {
// Read var bridge = &Bridge{}
var bridge Bridge db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(bridge)
db := core.CONTEXT.GetDB().Where(&Bridge{Base: Base{Uuid: uuid}}).First(&bridge)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else {
panic(db.Error)
} }
return &bridge }
return bridge
} }
//按照Id查询文件 //find by uuid. if not found panic NotFound error
func (this *BridgeDao) CheckByUuid(uuid string) *Bridge { func (this *BridgeDao) CheckByUuid(uuid string) *Bridge {
// Read entity := this.FindByUuid(uuid)
var bridge Bridge if entity == nil {
db := core.CONTEXT.GetDB().Where(&Bridge{Base: Base{Uuid: uuid}}).First(&bridge) panic(result.NotFound("not found record with uuid = %s", uuid))
this.PanicError(db.Error) }
return &bridge return entity
} }
//按照shareUuid和matterUuid查找 //find by shareUuid and matterUuid. if not found panic NotFound error.
func (this *BridgeDao) CheckByShareUuidAndMatterUuid(shareUuid string, matterUuid string) *Bridge { func (this *BridgeDao) CheckByShareUuidAndMatterUuid(shareUuid string, matterUuid string) *Bridge {
// Read var bridge = &Bridge{}
var bridge Bridge db := core.CONTEXT.GetDB().Where("share_uuid = ? AND matter_uuid = ?", shareUuid, matterUuid).First(bridge)
db := core.CONTEXT.GetDB().Where("share_uuid = ? AND matter_uuid = ?", shareUuid, matterUuid).First(&bridge) if db.Error != nil {
this.PanicError(db.Error) if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
panic(result.NotFound("not found record with shareUuid = %s and matterUuid = %s", shareUuid, matterUuid))
return &bridge } else {
panic(db.Error)
}
} }
//按分页条件获取分页 return bridge
}
//get pager
func (this *BridgeDao) Page(page int, pageSize int, shareUuid string, sortArray []builder.OrderPair) *Pager { func (this *BridgeDao) Page(page int, pageSize int, shareUuid string, sortArray []builder.OrderPair) *Pager {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
@ -74,7 +82,6 @@ func (this *BridgeDao) Page(page int, pageSize int, shareUuid string, sortArray
return pager return pager
} }
//创建
func (this *BridgeDao) Create(bridge *Bridge) *Bridge { func (this *BridgeDao) Create(bridge *Bridge) *Bridge {
timeUUID, _ := uuid.NewV4() timeUUID, _ := uuid.NewV4()
@ -88,7 +95,6 @@ func (this *BridgeDao) Create(bridge *Bridge) *Bridge {
return bridge return bridge
} }
//修改一条记录
func (this *BridgeDao) Save(bridge *Bridge) *Bridge { func (this *BridgeDao) Save(bridge *Bridge) *Bridge {
bridge.UpdateTime = time.Now() bridge.UpdateTime = time.Now()
@ -98,39 +104,33 @@ func (this *BridgeDao) Save(bridge *Bridge) *Bridge {
return bridge return bridge
} }
//删除一条记录
func (this *BridgeDao) Delete(bridge *Bridge) { func (this *BridgeDao) Delete(bridge *Bridge) {
db := core.CONTEXT.GetDB().Delete(&bridge) db := core.CONTEXT.GetDB().Delete(&bridge)
this.PanicError(db.Error) this.PanicError(db.Error)
} }
//删除一个matter对应的所有缓存
func (this *BridgeDao) DeleteByMatterUuid(matterUuid string) { func (this *BridgeDao) DeleteByMatterUuid(matterUuid string) {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
wp = wp.And(&builder.WherePair{Query: "matter_uuid = ?", Args: []interface{}{matterUuid}}) wp = wp.And(&builder.WherePair{Query: "matter_uuid = ?", Args: []interface{}{matterUuid}})
//删除文件记录
db := core.CONTEXT.GetDB().Where(wp.Query, wp.Args).Delete(Bridge{}) db := core.CONTEXT.GetDB().Where(wp.Query, wp.Args).Delete(Bridge{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }
//删除一个share对应的所有缓存
func (this *BridgeDao) DeleteByShareUuid(shareUuid string) { func (this *BridgeDao) DeleteByShareUuid(shareUuid string) {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
wp = wp.And(&builder.WherePair{Query: "share_uuid = ?", Args: []interface{}{shareUuid}}) wp = wp.And(&builder.WherePair{Query: "share_uuid = ?", Args: []interface{}{shareUuid}})
//删除文件记录
db := core.CONTEXT.GetDB().Where(wp.Query, wp.Args).Delete(Bridge{}) db := core.CONTEXT.GetDB().Where(wp.Query, wp.Args).Delete(Bridge{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }
//根据shareUuid获取关联的所有matter. func (this *BridgeDao) FindByShareUuid(shareUuid string) []*Bridge {
func (this *BridgeDao) ListByShareUuid(shareUuid string) []*Bridge {
if shareUuid == "" { if shareUuid == "" {
panic(result.BadRequest("shareUuid cannot be nil")) panic(result.BadRequest("shareUuid cannot be nil"))
@ -146,9 +146,8 @@ func (this *BridgeDao) ListByShareUuid(shareUuid string) []*Bridge {
return bridges return bridges
} }
//执行清理操作
func (this *BridgeDao) Cleanup() { func (this *BridgeDao) Cleanup() {
this.logger.Info("[BridgeDao]执行清理清除数据库中所有Bridge记录。") this.logger.Info("[BridgeDao] cleanup: delete all bridge records.")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Bridge{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Bridge{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -5,7 +5,7 @@ import (
) )
/** /**
* 分享记录和matter的关联表 * the link table for Share and Matter.
*/ */
type Bridge struct { type Bridge struct {
Base Base
@ -13,7 +13,6 @@ type Bridge struct {
MatterUuid string `json:"matterUuid" gorm:"type:char(36)"` MatterUuid string `json:"matterUuid" gorm:"type:char(36)"`
} }
// set File's table name to be `profiles`
func (this *Bridge) TableName() string { func (this *Bridge) TableName() string {
return core.TABLE_PREFIX + "bridge" return core.TABLE_PREFIX + "bridge"
} }

View File

@ -11,11 +11,9 @@ type BridgeService struct {
userDao *UserDao userDao *UserDao
} }
//初始化方法
func (this *BridgeService) Init() { func (this *BridgeService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.bridgeDao) b := core.CONTEXT.GetBean(this.bridgeDao)
if b, ok := b.(*BridgeDao); ok { if b, ok := b.(*BridgeDao); ok {
this.bridgeDao = b this.bridgeDao = b
@ -28,7 +26,6 @@ func (this *BridgeService) Init() {
} }
//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。
func (this *BridgeService) Detail(uuid string) *Bridge { func (this *BridgeService) Detail(uuid string) *Bridge {
bridge := this.bridgeDao.CheckByUuid(uuid) bridge := this.bridgeDao.CheckByUuid(uuid)

View File

@ -14,11 +14,9 @@ type DashboardController struct {
dashboardService *DashboardService dashboardService *DashboardService
} }
//初始化方法
func (this *DashboardController) Init() { func (this *DashboardController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.dashboardDao) b := core.CONTEXT.GetBean(this.dashboardDao)
if b, ok := b.(*DashboardDao); ok { if b, ok := b.(*DashboardDao); ok {
this.dashboardDao = b this.dashboardDao = b
@ -28,32 +26,20 @@ func (this *DashboardController) Init() {
if b, ok := b.(*DashboardService); ok { if b, ok := b.(*DashboardService); ok {
this.dashboardService = b this.dashboardService = b
} }
} }
//注册自己的路由。
func (this *DashboardController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *DashboardController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
//每个Controller需要主动注册自己的路由。
routeMap["/api/dashboard/page"] = this.Wrap(this.Page, USER_ROLE_ADMINISTRATOR) routeMap["/api/dashboard/page"] = this.Wrap(this.Page, USER_ROLE_ADMINISTRATOR)
routeMap["/api/dashboard/active/ip/top10"] = this.Wrap(this.ActiveIpTop10, USER_ROLE_ADMINISTRATOR) routeMap["/api/dashboard/active/ip/top10"] = this.Wrap(this.ActiveIpTop10, USER_ROLE_ADMINISTRATOR)
return routeMap return routeMap
} }
//过去七天分时调用量
func (this *DashboardController) InvokeList(writer http.ResponseWriter, request *http.Request) *result.WebResult {
return this.Success("")
}
//按照分页的方式获取某个图片缓存夹下图片缓存和子图片缓存夹的列表,通常情况下只有一页。
func (this *DashboardController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *DashboardController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult {
//如果是根目录那么就传入root.
pageStr := request.FormValue("page") pageStr := request.FormValue("page")
pageSizeStr := request.FormValue("pageSize") pageSizeStr := request.FormValue("pageSize")
orderCreateTime := request.FormValue("orderCreateTime") orderCreateTime := request.FormValue("orderCreateTime")
@ -99,6 +85,7 @@ func (this *DashboardController) Page(writer http.ResponseWriter, request *http.
} }
func (this *DashboardController) ActiveIpTop10(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *DashboardController) ActiveIpTop10(writer http.ResponseWriter, request *http.Request) *result.WebResult {
//TODO:
list := this.dashboardDao.ActiveIpTop10() list := this.dashboardDao.ActiveIpTop10()
return this.Success(list) return this.Success(list)
} }

View File

@ -12,7 +12,6 @@ type DashboardDao struct {
BaseDao BaseDao
} }
//创建
func (this *DashboardDao) Create(dashboard *Dashboard) *Dashboard { func (this *DashboardDao) Create(dashboard *Dashboard) *Dashboard {
timeUUID, _ := uuid.NewV4() timeUUID, _ := uuid.NewV4()
@ -26,7 +25,6 @@ func (this *DashboardDao) Create(dashboard *Dashboard) *Dashboard {
return dashboard return dashboard
} }
//修改一条记录
func (this *DashboardDao) Save(dashboard *Dashboard) *Dashboard { func (this *DashboardDao) Save(dashboard *Dashboard) *Dashboard {
dashboard.UpdateTime = time.Now() dashboard.UpdateTime = time.Now()
@ -36,26 +34,22 @@ func (this *DashboardDao) Save(dashboard *Dashboard) *Dashboard {
return dashboard return dashboard
} }
//删除一条记录
func (this *DashboardDao) Delete(dashboard *Dashboard) { func (this *DashboardDao) Delete(dashboard *Dashboard) {
db := core.CONTEXT.GetDB().Delete(&dashboard) db := core.CONTEXT.GetDB().Delete(&dashboard)
this.PanicError(db.Error) this.PanicError(db.Error)
} }
//按照dt查询
func (this *DashboardDao) FindByDt(dt string) *Dashboard { func (this *DashboardDao) FindByDt(dt string) *Dashboard {
// Read var dashboard = &Dashboard{}
var dashboard Dashboard db := core.CONTEXT.GetDB().Where("dt = ?", dt).First(dashboard)
db := core.CONTEXT.GetDB().Where(&Dashboard{Dt: dt}).First(&dashboard)
if db.Error != nil { if db.Error != nil {
return nil return nil
} }
return &dashboard return dashboard
} }
//获取某个文件夹下所有的文件和子文件
func (this *DashboardDao) Page(page int, pageSize int, dt string, sortArray []builder.OrderPair) *Pager { func (this *DashboardDao) Page(page int, pageSize int, dt string, sortArray []builder.OrderPair) *Pager {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
@ -79,7 +73,6 @@ func (this *DashboardDao) Page(page int, pageSize int, dt string, sortArray []bu
return pager return pager
} }
//获取最活跃的前10个ip
func (this *DashboardDao) ActiveIpTop10() []*DashboardIpTimes { func (this *DashboardDao) ActiveIpTop10() []*DashboardIpTimes {
var dashboardIpTimes []*DashboardIpTimes var dashboardIpTimes []*DashboardIpTimes
@ -102,7 +95,8 @@ func (this *DashboardDao) ActiveIpTop10() []*DashboardIpTimes {
for rows.Next() { for rows.Next() {
var ip string var ip string
var times int64 = 0 var times int64 = 0
rows.Scan(&ip, &times) err := rows.Scan(&ip, &times)
this.PanicError(err)
item := &DashboardIpTimes{ item := &DashboardIpTimes{
Ip: ip, Ip: ip,
Times: times, Times: times,
@ -113,9 +107,8 @@ func (this *DashboardDao) ActiveIpTop10() []*DashboardIpTimes {
return dashboardIpTimes return dashboardIpTimes
} }
//执行清理操作
func (this *DashboardDao) Cleanup() { func (this *DashboardDao) Cleanup() {
this.logger.Info("[DashboardDao]执行清理清除数据库中所有Dashboard记录。") this.logger.Info("[DashboardDao] cleanup. Delete all Dashboard records")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Dashboard{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Dashboard{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -3,20 +3,20 @@ package rest
import "github.com/eyebluecn/tank/code/core" import "github.com/eyebluecn/tank/code/core"
/** /**
* 系统的所有访问记录均记录在此 * application's dashboard.
*/ */
type Dashboard struct { type Dashboard struct {
Base Base
InvokeNum int64 `json:"invokeNum" gorm:"type:bigint(20) not null"` //当日访问量 InvokeNum int64 `json:"invokeNum" gorm:"type:bigint(20) not null"` //api invoke num.
TotalInvokeNum int64 `json:"totalInvokeNum" gorm:"type:bigint(20) not null;default:0"` //截至目前总访问量 TotalInvokeNum int64 `json:"totalInvokeNum" gorm:"type:bigint(20) not null;default:0"` //total invoke num up to now.
Uv int64 `json:"uv" gorm:"type:bigint(20) not null;default:0"` //当日UV Uv int64 `json:"uv" gorm:"type:bigint(20) not null;default:0"` //today's uv
TotalUv int64 `json:"totalUv" gorm:"type:bigint(20) not null;default:0"` //截至目前总UV TotalUv int64 `json:"totalUv" gorm:"type:bigint(20) not null;default:0"` //total uv
MatterNum int64 `json:"matterNum" gorm:"type:bigint(20) not null;default:0"` //文件数量 MatterNum int64 `json:"matterNum" gorm:"type:bigint(20) not null;default:0"` //file's num
TotalMatterNum int64 `json:"totalMatterNum" gorm:"type:bigint(20) not null;default:0"` //截至目前文件数量 TotalMatterNum int64 `json:"totalMatterNum" gorm:"type:bigint(20) not null;default:0"` //file's total number
FileSize int64 `json:"fileSize" gorm:"type:bigint(20) not null;default:0"` //当日文件大小 FileSize int64 `json:"fileSize" gorm:"type:bigint(20) not null;default:0"` //today's file size
TotalFileSize int64 `json:"totalFileSize" gorm:"type:bigint(20) not null;default:0"` //截至目前文件总大小 TotalFileSize int64 `json:"totalFileSize" gorm:"type:bigint(20) not null;default:0"` //total file's size
AvgCost int64 `json:"avgCost" gorm:"type:bigint(20) not null;default:0"` //请求平均耗时 ms AvgCost int64 `json:"avgCost" gorm:"type:bigint(20) not null;default:0"` //api time cost in ms
Dt string `json:"dt" gorm:"type:varchar(45) not null;index:idx_dt"` //日期 Dt string `json:"dt" gorm:"type:varchar(45) not null;index:idx_dt"` //date
} }
// set File's table name to be `profiles` // set File's table name to be `profiles`
@ -25,7 +25,7 @@ func (this *Dashboard) TableName() string {
} }
/** /**
* 统计IP活跃数的 * ip
*/ */
type DashboardIpTimes struct { type DashboardIpTimes struct {
Ip string `json:"ip"` Ip string `json:"ip"`

View File

@ -17,11 +17,9 @@ type DashboardService struct {
userDao *UserDao userDao *UserDao
} }
//初始化方法
func (this *DashboardService) Init() { func (this *DashboardService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.dashboardDao) b := core.CONTEXT.GetBean(this.dashboardDao)
if b, ok := b.(*DashboardDao); ok { if b, ok := b.(*DashboardDao); ok {
this.dashboardDao = b this.dashboardDao = b
@ -49,81 +47,61 @@ func (this *DashboardService) Init() {
} }
//系统启动,数据库配置完毕后会调用该方法
func (this *DashboardService) Bootstrap() { func (this *DashboardService) Bootstrap() {
//每日00:05分清洗离线数据 this.logger.Info("[cron job] Everyday 00:05 ETL dashboard data.")
expression := "0 5 0 * * ?" expression := "0 5 0 * * ?"
cronJob := cron.New() cronJob := cron.New()
err := cronJob.AddFunc(expression, this.etl) err := cronJob.AddFunc(expression, this.etl)
core.PanicError(err) core.PanicError(err)
cronJob.Start() cronJob.Start()
this.logger.Info("[cron job] 每日00:05清洗离线数据")
//立即执行一次数据清洗任务 //do the etl method now.
go core.RunWithRecovery(this.etl) go core.RunWithRecovery(this.etl)
} }
//每日清洗离线数据表。 // handle the dashboard data.
func (this *DashboardService) etl() { func (this *DashboardService) etl() {
this.logger.Info("每日定时数据清洗") this.logger.Info("ETL dashboard data.")
//准备日期开始结尾
startTime := util.FirstSecondOfDay(util.Yesterday()) startTime := util.FirstSecondOfDay(util.Yesterday())
endTime := util.LastSecondOfDay(util.Yesterday()) endTime := util.LastSecondOfDay(util.Yesterday())
dt := util.ConvertTimeToDateString(startTime) dt := util.ConvertTimeToDateString(startTime)
longTimeAgo := time.Now() longTimeAgo := time.Now()
longTimeAgo = longTimeAgo.AddDate(-20, 0, 0) longTimeAgo = longTimeAgo.AddDate(-20, 0, 0)
this.logger.Info("统计汇总表 %s -> %s", util.ConvertTimeToDateTimeString(startTime), util.ConvertTimeToDateTimeString(endTime)) this.logger.Info("Time %s -> %s", util.ConvertTimeToDateTimeString(startTime), util.ConvertTimeToDateTimeString(endTime))
//判断昨天的记录是否已经生成,如果生成了就直接删除掉 //check whether the record has created.
dbDashboard := this.dashboardDao.FindByDt(dt) dbDashboard := this.dashboardDao.FindByDt(dt)
if dbDashboard != nil { if dbDashboard != nil {
this.logger.Info(" %s 的汇总已经存在了,删除以进行更新", dt) this.logger.Info(" %s already exits. delete it and insert new one.", dt)
this.dashboardDao.Delete(dbDashboard) this.dashboardDao.Delete(dbDashboard)
} }
invokeNum := this.footprintDao.CountBetweenTime(startTime, endTime) invokeNum := this.footprintDao.CountBetweenTime(startTime, endTime)
this.logger.Info("调用数:%d", invokeNum)
totalInvokeNum := this.footprintDao.CountBetweenTime(longTimeAgo, endTime) totalInvokeNum := this.footprintDao.CountBetweenTime(longTimeAgo, endTime)
this.logger.Info("历史总调用数:%d", totalInvokeNum)
uv := this.footprintDao.UvBetweenTime(startTime, endTime) uv := this.footprintDao.UvBetweenTime(startTime, endTime)
this.logger.Info("UV%d", uv)
totalUv := this.footprintDao.UvBetweenTime(longTimeAgo, endTime) totalUv := this.footprintDao.UvBetweenTime(longTimeAgo, endTime)
this.logger.Info("历史总UV%d", totalUv)
matterNum := this.matterDao.CountBetweenTime(startTime, endTime) matterNum := this.matterDao.CountBetweenTime(startTime, endTime)
this.logger.Info("文件数量数:%d", matterNum)
totalMatterNum := this.matterDao.CountBetweenTime(longTimeAgo, endTime) totalMatterNum := this.matterDao.CountBetweenTime(longTimeAgo, endTime)
this.logger.Info("历史文件总数:%d", totalMatterNum)
var matterSize int64 matterSize := this.matterDao.SizeBetweenTime(startTime, endTime)
if matterNum != 0 {
matterSize = this.matterDao.SizeBetweenTime(startTime, endTime)
}
this.logger.Info("文件大小:%d", matterSize)
var totalMatterSize int64 totalMatterSize := this.matterDao.SizeBetweenTime(longTimeAgo, endTime)
if totalMatterNum != 0 {
totalMatterSize = this.matterDao.SizeBetweenTime(longTimeAgo, endTime)
}
this.logger.Info("历史文件总大小:%d", totalMatterSize)
cacheSize := this.imageCacheDao.SizeBetweenTime(startTime, endTime) cacheSize := this.imageCacheDao.SizeBetweenTime(startTime, endTime)
this.logger.Info("缓存大小:%d", cacheSize)
totalCacheSize := this.imageCacheDao.SizeBetweenTime(longTimeAgo, endTime) totalCacheSize := this.imageCacheDao.SizeBetweenTime(longTimeAgo, endTime)
this.logger.Info("历史缓存总大小:%d", totalCacheSize)
avgCost := this.footprintDao.AvgCostBetweenTime(startTime, endTime) avgCost := this.footprintDao.AvgCostBetweenTime(startTime, endTime)
this.logger.Info("平均耗时:%d ms", avgCost)
this.logger.Info("Dashboard Summery 1. invokeNum = %d, totalInvokeNum = %d, UV = %d, totalUV = %d, matterNum = %d, totalMatterNum = %d",
invokeNum, totalInvokeNum, uv, totalUv, matterNum, totalMatterNum)
this.logger.Info("Dashboard Summery 2. matterSize = %d, totalMatterSize = %d, cacheSize = %d, totalCacheSize = %d, avgCost = %d",
matterSize, totalMatterSize, cacheSize, totalCacheSize, avgCost)
dashboard := &Dashboard{ dashboard := &Dashboard{
InvokeNum: invokeNum, InvokeNum: invokeNum,
@ -139,5 +117,4 @@ func (this *DashboardService) etl() {
} }
this.dashboardDao.Create(dashboard) this.dashboardDao.Create(dashboard)
} }

View File

@ -15,7 +15,7 @@ import (
/** /**
* *
* WebDav协议文档 * WebDav document
* https://tools.ietf.org/html/rfc4918 * https://tools.ietf.org/html/rfc4918
* http://www.webdav.org/specs/rfc4918.html * http://www.webdav.org/specs/rfc4918.html
* *
@ -32,11 +32,9 @@ type DavController struct {
davService *DavService davService *DavService
} }
//初始化方法
func (this *DavController) Init() { func (this *DavController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean.
b := core.CONTEXT.GetBean(this.uploadTokenDao) b := core.CONTEXT.GetBean(this.uploadTokenDao)
if c, ok := b.(*UploadTokenDao); ok { if c, ok := b.(*UploadTokenDao); ok {
this.uploadTokenDao = c this.uploadTokenDao = c
@ -73,12 +71,12 @@ func (this *DavController) Init() {
} }
} }
//通过BasicAuth的方式授权。 //Auth user by BasicAuth
func (this *DavController) CheckCurrentUser(writer http.ResponseWriter, request *http.Request) *User { func (this *DavController) CheckCurrentUser(writer http.ResponseWriter, request *http.Request) *User {
username, password, ok := request.BasicAuth() username, password, ok := request.BasicAuth()
if !ok { if !ok {
//要求前端使用Basic的形式授权 // require the basic auth.
writer.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) writer.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
panic(result.ConstWebResult(result.LOGIN)) panic(result.ConstWebResult(result.LOGIN))
} }
@ -95,7 +93,6 @@ func (this *DavController) CheckCurrentUser(writer http.ResponseWriter, request
return user return user
} }
//注册自己的路由。
func (this *DavController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *DavController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
@ -103,23 +100,20 @@ func (this *DavController) RegisterRoutes() map[string]func(writer http.Response
return routeMap return routeMap
} }
//处理一些特殊的接口,比如参数包含在路径中,一般情况下controller不将参数放在url路径中 //handle some special routes, eg. params in the url.
func (this *DavController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) { func (this *DavController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
path := request.URL.Path path := request.URL.Path
//匹配 /api/dav{subPath} //match /api/dav{subPath}
pattern := fmt.Sprintf(`^%s(.*)$`, WEBDAV_PREFIX) pattern := fmt.Sprintf(`^%s(.*)$`, WEBDAV_PREFIX)
reg := regexp.MustCompile(pattern) reg := regexp.MustCompile(pattern)
strs := reg.FindStringSubmatch(path) strs := reg.FindStringSubmatch(path)
if len(strs) == 2 { if len(strs) == 2 {
var f = func(writer http.ResponseWriter, request *http.Request) { var f = func(writer http.ResponseWriter, request *http.Request) {
subPath := strs[1] subPath := strs[1]
//保证subPath不是以/结尾的。 //guarantee subPath not end with /
//最后多余的/要去掉 subPath = strings.TrimSuffix(subPath, "/")
if strings.HasSuffix(subPath, "/") {
subPath = subPath[0 : len(subPath)-1]
}
this.Index(writer, request, subPath) this.Index(writer, request, subPath)
} }
return f, true return f, true
@ -128,12 +122,10 @@ func (this *DavController) HandleRoutes(writer http.ResponseWriter, request *htt
return nil, false return nil, false
} }
//完成系统安装 func (this *DavController) debug(writer http.ResponseWriter, request *http.Request, subPath string) {
func (this *DavController) Index(writer http.ResponseWriter, request *http.Request, subPath string) {
/*打印所有HEADER以及请求参数*/ //Print the Request info.
fmt.Printf("\n------ %s -- %s ------\n", request.URL, subPath)
fmt.Printf("\n------ 请求: %s -- %s ------\n", request.URL, subPath)
fmt.Printf("\n------Method------\n") fmt.Printf("\n------Method------\n")
fmt.Println(request.Method) fmt.Println(request.Method)
@ -143,13 +135,13 @@ func (this *DavController) Index(writer http.ResponseWriter, request *http.Reque
fmt.Printf("%s = %s\n", key, value) fmt.Printf("%s = %s\n", key, value)
} }
fmt.Printf("\n------请求参数------\n") fmt.Printf("\n------Params------\n")
for key, value := range request.Form { for key, value := range request.Form {
fmt.Printf("%s = %s\n", key, value) fmt.Printf("%s = %s\n", key, value)
} }
fmt.Printf("\n------Body------\n") fmt.Printf("\n------Body------\n")
//ioutil.ReadAll 不可重复读,第二次读的时候就什么都没有了。 //ioutil.ReadAll cannot read again. when read again, there is nothing.
bodyBytes, err := ioutil.ReadAll(request.Body) bodyBytes, err := ioutil.ReadAll(request.Body)
if err != nil { if err != nil {
@ -157,7 +149,7 @@ func (this *DavController) Index(writer http.ResponseWriter, request *http.Reque
} }
fmt.Println(string(bodyBytes)) fmt.Println(string(bodyBytes))
//关闭之后再重新赋值 //close and resign
err = request.Body.Close() err = request.Body.Close()
if err != nil { if err != nil {
panic(err) panic(err)
@ -166,7 +158,12 @@ func (this *DavController) Index(writer http.ResponseWriter, request *http.Reque
fmt.Println("------------------") fmt.Println("------------------")
//获取请求者 }
func (this *DavController) Index(writer http.ResponseWriter, request *http.Request, subPath string) {
//this.debug(writer, request, subPath)
user := this.CheckCurrentUser(writer, request) user := this.CheckCurrentUser(writer, request)
this.davService.HandleDav(writer, request, user, subPath) this.davService.HandleDav(writer, request, user, subPath)

View File

@ -9,16 +9,16 @@ import (
"strconv" "strconv"
) )
//访问前缀,这个是特殊入口 //webdav url prefix.
var WEBDAV_PREFIX = "/api/dav" var WEBDAV_PREFIX = "/api/dav"
//动态的文件属性 //live prop.
type LiveProp struct { type LiveProp struct {
findFn func(user *User, matter *Matter) string findFn func(user *User, matter *Matter) string
dir bool dir bool
} }
//所有的动态属性定义及其值的获取方式 //all live prop map.
var LivePropMap = map[xml.Name]LiveProp{ var LivePropMap = map[xml.Name]LiveProp{
{Space: "DAV:", Local: "resourcetype"}: { {Space: "DAV:", Local: "resourcetype"}: {
findFn: func(user *User, matter *Matter) string { findFn: func(user *User, matter *Matter) string {
@ -106,7 +106,7 @@ var LivePropMap = map[xml.Name]LiveProp{
if user.SizeLimit >= 0 { if user.SizeLimit >= 0 {
size = user.SizeLimit size = user.SizeLimit
} else { } else {
//没有限制默认100G //TODO: no limit, default 100G.
size = 100 * 1024 * 1024 * 1024 size = 100 * 1024 * 1024 * 1024
} }
return fmt.Sprintf(`%d`, size) return fmt.Sprintf(`%d`, size)
@ -115,7 +115,7 @@ var LivePropMap = map[xml.Name]LiveProp{
}, },
{Space: "DAV:", Local: "quota-used-bytes"}: { {Space: "DAV:", Local: "quota-used-bytes"}: {
findFn: func(user *User, matter *Matter) string { findFn: func(user *User, matter *Matter) string {
//已使用大小,默认0 //TODO: default 0
return fmt.Sprintf(`%d`, 0) return fmt.Sprintf(`%d`, 0)
}, },
dir: true, dir: true,

View File

@ -16,10 +16,10 @@ import (
/** /**
* *
* WebDav协议文档 * WebDav document
* https://tools.ietf.org/html/rfc4918 * https://tools.ietf.org/html/rfc4918
* 主要参考 golang.org/x/net/webdav * refer: golang.org/x/net/webdav
* 测试机http://www.webdav.org/neon/litmus/ * test machine: http://www.webdav.org/neon/litmus/
*/ */
//@Service //@Service
type DavService struct { type DavService struct {
@ -28,11 +28,9 @@ type DavService struct {
matterService *MatterService matterService *MatterService
} }
//初始化方法
func (this *DavService) Init() { func (this *DavService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.matterDao) b := core.CONTEXT.GetBean(this.matterDao)
if b, ok := b.(*MatterDao); ok { if b, ok := b.(*MatterDao); ok {
this.matterDao = b this.matterDao = b
@ -45,7 +43,7 @@ func (this *DavService) Init() {
} }
//获取Header头中的Depth值暂不支持 infinity //get the depth in header. Not support infinity yet.
func (this *DavService) ParseDepth(request *http.Request) int { func (this *DavService) ParseDepth(request *http.Request) int {
depth := 1 depth := 1
@ -84,7 +82,7 @@ func (this *DavService) makePropstatResponse(href string, pstats []dav.Propstat)
return &resp return &resp
} }
//从一个matter中获取其 []dav.Propstat //fetch a matter's []dav.Propstat
func (this *DavService) PropstatsFromXmlNames(user *User, matter *Matter, xmlNames []xml.Name) []dav.Propstat { func (this *DavService) PropstatsFromXmlNames(user *User, matter *Matter, xmlNames []xml.Name) []dav.Propstat {
propstats := make([]dav.Propstat, 0) propstats := make([]dav.Propstat, 0)
@ -92,7 +90,7 @@ func (this *DavService) PropstatsFromXmlNames(user *User, matter *Matter, xmlNam
var properties []dav.Property var properties []dav.Property
for _, xmlName := range xmlNames { for _, xmlName := range xmlNames {
//TODO: deadprops尚未考虑 //TODO: deadprops not implement yet.
// Otherwise, it must either be a live property or we don't know it. // Otherwise, it must either be a live property or we don't know it.
if liveProp := LivePropMap[xmlName]; liveProp.findFn != nil && (liveProp.dir || !matter.Dir) { if liveProp := LivePropMap[xmlName]; liveProp.findFn != nil && (liveProp.dir || !matter.Dir) {
@ -103,7 +101,7 @@ func (this *DavService) PropstatsFromXmlNames(user *User, matter *Matter, xmlNam
InnerXML: []byte(innerXML), InnerXML: []byte(innerXML),
}) })
} else { } else {
this.logger.Info("%s的%s无法完成", matter.Path, xmlName.Local) this.logger.Info("%s %s cannot finish.", matter.Path, xmlName.Local)
} }
} }
@ -119,7 +117,6 @@ func (this *DavService) PropstatsFromXmlNames(user *User, matter *Matter, xmlNam
} }
//从一个matter中获取所有的propsNames
func (this *DavService) AllPropXmlNames(matter *Matter) []xml.Name { func (this *DavService) AllPropXmlNames(matter *Matter) []xml.Name {
pnames := make([]xml.Name, 0) pnames := make([]xml.Name, 0)
@ -132,7 +129,6 @@ func (this *DavService) AllPropXmlNames(matter *Matter) []xml.Name {
return pnames return pnames
} }
//从一个matter中获取其 []dav.Propstat
func (this *DavService) Propstats(user *User, matter *Matter, propfind *dav.Propfind) []dav.Propstat { func (this *DavService) Propstats(user *User, matter *Matter, propfind *dav.Propfind) []dav.Propstat {
propstats := make([]dav.Propstat, 0) propstats := make([]dav.Propstat, 0)
@ -140,7 +136,7 @@ func (this *DavService) Propstats(user *User, matter *Matter, propfind *dav.Prop
panic(result.BadRequest("TODO: propfind.Propname != nil ")) panic(result.BadRequest("TODO: propfind.Propname != nil "))
} else if propfind.Allprop != nil { } else if propfind.Allprop != nil {
//TODO: 如果include中还有内容那么包含进去。 //TODO: if include other things. add to it.
xmlNames := this.AllPropXmlNames(matter) xmlNames := this.AllPropXmlNames(matter)
propstats = this.PropstatsFromXmlNames(user, matter, xmlNames) propstats = this.PropstatsFromXmlNames(user, matter, xmlNames)
@ -153,48 +149,44 @@ func (this *DavService) Propstats(user *User, matter *Matter, propfind *dav.Prop
} }
//列出文件夹或者目录详情 //list the directory.
func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
fmt.Printf("PROPFIND %s\n", subPath) fmt.Printf("PROPFIND %s\n", subPath)
//获取请求的层数。暂不支持 infinity
depth := this.ParseDepth(request) depth := this.ParseDepth(request)
//读取请求参数。按照用户的参数请求返回内容。
propfind := dav.ReadPropfind(request.Body) propfind := dav.ReadPropfind(request.Body)
//寻找符合条件的matter. //find the matter, if subPath is null, means the root directory.
//如果是空或者/就是请求根目录
matter := this.matterDao.CheckWithRootByPath(subPath, user) matter := this.matterDao.CheckWithRootByPath(subPath, user)
var matters []*Matter var matters []*Matter
if depth == 0 { if depth == 0 {
matters = []*Matter{matter} matters = []*Matter{matter}
} else { } else {
// len(matters) == 0 表示该文件夹下面是空文件夹 // len(matters) == 0 means empty directory
matters = this.matterDao.ListByPuuidAndUserUuid(matter.Uuid, user.Uuid, nil) matters = this.matterDao.ListByPuuidAndUserUuid(matter.Uuid, user.Uuid, nil)
//将当前的matter添加到头部 //add this matter to head.
matters = append([]*Matter{matter}, matters...) matters = append([]*Matter{matter}, matters...)
} }
//准备一个输出结果的Writer //prepare a multiStatusWriter.
multiStatusWriter := &dav.MultiStatusWriter{Writer: writer} multiStatusWriter := &dav.MultiStatusWriter{Writer: writer}
for _, matter := range matters { for _, matter := range matters {
fmt.Printf("处理Matter %s\n", matter.Path) fmt.Printf("handle Matter %s\n", matter.Path)
propstats := this.Propstats(user, matter, propfind) propstats := this.Propstats(user, matter, propfind)
path := fmt.Sprintf("%s%s", WEBDAV_PREFIX, matter.Path) visitPath := fmt.Sprintf("%s%s", WEBDAV_PREFIX, matter.Path)
response := this.makePropstatResponse(path, propstats) response := this.makePropstatResponse(visitPath, propstats)
err := multiStatusWriter.Write(response) err := multiStatusWriter.Write(response)
this.PanicError(err) this.PanicError(err)
} }
//闭合
err := multiStatusWriter.Close() err := multiStatusWriter.Close()
this.PanicError(err) this.PanicError(err)
@ -202,25 +194,25 @@ func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http
} }
//请求文件详情(下载) //handle download
func (this *DavService) HandleGetHeadPost(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleGetHeadPost(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
fmt.Printf("GET %s\n", subPath) fmt.Printf("GET %s\n", subPath)
matter := this.matterDao.CheckWithRootByPath(subPath, user) matter := this.matterDao.CheckWithRootByPath(subPath, user)
//如果是文件夹,相当于是 Propfind //if this is a Directory, it means Propfind
if matter.Dir { if matter.Dir {
this.HandlePropfind(writer, request, user, subPath) this.HandlePropfind(writer, request, user, subPath)
return return
} }
//下载一个文件。 //download a file.
this.matterService.DownloadFile(writer, request, matter.AbsolutePath(), matter.Name, false) this.matterService.DownloadFile(writer, request, matter.AbsolutePath(), matter.Name, false)
} }
//上传文件 //upload a file
func (this *DavService) HandlePut(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandlePut(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
fmt.Printf("PUT %s\n", subPath) fmt.Printf("PUT %s\n", subPath)
@ -228,10 +220,9 @@ func (this *DavService) HandlePut(writer http.ResponseWriter, request *http.Requ
filename := util.GetFilenameOfPath(subPath) filename := util.GetFilenameOfPath(subPath)
dirPath := util.GetDirOfPath(subPath) dirPath := util.GetDirOfPath(subPath)
//寻找符合条件的matter.
dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user) dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user)
//如果存在,那么先删除再说。 //if exist delete it.
srcMatter := this.matterDao.findByUserUuidAndPath(user.Uuid, subPath) srcMatter := this.matterDao.findByUserUuidAndPath(user.Uuid, subPath)
if srcMatter != nil { if srcMatter != nil {
this.matterService.AtomicDelete(request, srcMatter) this.matterService.AtomicDelete(request, srcMatter)
@ -241,18 +232,17 @@ func (this *DavService) HandlePut(writer http.ResponseWriter, request *http.Requ
} }
//删除文件 //delete file
func (this *DavService) HandleDelete(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleDelete(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
fmt.Printf("DELETE %s\n", subPath) fmt.Printf("DELETE %s\n", subPath)
//寻找符合条件的matter.
matter := this.matterDao.CheckWithRootByPath(subPath, user) matter := this.matterDao.CheckWithRootByPath(subPath, user)
this.matterService.AtomicDelete(request, matter) this.matterService.AtomicDelete(request, matter)
} }
//创建文件夹 //crate a directory
func (this *DavService) HandleMkcol(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleMkcol(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
fmt.Printf("MKCOL %s\n", subPath) fmt.Printf("MKCOL %s\n", subPath)
@ -260,19 +250,17 @@ func (this *DavService) HandleMkcol(writer http.ResponseWriter, request *http.Re
thisDirName := util.GetFilenameOfPath(subPath) thisDirName := util.GetFilenameOfPath(subPath)
dirPath := util.GetDirOfPath(subPath) dirPath := util.GetDirOfPath(subPath)
//寻找符合条件的matter.
dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user) dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user)
this.matterService.AtomicCreateDirectory(request, dirMatter, thisDirName, user) this.matterService.AtomicCreateDirectory(request, dirMatter, thisDirName, user)
} }
//跨域请求的OPTIONS询问 //cors options
func (this *DavService) HandleOptions(w http.ResponseWriter, r *http.Request, user *User, subPath string) { func (this *DavService) HandleOptions(w http.ResponseWriter, r *http.Request, user *User, subPath string) {
fmt.Printf("OPTIONS %s\n", subPath) fmt.Printf("OPTIONS %s\n", subPath)
//寻找符合条件的matter.
matter := this.matterDao.CheckWithRootByPath(subPath, user) matter := this.matterDao.CheckWithRootByPath(subPath, user)
allow := "OPTIONS, LOCK, PUT, MKCOL" allow := "OPTIONS, LOCK, PUT, MKCOL"
@ -290,7 +278,7 @@ func (this *DavService) HandleOptions(w http.ResponseWriter, r *http.Request, us
} }
//移动或者复制的准备工作 //prepare for moving or copying
func (this *DavService) prepareMoveCopy( func (this *DavService) prepareMoveCopy(
writer http.ResponseWriter, writer http.ResponseWriter,
request *http.Request, request *http.Request,
@ -302,22 +290,22 @@ func (this *DavService) prepareMoveCopy(
destinationName string, destinationName string,
overwrite bool) { overwrite bool) {
//解析出目标路径。 //parse the destination.
destinationStr := request.Header.Get("Destination") destinationStr := request.Header.Get("Destination")
//解析出Overwrite。 //parse Overwrite。
overwriteStr := request.Header.Get("Overwrite") overwriteStr := request.Header.Get("Overwrite")
//有前缀的目标path //destination path with prefix
var fullDestinationPath string var fullDestinationPath string
//去掉前缀的目标path //destination path without prefix
var destinationPath string var destinationPath string
if destinationStr == "" { if destinationStr == "" {
panic(result.BadRequest("Header Destination cannot be null")) panic(result.BadRequest("Header Destination cannot be null"))
} }
//如果是重命名那么就不是http开头了。 //if rename. not start with http
if strings.HasPrefix(destinationStr, WEBDAV_PREFIX) { if strings.HasPrefix(destinationStr, WEBDAV_PREFIX) {
fullDestinationPath = destinationStr fullDestinationPath = destinationStr
} else { } else {
@ -329,10 +317,10 @@ func (this *DavService) prepareMoveCopy(
fullDestinationPath = destinationUrl.Path fullDestinationPath = destinationUrl.Path
} }
//去除 路径中的相对路径 比如 /a/b/../ => /a/ //clean the relative path. eg. /a/b/../ => /a/
fullDestinationPath = path.Clean(fullDestinationPath) fullDestinationPath = path.Clean(fullDestinationPath)
//去除前缀 //clean the prefix
pattern := fmt.Sprintf(`^%s(.*)$`, WEBDAV_PREFIX) pattern := fmt.Sprintf(`^%s(.*)$`, WEBDAV_PREFIX)
reg := regexp.MustCompile(pattern) reg := regexp.MustCompile(pattern)
strs := reg.FindStringSubmatch(fullDestinationPath) strs := reg.FindStringSubmatch(fullDestinationPath)
@ -351,133 +339,131 @@ func (this *DavService) prepareMoveCopy(
overwrite = true overwrite = true
} }
//如果前后一致,那么相当于没有改变 //if not change return.
if destinationPath == subPath { if destinationPath == subPath {
return return
} }
//源matter. //source matter
//寻找符合条件的matter.
srcMatter = this.matterDao.CheckWithRootByPath(subPath, user) srcMatter = this.matterDao.CheckWithRootByPath(subPath, user)
//如果是空或者/就是请求根目录 //if source matter is root.
if srcMatter.Uuid == MATTER_ROOT { if srcMatter.Uuid == MATTER_ROOT {
panic(result.BadRequest("you cannot move the root directory")) panic(result.BadRequest("you cannot move the root directory"))
} }
//寻找目标文件夹matter
destDirMatter = this.matterDao.CheckWithRootByPath(destinationDirPath, user) destDirMatter = this.matterDao.CheckWithRootByPath(destinationDirPath, user)
return srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite return srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite
} }
//移动或者重命名 //move or rename.
func (this *DavService) HandleMove(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleMove(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
fmt.Printf("MOVE %s\n", subPath) fmt.Printf("MOVE %s\n", subPath)
srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath) srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath)
//移动到新目录中去。 //move to the new directory
if destinationDirPath == srcDirPath { if destinationDirPath == srcDirPath {
//文件夹没变化,相当于重命名。 //if destination path not change. it means rename.
this.matterService.AtomicRename(request, srcMatter, destinationName, user) this.matterService.AtomicRename(request, srcMatter, destinationName, user)
} else { } else {
this.matterService.AtomicMove(request, srcMatter, destDirMatter, overwrite) this.matterService.AtomicMove(request, srcMatter, destDirMatter, overwrite)
} }
this.logger.Info("完成移动 %s => %s", subPath, destDirMatter.Path) this.logger.Info("finish moving %s => %s", subPath, destDirMatter.Path)
} }
//复制文件/文件夹 //copy file/directory
func (this *DavService) HandleCopy(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleCopy(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
fmt.Printf("COPY %s\n", subPath) fmt.Printf("COPY %s\n", subPath)
srcMatter, destDirMatter, _, _, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath) srcMatter, destDirMatter, _, _, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath)
//复制到新目录中去。 //copy to the new directory
this.matterService.AtomicCopy(request, srcMatter, destDirMatter, destinationName, overwrite) this.matterService.AtomicCopy(request, srcMatter, destDirMatter, destinationName, overwrite)
this.logger.Info("完成复制 %s => %s", subPath, destDirMatter.Path) this.logger.Info("finish copying %s => %s", subPath, destDirMatter.Path)
} }
//加锁 //lock.
func (this *DavService) HandleLock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleLock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
panic(result.BadRequest("not support LOCK yet.")) panic(result.BadRequest("not support LOCK yet."))
} }
//解锁 //unlock
func (this *DavService) HandleUnlock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleUnlock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
panic(result.BadRequest("not support UNLOCK yet.")) panic(result.BadRequest("not support UNLOCK yet."))
} }
//修改文件属性 //change the file's property
func (this *DavService) HandleProppatch(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleProppatch(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
panic(result.BadRequest("not support PROPPATCH yet.")) panic(result.BadRequest("not support PROPPATCH yet."))
} }
//处理所有的请求 //hanle all the request.
func (this *DavService) HandleDav(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { func (this *DavService) HandleDav(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
method := request.Method method := request.Method
if method == "OPTIONS" { if method == "OPTIONS" {
//跨域问询 //cors option
this.HandleOptions(writer, request, user, subPath) this.HandleOptions(writer, request, user, subPath)
} else if method == "GET" || method == "HEAD" || method == "POST" { } else if method == "GET" || method == "HEAD" || method == "POST" {
//请求文件详情(下载) //get the detail of file. download
this.HandleGetHeadPost(writer, request, user, subPath) this.HandleGetHeadPost(writer, request, user, subPath)
} else if method == "DELETE" { } else if method == "DELETE" {
//删除文件 //delete file
this.HandleDelete(writer, request, user, subPath) this.HandleDelete(writer, request, user, subPath)
} else if method == "PUT" { } else if method == "PUT" {
//上传文件 //upload file
this.HandlePut(writer, request, user, subPath) this.HandlePut(writer, request, user, subPath)
} else if method == "MKCOL" { } else if method == "MKCOL" {
//创建文件夹 //crate directory
this.HandleMkcol(writer, request, user, subPath) this.HandleMkcol(writer, request, user, subPath)
} else if method == "COPY" { } else if method == "COPY" {
//复制文件/文件夹 //copy file/directory
this.HandleCopy(writer, request, user, subPath) this.HandleCopy(writer, request, user, subPath)
} else if method == "MOVE" { } else if method == "MOVE" {
//移动(重命名)文件/文件夹 //move/rename a file or directory
this.HandleMove(writer, request, user, subPath) this.HandleMove(writer, request, user, subPath)
} else if method == "LOCK" { } else if method == "LOCK" {
//加锁 //lock
this.HandleLock(writer, request, user, subPath) this.HandleLock(writer, request, user, subPath)
} else if method == "UNLOCK" { } else if method == "UNLOCK" {
//释放锁 //unlock
this.HandleUnlock(writer, request, user, subPath) this.HandleUnlock(writer, request, user, subPath)
} else if method == "PROPFIND" { } else if method == "PROPFIND" {
//列出文件夹或者目录详情 //list a directory
this.HandlePropfind(writer, request, user, subPath) this.HandlePropfind(writer, request, user, subPath)
} else if method == "PROPPATCH" { } else if method == "PROPPATCH" {
//修改文件属性 //change file's property.
this.HandleProppatch(writer, request, user, subPath) this.HandleProppatch(writer, request, user, subPath)
} else { } else {

View File

@ -2,6 +2,7 @@ package rest
import ( import (
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/nu7hatch/gouuid" "github.com/nu7hatch/gouuid"
"time" "time"
) )
@ -10,31 +11,30 @@ type DownloadTokenDao struct {
BaseDao BaseDao
} }
//按照Id查询 //find by uuid. if not found return nil.
func (this *DownloadTokenDao) FindByUuid(uuid string) *DownloadToken { func (this *DownloadTokenDao) FindByUuid(uuid string) *DownloadToken {
var entity = &DownloadToken{}
// Read db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(entity)
var downloadToken = &DownloadToken{}
db := core.CONTEXT.GetDB().Where(&DownloadToken{Base: Base{Uuid: uuid}}).First(downloadToken)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else {
panic(db.Error)
} }
return downloadToken }
return entity
} }
//按照Id查询 //find by uuid. if not found panic NotFound error
func (this *DownloadTokenDao) CheckByUuid(uuid string) *DownloadToken { func (this *DownloadTokenDao) CheckByUuid(uuid string) *DownloadToken {
entity := this.FindByUuid(uuid)
// Read if entity == nil {
var downloadToken = &DownloadToken{} panic(result.NotFound("not found record with uuid = %s", uuid))
db := core.CONTEXT.GetDB().Where(&DownloadToken{Base: Base{Uuid: uuid}}).First(downloadToken) }
this.PanicError(db.Error) return entity
return downloadToken
} }
//创建一个session并且持久化到数据库中。
func (this *DownloadTokenDao) Create(downloadToken *DownloadToken) *DownloadToken { func (this *DownloadTokenDao) Create(downloadToken *DownloadToken) *DownloadToken {
timeUUID, _ := uuid.NewV4() timeUUID, _ := uuid.NewV4()
@ -49,7 +49,6 @@ func (this *DownloadTokenDao) Create(downloadToken *DownloadToken) *DownloadToke
return downloadToken return downloadToken
} }
//修改一个downloadToken
func (this *DownloadTokenDao) Save(downloadToken *DownloadToken) *DownloadToken { func (this *DownloadTokenDao) Save(downloadToken *DownloadToken) *DownloadToken {
downloadToken.UpdateTime = time.Now() downloadToken.UpdateTime = time.Now()
@ -59,9 +58,8 @@ func (this *DownloadTokenDao) Save(downloadToken *DownloadToken) *DownloadToken
return downloadToken return downloadToken
} }
//执行清理操作
func (this *DownloadTokenDao) Cleanup() { func (this *DownloadTokenDao) Cleanup() {
this.logger.Info("[DownloadTokenDao]执行清理清除数据库中所有DownloadToken记录。") this.logger.Info("[DownloadTokenDao] clean up. Delete all DownloadToken")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(DownloadToken{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(DownloadToken{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -11,11 +11,9 @@ type FootprintController struct {
footprintService *FootprintService footprintService *FootprintService
} }
//初始化方法
func (this *FootprintController) Init() { func (this *FootprintController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.footprintDao) b := core.CONTEXT.GetBean(this.footprintDao)
if b, ok := b.(*FootprintDao); ok { if b, ok := b.(*FootprintDao); ok {
this.footprintDao = b this.footprintDao = b
@ -28,7 +26,6 @@ func (this *FootprintController) Init() {
} }
//注册自己的路由。
func (this *FootprintController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *FootprintController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))

View File

@ -3,6 +3,7 @@ package rest
import ( import (
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/builder" "github.com/eyebluecn/tank/code/tool/builder"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/nu7hatch/gouuid" "github.com/nu7hatch/gouuid"
@ -13,31 +14,29 @@ type FootprintDao struct {
BaseDao BaseDao
} }
//按照Id查询文件 //find by uuid. if not found return nil.
func (this *FootprintDao) FindByUuid(uuid string) *Footprint { func (this *FootprintDao) FindByUuid(uuid string) *Footprint {
var entity = &Footprint{}
// Read db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(entity)
var footprint Footprint
db := core.CONTEXT.GetDB().Where(&Footprint{Base: Base{Uuid: uuid}}).First(&footprint)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else {
panic(db.Error)
} }
return &footprint }
return entity
} }
//按照Id查询文件 //find by uuid. if not found panic NotFound error
func (this *FootprintDao) CheckByUuid(uuid string) *Footprint { func (this *FootprintDao) CheckByUuid(uuid string) *Footprint {
entity := this.FindByUuid(uuid)
// Read if entity == nil {
var footprint Footprint panic(result.NotFound("not found record with uuid = %s", uuid))
db := core.CONTEXT.GetDB().Where(&Footprint{Base: Base{Uuid: uuid}}).First(&footprint) }
this.PanicError(db.Error) return entity
return &footprint
} }
//按分页条件获取分页
func (this *FootprintDao) Page(page int, pageSize int, userUuid string, sortArray []builder.OrderPair) *Pager { func (this *FootprintDao) Page(page int, pageSize int, userUuid string, sortArray []builder.OrderPair) *Pager {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
@ -61,7 +60,6 @@ func (this *FootprintDao) Page(page int, pageSize int, userUuid string, sortArra
return pager return pager
} }
//创建
func (this *FootprintDao) Create(footprint *Footprint) *Footprint { func (this *FootprintDao) Create(footprint *Footprint) *Footprint {
timeUUID, _ := uuid.NewV4() timeUUID, _ := uuid.NewV4()
@ -75,7 +73,6 @@ func (this *FootprintDao) Create(footprint *Footprint) *Footprint {
return footprint return footprint
} }
//修改一条记录
func (this *FootprintDao) Save(footprint *Footprint) *Footprint { func (this *FootprintDao) Save(footprint *Footprint) *Footprint {
footprint.UpdateTime = time.Now() footprint.UpdateTime = time.Now()
@ -85,14 +82,12 @@ func (this *FootprintDao) Save(footprint *Footprint) *Footprint {
return footprint return footprint
} }
//删除一条记录
func (this *FootprintDao) Delete(footprint *Footprint) { func (this *FootprintDao) Delete(footprint *Footprint) {
db := core.CONTEXT.GetDB().Delete(&footprint) db := core.CONTEXT.GetDB().Delete(&footprint)
this.PanicError(db.Error) this.PanicError(db.Error)
} }
//获取一段时间中,总的数量
func (this *FootprintDao) CountBetweenTime(startTime time.Time, endTime time.Time) int64 { func (this *FootprintDao) CountBetweenTime(startTime time.Time, endTime time.Time) int64 {
var count int64 var count int64
db := core.CONTEXT.GetDB().Model(&Footprint{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Count(&count) db := core.CONTEXT.GetDB().Model(&Footprint{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Count(&count)
@ -100,35 +95,51 @@ func (this *FootprintDao) CountBetweenTime(startTime time.Time, endTime time.Tim
return count return count
} }
//获取一段时间中UV的数量
func (this *FootprintDao) UvBetweenTime(startTime time.Time, endTime time.Time) int64 { func (this *FootprintDao) UvBetweenTime(startTime time.Time, endTime time.Time) int64 {
var wp = &builder.WherePair{Query: "create_time >= ? AND create_time <= ?", Args: []interface{}{startTime, endTime}}
var count int64 var count int64
db := core.CONTEXT.GetDB().Model(&Footprint{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("COUNT(DISTINCT(ip))") db := core.CONTEXT.GetDB().Model(&Footprint{}).Where(wp.Query, wp.Args...).Count(&count)
if count == 0 {
return 0
}
db = core.CONTEXT.GetDB().Model(&Footprint{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("COUNT(DISTINCT(ip))")
this.PanicError(db.Error) this.PanicError(db.Error)
row := db.Row() row := db.Row()
row.Scan(&count) err := row.Scan(&count)
this.PanicError(err)
return count return count
} }
//获取一段时间中平均耗时
func (this *FootprintDao) AvgCostBetweenTime(startTime time.Time, endTime time.Time) int64 { func (this *FootprintDao) AvgCostBetweenTime(startTime time.Time, endTime time.Time) int64 {
var wp = &builder.WherePair{Query: "create_time >= ? AND create_time <= ?", Args: []interface{}{startTime, endTime}}
var count int64
db := core.CONTEXT.GetDB().Model(&Footprint{}).Where(wp.Query, wp.Args...).Count(&count)
if count == 0 {
return 0
}
var cost float64 var cost float64
db := core.CONTEXT.GetDB().Model(&Footprint{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("AVG(cost)") db = core.CONTEXT.GetDB().Model(&Footprint{}).Where(wp.Query, wp.Args...).Select("AVG(cost)")
this.PanicError(db.Error) this.PanicError(db.Error)
row := db.Row() row := db.Row()
row.Scan(&cost) err := row.Scan(&cost)
this.PanicError(err)
return int64(cost) return int64(cost)
} }
//删除某个时刻之前的记录
func (this *FootprintDao) DeleteByCreateTimeBefore(createTime time.Time) { func (this *FootprintDao) DeleteByCreateTimeBefore(createTime time.Time) {
db := core.CONTEXT.GetDB().Where("create_time < ?", createTime).Delete(Footprint{}) db := core.CONTEXT.GetDB().Where("create_time < ?", createTime).Delete(Footprint{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }
//执行清理操作 //System cleanup.
func (this *FootprintDao) Cleanup() { func (this *FootprintDao) Cleanup() {
this.logger.Info("[FootprintDao]执行清理清除数据库中所有Footprint记录。") this.logger.Info("[FootprintDao][DownloadTokenDao] clean up. Delete all Footprint")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Footprint{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Footprint{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -3,7 +3,7 @@ package rest
import "github.com/eyebluecn/tank/code/core" import "github.com/eyebluecn/tank/code/core"
/** /**
* 系统的所有访问记录均记录在此 * visit record.
*/ */
type Footprint struct { type Footprint struct {
Base Base

View File

@ -17,11 +17,9 @@ type FootprintService struct {
userDao *UserDao userDao *UserDao
} }
//初始化方法
func (this *FootprintService) Init() { func (this *FootprintService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.footprintDao) b := core.CONTEXT.GetBean(this.footprintDao)
if b, ok := b.(*FootprintDao); ok { if b, ok := b.(*FootprintDao); ok {
this.footprintDao = b this.footprintDao = b
@ -34,7 +32,6 @@ func (this *FootprintService) Init() {
} }
//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。
func (this *FootprintService) Detail(uuid string) *Footprint { func (this *FootprintService) Detail(uuid string) *Footprint {
footprint := this.footprintDao.CheckByUuid(uuid) footprint := this.footprintDao.CheckByUuid(uuid)
@ -42,17 +39,17 @@ func (this *FootprintService) Detail(uuid string) *Footprint {
return footprint return footprint
} }
//记录访问记录 //log a request.
func (this *FootprintService) Trace(request *http.Request, duration time.Duration, success bool) { func (this *FootprintService) Trace(request *http.Request, duration time.Duration, success bool) {
params := make(map[string][]string) params := make(map[string][]string)
//POST请求参数 //POST params
values := request.PostForm values := request.PostForm
for key, val := range values { for key, val := range values {
params[key] = val params[key] = val
} }
//GET请求参数 //GET params
values1 := request.URL.Query() values1 := request.URL.Query()
for key, val := range values1 { for key, val := range values1 {
params[key] = val params[key] = val
@ -65,14 +62,12 @@ func (this *FootprintService) Trace(request *http.Request, duration time.Duratio
} }
} }
//用json的方式输出返回值。
paramsString := "{}" paramsString := "{}"
paramsData, err := json.Marshal(params) paramsData, err := json.Marshal(params)
if err == nil { if err == nil {
paramsString = string(paramsData) paramsString = string(paramsData)
} }
//将文件信息存入数据库中。
footprint := &Footprint{ footprint := &Footprint{
Ip: util.GetIpAddress(request), Ip: util.GetIpAddress(request),
Host: request.Host, Host: request.Host,
@ -82,7 +77,7 @@ func (this *FootprintService) Trace(request *http.Request, duration time.Duratio
Success: success, Success: success,
} }
//有可能DB尚且没有配置 直接打印出内容,并且退出 //if db not config just print content.
if core.CONFIG.Installed() { if core.CONFIG.Installed() {
user := this.findUser(request) user := this.findUser(request)
userUuid := "" userUuid := ""
@ -93,35 +88,30 @@ func (this *FootprintService) Trace(request *http.Request, duration time.Duratio
footprint = this.footprintDao.Create(footprint) footprint = this.footprintDao.Create(footprint)
} }
//用json的方式输出返回值。
this.logger.Info("Ip:%s Cost:%d Uri:%s Params:%s", footprint.Ip, int64(duration/time.Millisecond), footprint.Uri, paramsString) this.logger.Info("Ip:%s Cost:%d Uri:%s Params:%s", footprint.Ip, int64(duration/time.Millisecond), footprint.Uri, paramsString)
} }
//系统启动,数据库配置完毕后会调用该方法
func (this *FootprintService) Bootstrap() { func (this *FootprintService) Bootstrap() {
//每日00:10 删除8日之前的访问数据 this.logger.Info("[cron job] Every day 00:10 delete Footprint data 8 days ago.")
expression := "0 10 0 * * ?" expression := "0 10 0 * * ?"
cronJob := cron.New() cronJob := cron.New()
err := cronJob.AddFunc(expression, this.cleanOldData) err := cronJob.AddFunc(expression, this.cleanOldData)
core.PanicError(err) core.PanicError(err)
cronJob.Start() cronJob.Start()
this.logger.Info("[cron job] 每日00:10 删除8日之前的访问数据")
//立即执行一次数据清洗任务
go core.RunWithRecovery(this.cleanOldData) go core.RunWithRecovery(this.cleanOldData)
} }
//定期删除8日前的数据。
func (this *FootprintService) cleanOldData() { func (this *FootprintService) cleanOldData() {
day8Ago := time.Now() day8Ago := time.Now()
day8Ago = day8Ago.AddDate(0, 0, -8) day8Ago = day8Ago.AddDate(0, 0, -8)
day8Ago = util.FirstSecondOfDay(day8Ago) day8Ago = util.FirstSecondOfDay(day8Ago)
this.logger.Info("删除%s之前的访问数据", util.ConvertTimeToDateTimeString(day8Ago)) this.logger.Info("Delete footprint data before %s", util.ConvertTimeToDateTimeString(day8Ago))
this.footprintDao.DeleteByCreateTimeBefore(day8Ago) this.footprintDao.DeleteByCreateTimeBefore(day8Ago)
} }

View File

@ -15,11 +15,9 @@ type ImageCacheController struct {
imageCacheService *ImageCacheService imageCacheService *ImageCacheService
} }
//初始化方法
func (this *ImageCacheController) Init() { func (this *ImageCacheController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.imageCacheDao) b := core.CONTEXT.GetBean(this.imageCacheDao)
if b, ok := b.(*ImageCacheDao); ok { if b, ok := b.(*ImageCacheDao); ok {
this.imageCacheDao = b this.imageCacheDao = b
@ -32,12 +30,10 @@ func (this *ImageCacheController) Init() {
} }
//注册自己的路由。
func (this *ImageCacheController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *ImageCacheController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
//每个Controller需要主动注册自己的路由。
routeMap["/api/image/cache/delete"] = this.Wrap(this.Delete, USER_ROLE_USER) routeMap["/api/image/cache/delete"] = this.Wrap(this.Delete, USER_ROLE_USER)
routeMap["/api/image/cache/delete/batch"] = this.Wrap(this.DeleteBatch, USER_ROLE_USER) routeMap["/api/image/cache/delete/batch"] = this.Wrap(this.DeleteBatch, USER_ROLE_USER)
routeMap["/api/image/cache/detail"] = this.Wrap(this.Detail, USER_ROLE_USER) routeMap["/api/image/cache/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
@ -46,7 +42,6 @@ func (this *ImageCacheController) RegisterRoutes() map[string]func(writer http.R
return routeMap return routeMap
} }
//查看某个图片缓存的详情。
func (this *ImageCacheController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *ImageCacheController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuid := request.FormValue("uuid") uuid := request.FormValue("uuid")
@ -56,7 +51,6 @@ func (this *ImageCacheController) Detail(writer http.ResponseWriter, request *ht
imageCache := this.imageCacheService.Detail(uuid) imageCache := this.imageCacheService.Detail(uuid)
//验证当前之人是否有权限查看这么详细。
user := this.checkUser(request) user := this.checkUser(request)
if imageCache.UserUuid != user.Uuid { if imageCache.UserUuid != user.Uuid {
panic(result.UNAUTHORIZED) panic(result.UNAUTHORIZED)
@ -66,9 +60,8 @@ func (this *ImageCacheController) Detail(writer http.ResponseWriter, request *ht
} }
//按照分页的方式获取某个图片缓存夹下图片缓存和子图片缓存夹的列表,通常情况下只有一页。
func (this *ImageCacheController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *ImageCacheController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult {
//如果是根目录那么就传入root.
pageStr := request.FormValue("page") pageStr := request.FormValue("page")
pageSizeStr := request.FormValue("pageSize") pageSizeStr := request.FormValue("pageSize")
orderCreateTime := request.FormValue("orderCreateTime") orderCreateTime := request.FormValue("orderCreateTime")
@ -120,7 +113,6 @@ func (this *ImageCacheController) Page(writer http.ResponseWriter, request *http
return this.Success(pager) return this.Success(pager)
} }
//删除一个图片缓存
func (this *ImageCacheController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *ImageCacheController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuid := request.FormValue("uuid") uuid := request.FormValue("uuid")
@ -130,19 +122,16 @@ func (this *ImageCacheController) Delete(writer http.ResponseWriter, request *ht
imageCache := this.imageCacheDao.FindByUuid(uuid) imageCache := this.imageCacheDao.FindByUuid(uuid)
//判断图片缓存的所属人是否正确
user := this.checkUser(request) user := this.checkUser(request)
if imageCache.UserUuid != user.Uuid { if imageCache.UserUuid != user.Uuid {
panic(result.UNAUTHORIZED)
panic(result.Unauthorized("没有权限"))
} }
this.imageCacheDao.Delete(imageCache) this.imageCacheDao.Delete(imageCache)
return this.Success("删除成功!") return this.Success("OK")
} }
//删除一系列图片缓存。
func (this *ImageCacheController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *ImageCacheController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuids := request.FormValue("uuids") uuids := request.FormValue("uuids")
@ -156,15 +145,14 @@ func (this *ImageCacheController) DeleteBatch(writer http.ResponseWriter, reques
imageCache := this.imageCacheDao.FindByUuid(uuid) imageCache := this.imageCacheDao.FindByUuid(uuid)
//判断图片缓存的所属人是否正确
user := this.checkUser(request) user := this.checkUser(request)
if imageCache.UserUuid != user.Uuid { if imageCache.UserUuid != user.Uuid {
panic(result.Unauthorized("没有权限")) panic(result.UNAUTHORIZED)
} }
this.imageCacheDao.Delete(imageCache) this.imageCacheDao.Delete(imageCache)
} }
return this.Success("删除成功!") return this.Success("OK")
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/builder" "github.com/eyebluecn/tank/code/tool/builder"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util" "github.com/eyebluecn/tank/code/tool/util"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/nu7hatch/gouuid" "github.com/nu7hatch/gouuid"
@ -16,31 +17,30 @@ type ImageCacheDao struct {
BaseDao BaseDao
} }
//按照Id查询文件 //find by uuid. if not found return nil.
func (this *ImageCacheDao) FindByUuid(uuid string) *ImageCache { func (this *ImageCacheDao) FindByUuid(uuid string) *ImageCache {
var entity = &ImageCache{}
// Read db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(entity)
var imageCache ImageCache
db := core.CONTEXT.GetDB().Where(&ImageCache{Base: Base{Uuid: uuid}}).First(&imageCache)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else {
panic(db.Error)
} }
return &imageCache }
return entity
} }
//按照Id查询文件 //find by uuid. if not found panic NotFound error
func (this *ImageCacheDao) CheckByUuid(uuid string) *ImageCache { func (this *ImageCacheDao) CheckByUuid(uuid string) *ImageCache {
entity := this.FindByUuid(uuid)
// Read if entity == nil {
var imageCache ImageCache panic(result.NotFound("not found record with uuid = %s", uuid))
db := core.CONTEXT.GetDB().Where(&ImageCache{Base: Base{Uuid: uuid}}).First(&imageCache) }
this.PanicError(db.Error) return entity
return &imageCache
} }
//按照名字查询文件夹
func (this *ImageCacheDao) FindByMatterUuidAndMode(matterUuid string, mode string) *ImageCache { func (this *ImageCacheDao) FindByMatterUuidAndMode(matterUuid string, mode string) *ImageCache {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
@ -63,7 +63,6 @@ func (this *ImageCacheDao) FindByMatterUuidAndMode(matterUuid string, mode strin
return imageCache return imageCache
} }
//按照id和userUuid来查找。找不到抛异常。
func (this *ImageCacheDao) CheckByUuidAndUserUuid(uuid string, userUuid string) *ImageCache { func (this *ImageCacheDao) CheckByUuidAndUserUuid(uuid string, userUuid string) *ImageCache {
// Read // Read
@ -75,8 +74,7 @@ func (this *ImageCacheDao) CheckByUuidAndUserUuid(uuid string, userUuid string)
} }
//获取某个用户的某个文件夹下的某个名字的文件(或文件夹)列表 func (this *ImageCacheDao) FindByUserUuidAndPuuidAndDirAndName(userUuid string) []*ImageCache {
func (this *ImageCacheDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string) []*ImageCache {
var imageCaches []*ImageCache var imageCaches []*ImageCache
@ -88,7 +86,6 @@ func (this *ImageCacheDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string)
return imageCaches return imageCaches
} }
//获取某个文件夹下所有的文件和子文件
func (this *ImageCacheDao) Page(page int, pageSize int, userUuid string, matterUuid string, sortArray []builder.OrderPair) *Pager { func (this *ImageCacheDao) Page(page int, pageSize int, userUuid string, matterUuid string, sortArray []builder.OrderPair) *Pager {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
@ -116,7 +113,6 @@ func (this *ImageCacheDao) Page(page int, pageSize int, userUuid string, matterU
return pager return pager
} }
//创建
func (this *ImageCacheDao) Create(imageCache *ImageCache) *ImageCache { func (this *ImageCacheDao) Create(imageCache *ImageCache) *ImageCache {
timeUUID, _ := uuid.NewV4() timeUUID, _ := uuid.NewV4()
@ -130,7 +126,6 @@ func (this *ImageCacheDao) Create(imageCache *ImageCache) *ImageCache {
return imageCache return imageCache
} }
//修改一个文件
func (this *ImageCacheDao) Save(imageCache *ImageCache) *ImageCache { func (this *ImageCacheDao) Save(imageCache *ImageCache) *ImageCache {
imageCache.UpdateTime = time.Now() imageCache.UpdateTime = time.Now()
@ -140,25 +135,24 @@ func (this *ImageCacheDao) Save(imageCache *ImageCache) *ImageCache {
return imageCache return imageCache
} }
//删除一个文件包括文件夹
func (this *ImageCacheDao) deleteFileAndDir(imageCache *ImageCache) { func (this *ImageCacheDao) deleteFileAndDir(imageCache *ImageCache) {
filePath := GetUserCacheRootDir(imageCache.Username) + imageCache.Path filePath := GetUserCacheRootDir(imageCache.Username) + imageCache.Path
dirPath := filepath.Dir(filePath) dirPath := filepath.Dir(filePath)
//删除文件 //delete file from disk.
err := os.Remove(filePath) err := os.Remove(filePath)
if err != nil { if err != nil {
this.logger.Error(fmt.Sprintf("删除磁盘上的文件%s出错 %s", filePath, err.Error())) this.logger.Error(fmt.Sprintf("error while deleting %s from disk %s", filePath, err.Error()))
} }
//如果这一层文件夹是空的,那么删除文件夹本身。 //if this level is empty. Delete the directory
util.DeleteEmptyDirRecursive(dirPath) util.DeleteEmptyDirRecursive(dirPath)
} }
//删除一个文件,数据库中删除,物理磁盘上删除。 //delete a file from db and disk.
func (this *ImageCacheDao) Delete(imageCache *ImageCache) { func (this *ImageCacheDao) Delete(imageCache *ImageCache) {
db := core.CONTEXT.GetDB().Delete(&imageCache) db := core.CONTEXT.GetDB().Delete(&imageCache)
@ -168,42 +162,50 @@ func (this *ImageCacheDao) Delete(imageCache *ImageCache) {
} }
//删除一个matter对应的所有缓存 //delete all the cache of a matter.
func (this *ImageCacheDao) DeleteByMatterUuid(matterUuid string) { func (this *ImageCacheDao) DeleteByMatterUuid(matterUuid string) {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
wp = wp.And(&builder.WherePair{Query: "matter_uuid = ?", Args: []interface{}{matterUuid}}) wp = wp.And(&builder.WherePair{Query: "matter_uuid = ?", Args: []interface{}{matterUuid}})
//查询出即将删除的图片缓存
var imageCaches []*ImageCache var imageCaches []*ImageCache
db := core.CONTEXT.GetDB().Where(wp.Query, wp.Args).Find(&imageCaches) db := core.CONTEXT.GetDB().Where(wp.Query, wp.Args).Find(&imageCaches)
this.PanicError(db.Error) this.PanicError(db.Error)
//删除文件记录 //delete from db.
db = core.CONTEXT.GetDB().Where(wp.Query, wp.Args).Delete(ImageCache{}) db = core.CONTEXT.GetDB().Where(wp.Query, wp.Args).Delete(ImageCache{})
this.PanicError(db.Error) this.PanicError(db.Error)
//删除文件实体 //delete from disk.
for _, imageCache := range imageCaches { for _, imageCache := range imageCaches {
this.deleteFileAndDir(imageCache) this.deleteFileAndDir(imageCache)
} }
} }
//获取一段时间中文件总大小
func (this *ImageCacheDao) SizeBetweenTime(startTime time.Time, endTime time.Time) int64 { func (this *ImageCacheDao) SizeBetweenTime(startTime time.Time, endTime time.Time) int64 {
var wp = &builder.WherePair{Query: "create_time >= ? AND create_time <= ?", Args: []interface{}{startTime, endTime}}
var count int64
db := core.CONTEXT.GetDB().Model(&ImageCache{}).Where(wp.Query, wp.Args...).Count(&count)
if count == 0 {
return 0
}
var size int64 var size int64
db := core.CONTEXT.GetDB().Model(&ImageCache{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("SUM(size)") db = core.CONTEXT.GetDB().Model(&ImageCache{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("SUM(size)")
this.PanicError(db.Error) this.PanicError(db.Error)
row := db.Row() row := db.Row()
row.Scan(&size) err := row.Scan(&size)
this.PanicError(err)
return size return size
} }
//执行清理操作 //System cleanup.
func (this *ImageCacheDao) Cleanup() { func (this *ImageCacheDao) Cleanup() {
this.logger.Info("[ImageCacheDao]执行清理清除数据库中所有ImageCache记录。") this.logger.Info("[ImageCacheDao]clean up. Delete all ImageCache ")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(ImageCache{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(ImageCache{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -5,7 +5,7 @@ import (
) )
/** /**
* 图片缓存对于那些处理过的图片统一管理在这里 * image cache.
*/ */
type ImageCache struct { type ImageCache struct {
Base Base
@ -26,7 +26,7 @@ func (this *ImageCache) TableName() string {
return core.TABLE_PREFIX + "image_cache" return core.TABLE_PREFIX + "image_cache"
} }
// 获取该ImageCache的绝对路径。path代表的是相对路径。 // get the absolute path. path in db means relative path.
func (this *ImageCache) AbsolutePath() string { func (this *ImageCache) AbsolutePath() string {
return GetUserCacheRootDir(this.Username) + this.Path return GetUserCacheRootDir(this.Username) + this.Path
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util" "github.com/eyebluecn/tank/code/tool/util"
"image" "image"
"net/http" "net/http"
@ -21,11 +22,9 @@ type ImageCacheService struct {
matterDao *MatterDao matterDao *MatterDao
} }
//初始化方法
func (this *ImageCacheService) Init() { func (this *ImageCacheService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.imageCacheDao) b := core.CONTEXT.GetBean(this.imageCacheDao)
if b, ok := b.(*ImageCacheDao); ok { if b, ok := b.(*ImageCacheDao); ok {
this.imageCacheDao = b this.imageCacheDao = b
@ -43,7 +42,6 @@ func (this *ImageCacheService) Init() {
} }
//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。
func (this *ImageCacheService) Detail(uuid string) *ImageCache { func (this *ImageCacheService) Detail(uuid string) *ImageCache {
imageCache := this.imageCacheDao.CheckByUuid(uuid) imageCache := this.imageCacheDao.CheckByUuid(uuid)
@ -51,52 +49,23 @@ func (this *ImageCacheService) Detail(uuid string) *ImageCache {
return imageCache return imageCache
} }
//获取预处理时必要的参数 // prepare the resize parameters.
func (this *ImageCacheService) ResizeParams(request *http.Request) (needProcess bool, resizeMode string, resizeWidth int, resizeHeight int) { func (this *ImageCacheService) ResizeParams(request *http.Request) (needProcess bool, resizeMode string, resizeWidth int, resizeHeight int) {
var err error var err error
//1.0 模式准备逐步废弃掉 if request.FormValue("ir") != "" {
if request.FormValue("imageProcess") == "resize" { //mode_w_h if w or h equal means not required.
//老模式使用 imageResizeM,imageResizeW,imageResizeH
imageResizeM := request.FormValue("imageResizeM")
if imageResizeM == "" {
imageResizeM = "fit"
} else if imageResizeM != "fit" && imageResizeM != "fill" && imageResizeM != "fixed" {
panic("imageResizeM参数错误")
}
imageResizeWStr := request.FormValue("imageResizeW")
var imageResizeW int
if imageResizeWStr != "" {
imageResizeW, err = strconv.Atoi(imageResizeWStr)
this.PanicError(err)
if imageResizeW < 1 || imageResizeW > 4096 {
panic("缩放尺寸不能超过4096")
}
}
imageResizeHStr := request.FormValue("imageResizeH")
var imageResizeH int
if imageResizeHStr != "" {
imageResizeH, err = strconv.Atoi(imageResizeHStr)
this.PanicError(err)
if imageResizeH < 1 || imageResizeH > 4096 {
panic("缩放尺寸不能超过4096")
}
}
return true, imageResizeM, imageResizeW, imageResizeH
} else if request.FormValue("ir") != "" {
//新模式使用 mode_w_h 如果w或者h为0表示这项值不设置
imageResizeStr := request.FormValue("ir") imageResizeStr := request.FormValue("ir")
arr := strings.Split(imageResizeStr, "_") arr := strings.Split(imageResizeStr, "_")
if len(arr) != 3 { if len(arr) != 3 {
panic("参数不符合规范格式要求为mode_w_h") panic(result.BadRequest("param error. the format is mode_w_h"))
} }
imageResizeM := arr[0] imageResizeM := arr[0]
if imageResizeM == "" { if imageResizeM == "" {
imageResizeM = "fit" imageResizeM = "fit"
} else if imageResizeM != "fit" && imageResizeM != "fill" && imageResizeM != "fixed" { } else if imageResizeM != "fit" && imageResizeM != "fill" && imageResizeM != "fixed" {
panic("imageResizeM参数错误") panic(result.BadRequest("mode can only be fit/fill/fixed"))
} }
imageResizeWStr := arr[1] imageResizeWStr := arr[1]
var imageResizeW int var imageResizeW int
@ -104,7 +73,7 @@ func (this *ImageCacheService) ResizeParams(request *http.Request) (needProcess
imageResizeW, err = strconv.Atoi(imageResizeWStr) imageResizeW, err = strconv.Atoi(imageResizeWStr)
this.PanicError(err) this.PanicError(err)
if imageResizeW < 0 || imageResizeW > 4096 { if imageResizeW < 0 || imageResizeW > 4096 {
panic("缩放尺寸不能超过4096") panic(result.BadRequest("zoom size cannot exceed 4096"))
} }
} }
imageResizeHStr := arr[2] imageResizeHStr := arr[2]
@ -113,7 +82,7 @@ func (this *ImageCacheService) ResizeParams(request *http.Request) (needProcess
imageResizeH, err = strconv.Atoi(imageResizeHStr) imageResizeH, err = strconv.Atoi(imageResizeHStr)
this.PanicError(err) this.PanicError(err)
if imageResizeH < 0 || imageResizeH > 4096 { if imageResizeH < 0 || imageResizeH > 4096 {
panic("缩放尺寸不能超过4096") panic(result.BadRequest("zoom size cannot exceed 4096"))
} }
} }
return true, imageResizeM, imageResizeW, imageResizeH return true, imageResizeM, imageResizeW, imageResizeH
@ -123,7 +92,7 @@ func (this *ImageCacheService) ResizeParams(request *http.Request) (needProcess
} }
//图片预处理功能。 //resize image.
func (this *ImageCacheService) ResizeImage(request *http.Request, filePath string) *image.NRGBA { func (this *ImageCacheService) ResizeImage(request *http.Request, filePath string) *image.NRGBA {
diskFile, err := os.Open(filePath) diskFile, err := os.Open(filePath)
@ -135,52 +104,54 @@ func (this *ImageCacheService) ResizeImage(request *http.Request, filePath strin
_, imageResizeM, imageResizeW, imageResizeH := this.ResizeParams(request) _, imageResizeM, imageResizeW, imageResizeH := this.ResizeParams(request)
//单边缩略
if imageResizeM == "fit" { if imageResizeM == "fit" {
//将图缩略成宽度为100高度按比例处理。 //fit mode.
if imageResizeW != 0 { if imageResizeW != 0 {
//eg. width = 100 height auto in proportion
src, err := imaging.Decode(diskFile) src, err := imaging.Decode(diskFile)
this.PanicError(err) this.PanicError(err)
return imaging.Resize(src, imageResizeW, 0, imaging.Lanczos) return imaging.Resize(src, imageResizeW, 0, imaging.Lanczos)
} else if imageResizeH != 0 { } else if imageResizeH != 0 {
//将图缩略成高度为100宽度按比例处理。 //eg. height = 100 width auto in proportion
src, err := imaging.Decode(diskFile) src, err := imaging.Decode(diskFile)
this.PanicError(err) this.PanicError(err)
return imaging.Resize(src, 0, imageResizeH, imaging.Lanczos) return imaging.Resize(src, 0, imageResizeH, imaging.Lanczos)
} else { } else {
panic("单边缩略必须指定宽或者高") panic(result.BadRequest("mode fit required width or height"))
} }
} else if imageResizeM == "fill" { } else if imageResizeM == "fill" {
//固定宽高,自动裁剪 //fill mode. specify the width and height
if imageResizeW > 0 && imageResizeH > 0 { if imageResizeW > 0 && imageResizeH > 0 {
src, err := imaging.Decode(diskFile) src, err := imaging.Decode(diskFile)
this.PanicError(err) this.PanicError(err)
return imaging.Fill(src, imageResizeW, imageResizeH, imaging.Center, imaging.Lanczos) return imaging.Fill(src, imageResizeW, imageResizeH, imaging.Center, imaging.Lanczos)
} else { } else {
panic("固定宽高,自动裁剪 必须同时指定宽和高") panic(result.BadRequest("mode fill required width and height"))
} }
} else if imageResizeM == "fixed" { } else if imageResizeM == "fixed" {
//强制宽高缩略 //fixed mode
if imageResizeW > 0 && imageResizeH > 0 { if imageResizeW > 0 && imageResizeH > 0 {
src, err := imaging.Decode(diskFile) src, err := imaging.Decode(diskFile)
this.PanicError(err) this.PanicError(err)
return imaging.Resize(src, imageResizeW, imageResizeH, imaging.Lanczos) return imaging.Resize(src, imageResizeW, imageResizeH, imaging.Lanczos)
} else { } else {
panic("强制宽高缩略必须同时指定宽和高") panic(result.BadRequest("mode fixed required width and height"))
} }
} else { } else {
panic("不支持" + imageResizeM + "处理模式") panic(result.BadRequest("not support mode %s", imageResizeM))
} }
} }
//缓存一张图片 //cache an image
func (this *ImageCacheService) cacheImage(writer http.ResponseWriter, request *http.Request, matter *Matter) *ImageCache { func (this *ImageCacheService) cacheImage(writer http.ResponseWriter, request *http.Request, matter *Matter) *ImageCache {
//当前的文件是否是图片,只有图片才能处理。 //only these image can do.
extension := util.GetExtension(matter.Name) extension := util.GetExtension(matter.Name)
formats := map[string]imaging.Format{ formats := map[string]imaging.Format{
".jpg": imaging.JPEG, ".jpg": imaging.JPEG,
@ -197,19 +168,18 @@ func (this *ImageCacheService) cacheImage(writer http.ResponseWriter, request *h
format, ok := formats[extension] format, ok := formats[extension]
if !ok { if !ok {
panic("该图片格式不支持处理") panic(result.BadRequest("not support this kind of image's (%s) resize", extension))
} }
user := this.userDao.FindByUuid(matter.UserUuid) user := this.userDao.FindByUuid(matter.UserUuid)
//resize图片
dstImage := this.ResizeImage(request, matter.AbsolutePath()) dstImage := this.ResizeImage(request, matter.AbsolutePath())
cacheImageName := util.GetSimpleFileName(matter.Name) + "_" + mode + extension cacheImageName := util.GetSimpleFileName(matter.Name) + "_" + mode + extension
cacheImageRelativePath := util.GetSimpleFileName(matter.Path) + "_" + mode + extension cacheImageRelativePath := util.GetSimpleFileName(matter.Path) + "_" + mode + extension
cacheImageAbsolutePath := GetUserCacheRootDir(user.Username) + util.GetSimpleFileName(matter.Path) + "_" + mode + extension cacheImageAbsolutePath := GetUserCacheRootDir(user.Username) + util.GetSimpleFileName(matter.Path) + "_" + mode + extension
//创建目录。 //create directory
dir := filepath.Dir(cacheImageAbsolutePath) dir := filepath.Dir(cacheImageAbsolutePath)
util.MakeDirAll(dir) util.MakeDirAll(dir)
@ -220,15 +190,13 @@ func (this *ImageCacheService) cacheImage(writer http.ResponseWriter, request *h
this.PanicError(e) this.PanicError(e)
}() }()
//处理后的图片存放在本地 //store on disk after handle
err = imaging.Encode(fileWriter, dstImage, format) err = imaging.Encode(fileWriter, dstImage, format)
this.PanicError(err) this.PanicError(err)
//获取新文件的大小
fileInfo, err := fileWriter.Stat() fileInfo, err := fileWriter.Stat()
this.PanicError(err) this.PanicError(err)
//相关信息写到缓存中去
imageCache := &ImageCache{ imageCache := &ImageCache{
Name: cacheImageName, Name: cacheImageName,
UserUuid: matter.UserUuid, UserUuid: matter.UserUuid,

View File

@ -9,15 +9,13 @@ import (
"github.com/eyebluecn/tank/code/tool/util" "github.com/eyebluecn/tank/code/tool/util"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/nu7hatch/gouuid" "github.com/nu7hatch/gouuid"
"go/build"
"io/ioutil"
"net/http" "net/http"
"regexp" "regexp"
"strconv" "strconv"
"time" "time"
) )
//安装程序的接口,只有安装阶段可以访问。 //install apis. Only when installing period can be visited.
type InstallController struct { type InstallController struct {
BaseController BaseController
uploadTokenDao *UploadTokenDao uploadTokenDao *UploadTokenDao
@ -29,11 +27,9 @@ type InstallController struct {
tableNames []IBase tableNames []IBase
} }
//初始化方法
func (this *InstallController) Init() { func (this *InstallController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean.
b := core.CONTEXT.GetBean(this.uploadTokenDao) b := core.CONTEXT.GetBean(this.uploadTokenDao)
if c, ok := b.(*UploadTokenDao); ok { if c, ok := b.(*UploadTokenDao); ok {
this.uploadTokenDao = c this.uploadTokenDao = c
@ -80,12 +76,10 @@ func (this *InstallController) Init() {
} }
//注册自己的路由。
func (this *InstallController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *InstallController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
//每个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.TableInfoList, 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/table"] = this.Wrap(this.CreateTable, USER_ROLE_GUEST)
@ -97,7 +91,6 @@ func (this *InstallController) RegisterRoutes() map[string]func(writer http.Resp
return routeMap return routeMap
} }
//获取数据库连接
func (this *InstallController) openDbConnection(writer http.ResponseWriter, request *http.Request) *gorm.DB { func (this *InstallController) openDbConnection(writer http.ResponseWriter, request *http.Request) *gorm.DB {
mysqlPortStr := request.FormValue("mysqlPort") mysqlPortStr := request.FormValue("mysqlPort")
mysqlHost := request.FormValue("mysqlHost") mysqlHost := request.FormValue("mysqlHost")
@ -114,7 +107,7 @@ func (this *InstallController) openDbConnection(writer http.ResponseWriter, requ
mysqlUrl := util.GetMysqlUrl(mysqlPort, mysqlHost, mysqlSchema, mysqlUsername, mysqlPassword) mysqlUrl := util.GetMysqlUrl(mysqlPort, mysqlHost, mysqlSchema, mysqlUsername, mysqlPassword)
this.logger.Info("连接MySQL %s", mysqlUrl) this.logger.Info("Connect MySQL %s", mysqlUrl)
var err error = nil var err error = nil
db, err := gorm.Open("mysql", mysqlUrl) db, err := gorm.Open("mysql", mysqlUrl)
@ -126,50 +119,18 @@ func (this *InstallController) openDbConnection(writer http.ResponseWriter, requ
} }
//关闭数据库连接
func (this *InstallController) closeDbConnection(db *gorm.DB) { func (this *InstallController) closeDbConnection(db *gorm.DB) {
if db != nil { if db != nil {
err := db.Close() err := db.Close()
if err != nil { if err != nil {
this.logger.Error("关闭数据库连接出错 %v", err) this.logger.Error("occur error when close db. %v", err)
} }
} }
} }
//根据表名获取建表SQL语句
func (this *InstallController) getCreateSQLFromFile(tableName string) string {
//1. 从当前安装目录db下去寻找建表文件。
homePath := util.GetHomePath()
filePath := homePath + "/db/" + tableName + ".sql"
exists := util.PathExists(filePath)
//2. 从GOPATH下面去找因为可能是开发环境
if !exists {
this.logger.Info("GOPATH = %s", build.Default.GOPATH)
filePath1 := filePath
filePath = build.Default.GOPATH + "/src/tank/build/db/" + tableName + ".sql"
exists = util.PathExists(filePath)
if !exists {
panic(result.Server("%s 或 %s 均不存在,请检查你的安装情况。", filePath1, filePath))
}
}
//读取文件内容.
bytes, err := ioutil.ReadFile(filePath)
this.PanicError(err)
return string(bytes)
}
//根据表名获取建表SQL语句
func (this *InstallController) getTableMeta(gormDb *gorm.DB, entity IBase) (bool, []*gorm.StructField, []*gorm.StructField) { func (this *InstallController) getTableMeta(gormDb *gorm.DB, entity IBase) (bool, []*gorm.StructField, []*gorm.StructField) {
//挣扎一下,尝试获取建表语句。
db := gormDb.Unscoped() db := gormDb.Unscoped()
scope := db.NewScope(entity) scope := db.NewScope(entity)
@ -197,7 +158,6 @@ func (this *InstallController) getTableMeta(gormDb *gorm.DB, entity IBase) (bool
} }
//根据表名获取建表SQL语句
func (this *InstallController) getTableMetaList(db *gorm.DB) []*InstallTableInfo { func (this *InstallController) getTableMetaList(db *gorm.DB) []*InstallTableInfo {
var installTableInfos []*InstallTableInfo var installTableInfos []*InstallTableInfo
@ -215,7 +175,7 @@ func (this *InstallController) getTableMetaList(db *gorm.DB) []*InstallTableInfo
return installTableInfos return installTableInfos
} }
//验证表结构是否完整。会直接抛出异常 // validate table whether integrity. if not panic err.
func (this *InstallController) validateTableMetaList(tableInfoList []*InstallTableInfo) { func (this *InstallController) validateTableMetaList(tableInfoList []*InstallTableInfo) {
for _, tableInfo := range tableInfoList { for _, tableInfo := range tableInfoList {
@ -236,20 +196,19 @@ func (this *InstallController) validateTableMetaList(tableInfoList []*InstallTab
} }
//验证数据库连接 //Ping db.
func (this *InstallController) Verify(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *InstallController) Verify(writer http.ResponseWriter, request *http.Request) *result.WebResult {
db := this.openDbConnection(writer, request) db := this.openDbConnection(writer, request)
defer this.closeDbConnection(db) defer this.closeDbConnection(db)
this.logger.Info("Ping一下数据库") this.logger.Info("Ping DB")
err := db.DB().Ping() err := db.DB().Ping()
this.PanicError(err) this.PanicError(err)
return this.Success("OK") return this.Success("OK")
} }
//获取需要安装的数据库表
func (this *InstallController) TableInfoList(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *InstallController) TableInfoList(writer http.ResponseWriter, request *http.Request) *result.WebResult {
db := this.openDbConnection(writer, request) db := this.openDbConnection(writer, request)
@ -258,7 +217,6 @@ func (this *InstallController) TableInfoList(writer http.ResponseWriter, request
return this.Success(this.getTableMetaList(db)) return this.Success(this.getTableMetaList(db))
} }
//创建缺失数据库和表
func (this *InstallController) CreateTable(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *InstallController) CreateTable(writer http.ResponseWriter, request *http.Request) *result.WebResult {
var installTableInfos []*InstallTableInfo var installTableInfos []*InstallTableInfo
@ -268,7 +226,7 @@ func (this *InstallController) CreateTable(writer http.ResponseWriter, request *
for _, iBase := range this.tableNames { for _, iBase := range this.tableNames {
//补全缺失字段或者创建数据库表 //complete the missing fields or create table.
db1 := db.AutoMigrate(iBase) db1 := db.AutoMigrate(iBase)
this.PanicError(db1.Error) this.PanicError(db1.Error)
@ -286,7 +244,7 @@ func (this *InstallController) CreateTable(writer http.ResponseWriter, request *
} }
//获取管理员列表(10条记录) //get the list of admin.
func (this *InstallController) AdminList(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *InstallController) AdminList(writer http.ResponseWriter, request *http.Request) *result.WebResult {
db := this.openDbConnection(writer, request) db := this.openDbConnection(writer, request)
@ -304,7 +262,7 @@ func (this *InstallController) AdminList(writer http.ResponseWriter, request *ht
return this.Success(users) return this.Success(users)
} }
//创建管理员 //create admin
func (this *InstallController) CreateAdmin(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *InstallController) CreateAdmin(writer http.ResponseWriter, request *http.Request) *result.WebResult {
db := this.openDbConnection(writer, request) db := this.openDbConnection(writer, request)
@ -313,7 +271,7 @@ func (this *InstallController) CreateAdmin(writer http.ResponseWriter, request *
adminUsername := request.FormValue("adminUsername") adminUsername := request.FormValue("adminUsername")
adminPassword := request.FormValue("adminPassword") adminPassword := request.FormValue("adminPassword")
//验证超级管理员的信息 //validate admin's username
if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, adminUsername); !m { if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, adminUsername); !m {
panic(result.BadRequest(`admin's username cannot oly be alphabet, number or '_'`)) panic(result.BadRequest(`admin's username cannot oly be alphabet, number or '_'`))
} }
@ -322,7 +280,7 @@ func (this *InstallController) CreateAdmin(writer http.ResponseWriter, request *
panic(result.BadRequest(`admin's password at least 6 chars'`)) panic(result.BadRequest(`admin's password at least 6 chars'`))
} }
//检查是否有重复。 //check whether duplicate
var count2 int64 var count2 int64
db2 := db.Model(&User{}).Where("username = ?", adminUsername).Count(&count2) db2 := db.Model(&User{}).Where("username = ?", adminUsername).Count(&count2)
this.PanicError(db2.Error) this.PanicError(db2.Error)
@ -350,7 +308,7 @@ func (this *InstallController) CreateAdmin(writer http.ResponseWriter, request *
} }
//(如果数据库中本身存在管理员了)验证管理员 //(if there is admin in db)Validate admin.
func (this *InstallController) ValidateAdmin(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *InstallController) ValidateAdmin(writer http.ResponseWriter, request *http.Request) *result.WebResult {
db := this.openDbConnection(writer, request) db := this.openDbConnection(writer, request)
@ -359,7 +317,6 @@ func (this *InstallController) ValidateAdmin(writer http.ResponseWriter, request
adminUsername := request.FormValue("adminUsername") adminUsername := request.FormValue("adminUsername")
adminPassword := request.FormValue("adminPassword") adminPassword := request.FormValue("adminPassword")
//验证超级管理员的信息
if adminUsername == "" { if adminUsername == "" {
panic(result.BadRequest(`admin's username cannot be null'`)) panic(result.BadRequest(`admin's username cannot be null'`))
} }
@ -385,7 +342,7 @@ func (this *InstallController) ValidateAdmin(writer http.ResponseWriter, request
} }
//完成系统安装 //Finish the installation
func (this *InstallController) Finish(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *InstallController) Finish(writer http.ResponseWriter, request *http.Request) *result.WebResult {
mysqlPortStr := request.FormValue("mysqlPort") mysqlPortStr := request.FormValue("mysqlPort")
@ -401,15 +358,15 @@ func (this *InstallController) Finish(writer http.ResponseWriter, request *http.
mysqlPort = tmp mysqlPort = tmp
} }
//要求数据库连接通畅 //Recheck the db connection
db := this.openDbConnection(writer, request) db := this.openDbConnection(writer, request)
defer this.closeDbConnection(db) defer this.closeDbConnection(db)
//要求数据库完整。 //Recheck the integrity of tables.
tableMetaList := this.getTableMetaList(db) tableMetaList := this.getTableMetaList(db)
this.validateTableMetaList(tableMetaList) this.validateTableMetaList(tableMetaList)
//要求至少有一名管理员。 //At least one admin
var count1 int64 var count1 int64
db1 := db.Model(&User{}).Where("role = ?", USER_ROLE_ADMINISTRATOR).Count(&count1) db1 := db.Model(&User{}).Where("role = ?", USER_ROLE_ADMINISTRATOR).Count(&count1)
this.PanicError(db1.Error) this.PanicError(db1.Error)
@ -417,10 +374,10 @@ func (this *InstallController) Finish(writer http.ResponseWriter, request *http.
panic(result.BadRequest(`please config at least one admin user`)) panic(result.BadRequest(`please config at least one admin user`))
} }
//通知配置文件安装完毕。 //announce the config to write config to tank.json
core.CONFIG.FinishInstall(mysqlPort, mysqlHost, mysqlSchema, mysqlUsername, mysqlPassword) core.CONFIG.FinishInstall(mysqlPort, mysqlHost, mysqlSchema, mysqlUsername, mysqlPassword)
//通知全局上下文,说系统安装好了 //announce the context to broadcast the installation news to bean.
core.CONTEXT.InstallOk() core.CONTEXT.InstallOk()
return this.Success("OK") return this.Success("OK")

View File

@ -3,7 +3,7 @@ package rest
import "github.com/jinzhu/gorm" import "github.com/jinzhu/gorm"
/** /**
* 表名对应的表结构 * table meta info.
*/ */
type InstallTableInfo struct { type InstallTableInfo struct {
Name string `json:"name"` Name string `json:"name"`

View File

@ -22,11 +22,9 @@ type MatterController struct {
imageCacheService *ImageCacheService imageCacheService *ImageCacheService
} }
//初始化方法 start to develop v3.
func (this *MatterController) Init() { func (this *MatterController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.matterDao) b := core.CONTEXT.GetBean(this.matterDao)
if b, ok := b.(*MatterDao); ok { if b, ok := b.(*MatterDao); ok {
this.matterDao = b this.matterDao = b
@ -68,12 +66,10 @@ func (this *MatterController) Init() {
} }
} }
//注册自己的路由。
func (this *MatterController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *MatterController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
//每个Controller需要主动注册自己的路由。
routeMap["/api/matter/create/directory"] = this.Wrap(this.CreateDirectory, USER_ROLE_USER) routeMap["/api/matter/create/directory"] = this.Wrap(this.CreateDirectory, USER_ROLE_USER)
routeMap["/api/matter/upload"] = this.Wrap(this.Upload, USER_ROLE_USER) routeMap["/api/matter/upload"] = this.Wrap(this.Upload, USER_ROLE_USER)
routeMap["/api/matter/crawl"] = this.Wrap(this.Crawl, USER_ROLE_USER) routeMap["/api/matter/crawl"] = this.Wrap(this.Crawl, USER_ROLE_USER)
@ -85,14 +81,13 @@ func (this *MatterController) RegisterRoutes() map[string]func(writer http.Respo
routeMap["/api/matter/detail"] = this.Wrap(this.Detail, USER_ROLE_USER) routeMap["/api/matter/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
routeMap["/api/matter/page"] = this.Wrap(this.Page, USER_ROLE_GUEST) routeMap["/api/matter/page"] = this.Wrap(this.Page, USER_ROLE_GUEST)
//本地文件映射 //mirror local files.
routeMap["/api/matter/mirror"] = this.Wrap(this.Mirror, USER_ROLE_USER) routeMap["/api/matter/mirror"] = this.Wrap(this.Mirror, USER_ROLE_USER)
routeMap["/api/matter/zip"] = this.Wrap(this.Zip, USER_ROLE_USER) routeMap["/api/matter/zip"] = this.Wrap(this.Zip, USER_ROLE_USER)
return routeMap return routeMap
} }
//查看某个文件的详情。
func (this *MatterController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuid := request.FormValue("uuid") uuid := request.FormValue("uuid")
@ -102,7 +97,6 @@ func (this *MatterController) Detail(writer http.ResponseWriter, request *http.R
matter := this.matterService.Detail(request, uuid) matter := this.matterService.Detail(request, uuid)
//验证当前之人是否有权限查看这么详细。
user := this.checkUser(request) user := this.checkUser(request)
if matter.UserUuid != user.Uuid { if matter.UserUuid != user.Uuid {
panic(result.UNAUTHORIZED) panic(result.UNAUTHORIZED)
@ -112,10 +106,8 @@ func (this *MatterController) Detail(writer http.ResponseWriter, request *http.R
} }
//按照分页的方式获取某个文件夹下文件和子文件夹的列表,通常情况下只有一页。
func (this *MatterController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult {
//如果是根目录那么就传入root.
pageStr := request.FormValue("page") pageStr := request.FormValue("page")
pageSizeStr := request.FormValue("pageSize") pageSizeStr := request.FormValue("pageSize")
orderCreateTime := request.FormValue("orderCreateTime") orderCreateTime := request.FormValue("orderCreateTime")
@ -133,7 +125,7 @@ func (this *MatterController) Page(writer http.ResponseWriter, request *http.Req
var userUuid string var userUuid string
//使用分享提取码的形式授权。 //auth by shareUuid.
shareUuid := request.FormValue("shareUuid") shareUuid := request.FormValue("shareUuid")
shareCode := request.FormValue("shareCode") shareCode := request.FormValue("shareCode")
shareRootUuid := request.FormValue("shareRootUuid") shareRootUuid := request.FormValue("shareRootUuid")
@ -149,12 +141,12 @@ func (this *MatterController) Page(writer http.ResponseWriter, request *http.Req
} }
user := this.findUser(request) user := this.findUser(request)
//根据某个shareUuid和code某个用户是否有权限获取 shareRootUuid 下面的 matterUuid
this.shareService.ValidateMatter(shareUuid, shareCode, user, shareRootUuid, dirMatter) this.shareService.ValidateMatter(shareUuid, shareCode, user, shareRootUuid, dirMatter)
userUuid = dirMatter.Uuid userUuid = dirMatter.Uuid
} else { } else {
//非分享模式要求必须登录 //if cannot auth by share. Then login is required.
user := this.checkUser(request) user := this.checkUser(request)
userUuid = user.Uuid userUuid = user.Uuid
@ -173,7 +165,6 @@ func (this *MatterController) Page(writer http.ResponseWriter, request *http.Req
} }
} }
//筛选后缀名
var extensions []string var extensions []string
if extensionsStr != "" { if extensionsStr != "" {
extensions = strings.Split(extensionsStr, ",") extensions = strings.Split(extensionsStr, ",")
@ -215,28 +206,19 @@ func (this *MatterController) Page(writer http.ResponseWriter, request *http.Req
return this.Success(pager) return this.Success(pager)
} }
//创建一个文件夹。
func (this *MatterController) CreateDirectory(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) CreateDirectory(writer http.ResponseWriter, request *http.Request) *result.WebResult {
puuid := request.FormValue("puuid") puuid := request.FormValue("puuid")
name := request.FormValue("name") name := request.FormValue("name")
//管理员可以指定给某个用户创建文件夹。
user := this.checkUser(request) user := this.checkUser(request)
//找到父级matter var dirMatter = this.matterDao.CheckWithRootByUuid(puuid, user)
var dirMatter *Matter
if puuid == MATTER_ROOT {
dirMatter = NewRootMatter(user)
} else {
dirMatter = this.matterDao.CheckByUuid(puuid)
}
matter := this.matterService.AtomicCreateDirectory(request, dirMatter, name, user) matter := this.matterService.AtomicCreateDirectory(request, dirMatter, name, user)
return this.Success(matter) return this.Success(matter)
} }
//上传文件
func (this *MatterController) Upload(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Upload(writer http.ResponseWriter, request *http.Request) *result.WebResult {
puuid := request.FormValue("puuid") puuid := request.FormValue("puuid")
@ -255,7 +237,7 @@ func (this *MatterController) Upload(writer http.ResponseWriter, request *http.R
err = request.ParseMultipartForm(32 << 20) err = request.ParseMultipartForm(32 << 20)
this.PanicError(err) this.PanicError(err)
//对于IE浏览器filename可能包含了路径。 //for IE browser. filename may contains filepath.
fileName := handler.Filename fileName := handler.Filename
pos := strings.LastIndex(fileName, "\\") pos := strings.LastIndex(fileName, "\\")
if pos != -1 { if pos != -1 {
@ -268,13 +250,13 @@ func (this *MatterController) Upload(writer http.ResponseWriter, request *http.R
dirMatter := this.matterDao.CheckWithRootByUuid(puuid, user) dirMatter := this.matterDao.CheckWithRootByUuid(puuid, user)
//为了支持多文件同时上传 //support upload simultaneously
matter := this.matterService.Upload(request, file, user, dirMatter, fileName, privacy) matter := this.matterService.Upload(request, file, user, dirMatter, fileName, privacy)
return this.Success(matter) return this.Success(matter)
} }
//从一个Url中去爬取资源 //crawl a file by url.
func (this *MatterController) Crawl(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Crawl(writer http.ResponseWriter, request *http.Request) *result.WebResult {
url := request.FormValue("url") url := request.FormValue("url")
@ -286,11 +268,11 @@ func (this *MatterController) Crawl(writer http.ResponseWriter, request *http.Re
dirMatter := this.matterService.CreateDirectories(request, user, destPath) dirMatter := this.matterService.CreateDirectories(request, user, destPath)
if url == "" || (!strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://")) { if url == "" || (!strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://")) {
panic("资源url必填并且应该以http://或者https://开头") panic(" url must start with http:// or https://")
} }
if filename == "" { if filename == "" {
panic("filename 必填") panic("filename cannot be null")
} }
matter := this.matterService.AtomicCrawl(request, url, filename, user, dirMatter, true) matter := this.matterService.AtomicCrawl(request, url, filename, user, dirMatter, true)
@ -298,7 +280,6 @@ func (this *MatterController) Crawl(writer http.ResponseWriter, request *http.Re
return this.Success(matter) return this.Success(matter)
} }
//删除一个文件
func (this *MatterController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuid := request.FormValue("uuid") uuid := request.FormValue("uuid")
@ -308,7 +289,6 @@ func (this *MatterController) Delete(writer http.ResponseWriter, request *http.R
matter := this.matterDao.CheckByUuid(uuid) matter := this.matterDao.CheckByUuid(uuid)
//判断文件的所属人是否正确
user := this.checkUser(request) user := this.checkUser(request)
if matter.UserUuid != user.Uuid { if matter.UserUuid != user.Uuid {
panic(result.UNAUTHORIZED) panic(result.UNAUTHORIZED)
@ -316,10 +296,9 @@ func (this *MatterController) Delete(writer http.ResponseWriter, request *http.R
this.matterService.AtomicDelete(request, matter) this.matterService.AtomicDelete(request, matter)
return this.Success("删除成功!") return this.Success("OK")
} }
//删除一系列文件。
func (this *MatterController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuids := request.FormValue("uuids") uuids := request.FormValue("uuids")
@ -333,13 +312,11 @@ func (this *MatterController) DeleteBatch(writer http.ResponseWriter, request *h
matter := this.matterDao.FindByUuid(uuid) matter := this.matterDao.FindByUuid(uuid)
//如果matter已经是nil了直接跳过
if matter == nil { if matter == nil {
this.logger.Warn("%s not exist anymore", uuid) this.logger.Warn("%s not exist anymore", uuid)
continue continue
} }
//判断文件的所属人是否正确
user := this.checkUser(request) user := this.checkUser(request)
if matter.UserUuid != user.Uuid { if matter.UserUuid != user.Uuid {
panic(result.UNAUTHORIZED) panic(result.UNAUTHORIZED)
@ -349,10 +326,9 @@ func (this *MatterController) DeleteBatch(writer http.ResponseWriter, request *h
} }
return this.Success("删除成功!") return this.Success("OK")
} }
//重命名一个文件或一个文件夹
func (this *MatterController) Rename(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Rename(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuid := request.FormValue("uuid") uuid := request.FormValue("uuid")
@ -360,7 +336,6 @@ func (this *MatterController) Rename(writer http.ResponseWriter, request *http.R
user := this.checkUser(request) user := this.checkUser(request)
//找出该文件或者文件夹
matter := this.matterDao.CheckByUuid(uuid) matter := this.matterDao.CheckByUuid(uuid)
if matter.UserUuid != user.Uuid { if matter.UserUuid != user.Uuid {
@ -372,7 +347,6 @@ func (this *MatterController) Rename(writer http.ResponseWriter, request *http.R
return this.Success(matter) return this.Success(matter)
} }
//改变一个文件的公私有属性
func (this *MatterController) ChangePrivacy(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) ChangePrivacy(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuid := request.FormValue("uuid") uuid := request.FormValue("uuid")
privacyStr := request.FormValue("privacy") privacyStr := request.FormValue("privacy")
@ -380,14 +354,13 @@ func (this *MatterController) ChangePrivacy(writer http.ResponseWriter, request
if privacyStr == TRUE { if privacyStr == TRUE {
privacy = true privacy = true
} }
//找出该文件或者文件夹
matter := this.matterDao.CheckByUuid(uuid) matter := this.matterDao.CheckByUuid(uuid)
if matter.Privacy == privacy { if matter.Privacy == privacy {
panic("公私有属性没有改变!") panic(result.BadRequest("not changed. Invalid operation."))
} }
//权限验证
user := this.checkUser(request) user := this.checkUser(request)
if matter.UserUuid != user.Uuid { if matter.UserUuid != user.Uuid {
panic(result.UNAUTHORIZED) panic(result.UNAUTHORIZED)
@ -396,17 +369,15 @@ func (this *MatterController) ChangePrivacy(writer http.ResponseWriter, request
matter.Privacy = privacy matter.Privacy = privacy
this.matterDao.Save(matter) this.matterDao.Save(matter)
return this.Success("设置成功") return this.Success("OK")
} }
//将一个文件夹或者文件移入到另一个文件夹下。
func (this *MatterController) Move(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Move(writer http.ResponseWriter, request *http.Request) *result.WebResult {
srcUuidsStr := request.FormValue("srcUuids") srcUuidsStr := request.FormValue("srcUuids")
destUuid := request.FormValue("destUuid") destUuid := request.FormValue("destUuid")
var srcUuids []string var srcUuids []string
//验证参数。
if srcUuidsStr == "" { if srcUuidsStr == "" {
panic(result.BadRequest("srcUuids cannot be null")) panic(result.BadRequest("srcUuids cannot be null"))
} else { } else {
@ -415,7 +386,6 @@ func (this *MatterController) Move(writer http.ResponseWriter, request *http.Req
user := this.checkUser(request) user := this.checkUser(request)
//验证dest是否有问题
var destMatter = this.matterDao.CheckWithRootByUuid(destUuid, user) var destMatter = this.matterDao.CheckWithRootByUuid(destUuid, user)
if !destMatter.Dir { if !destMatter.Dir {
panic(result.BadRequest("destination is not a directory")) panic(result.BadRequest("destination is not a directory"))
@ -426,23 +396,20 @@ func (this *MatterController) Move(writer http.ResponseWriter, request *http.Req
} }
var srcMatters []*Matter var srcMatters []*Matter
//验证src是否有问题。
for _, uuid := range srcUuids { for _, uuid := range srcUuids {
//找出该文件或者文件夹
srcMatter := this.matterDao.CheckByUuid(uuid) srcMatter := this.matterDao.CheckByUuid(uuid)
if srcMatter.Puuid == destMatter.Uuid { if srcMatter.Puuid == destMatter.Uuid {
panic(result.BadRequest("no move, invalid operation")) panic(result.BadRequest("no move, invalid operation"))
} }
//判断同级文件夹中是否有同名的文件 //check whether there are files with the same name.
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, destMatter.Uuid, srcMatter.Dir, srcMatter.Name) count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, destMatter.Uuid, srcMatter.Dir, srcMatter.Name)
if count > 0 { if count > 0 {
panic(result.BadRequestI18n(request, i18n.MatterExist, srcMatter.Name)) panic(result.BadRequestI18n(request, i18n.MatterExist, srcMatter.Name))
} }
//判断和目标文件夹是否是同一个主人。
if srcMatter.UserUuid != destMatter.UserUuid { if srcMatter.UserUuid != destMatter.UserUuid {
panic("owner not the same") panic("owner not the same")
} }
@ -455,7 +422,7 @@ func (this *MatterController) Move(writer http.ResponseWriter, request *http.Req
return this.Success(nil) return this.Success(nil)
} }
//将本地文件映射到蓝眼云盘中去。 //mirror local files to EyeblueTank
func (this *MatterController) Mirror(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Mirror(writer http.ResponseWriter, request *http.Request) *result.WebResult {
srcPath := request.FormValue("srcPath") srcPath := request.FormValue("srcPath")
@ -479,7 +446,7 @@ func (this *MatterController) Mirror(writer http.ResponseWriter, request *http.R
} }
//下载压缩包 //download zip.
func (this *MatterController) Zip(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *MatterController) Zip(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuids := request.FormValue("uuids") uuids := request.FormValue("uuids")

View File

@ -17,11 +17,9 @@ type MatterDao struct {
bridgeDao *BridgeDao bridgeDao *BridgeDao
} }
//初始化方法
func (this *MatterDao) Init() { func (this *MatterDao) Init() {
this.BaseDao.Init() this.BaseDao.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.imageCacheDao) b := core.CONTEXT.GetBean(this.imageCacheDao)
if b, ok := b.(*ImageCacheDao); ok { if b, ok := b.(*ImageCacheDao); ok {
this.imageCacheDao = b this.imageCacheDao = b
@ -34,32 +32,29 @@ func (this *MatterDao) Init() {
} }
//按照Id查询文件
func (this *MatterDao) FindByUuid(uuid string) *Matter { func (this *MatterDao) FindByUuid(uuid string) *Matter {
var entity = &Matter{}
// Read db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(entity)
var matter Matter
db := core.CONTEXT.GetDB().Where(&Matter{Base: Base{Uuid: uuid}}).First(&matter)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND { if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else { } else {
this.PanicError(db.Error) panic(db.Error)
} }
} }
return &matter return entity
} }
//按照Id查询文件 //find by uuid. if not found panic NotFound error
func (this *MatterDao) CheckByUuid(uuid string) *Matter { func (this *MatterDao) CheckByUuid(uuid string) *Matter {
matter := this.FindByUuid(uuid) entity := this.FindByUuid(uuid)
if matter == nil { if entity == nil {
panic(result.NotFound("%s 对应的matter不存在", uuid)) panic(result.NotFound("not found record with uuid = %s", uuid))
} }
return matter return entity
} }
//按照uuid查找一个文件夹可能返回root对应的matter. // find by uuid. if uuid=root, then return the Root Matter
func (this *MatterDao) CheckWithRootByUuid(uuid string, user *User) *Matter { func (this *MatterDao) CheckWithRootByUuid(uuid string, user *User) *Matter {
if uuid == "" { if uuid == "" {
@ -79,7 +74,7 @@ func (this *MatterDao) CheckWithRootByUuid(uuid string, user *User) *Matter {
return matter return matter
} }
//按照path查找一个matter可能返回root对应的matter. // find by path. if path=/, then return the Root Matter
func (this *MatterDao) CheckWithRootByPath(path string, user *User) *Matter { func (this *MatterDao) CheckWithRootByPath(path string, user *User) *Matter {
var matter *Matter var matter *Matter
@ -88,7 +83,6 @@ func (this *MatterDao) CheckWithRootByPath(path string, user *User) *Matter {
panic(result.BadRequest("user cannot be null.")) panic(result.BadRequest("user cannot be null."))
} }
//目标文件夹matter
if path == "" || path == "/" { if path == "" || path == "/" {
matter = NewRootMatter(user) matter = NewRootMatter(user)
} else { } else {
@ -98,7 +92,6 @@ func (this *MatterDao) CheckWithRootByPath(path string, user *User) *Matter {
return matter return matter
} }
//按照名字查询文件夹
func (this *MatterDao) FindByUserUuidAndPuuidAndNameAndDirTrue(userUuid string, puuid string, name string) *Matter { func (this *MatterDao) FindByUserUuidAndPuuidAndNameAndDirTrue(userUuid string, puuid string, name string) *Matter {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
@ -127,10 +120,8 @@ func (this *MatterDao) FindByUserUuidAndPuuidAndNameAndDirTrue(userUuid string,
return matter return matter
} }
//按照id和userUuid来查找。找不到抛异常。
func (this *MatterDao) CheckByUuidAndUserUuid(uuid string, userUuid string) *Matter { func (this *MatterDao) CheckByUuidAndUserUuid(uuid string, userUuid string) *Matter {
// Read
var matter = &Matter{} var matter = &Matter{}
db := core.CONTEXT.GetDB().Where(&Matter{Base: Base{Uuid: uuid}, UserUuid: userUuid}).First(matter) db := core.CONTEXT.GetDB().Where(&Matter{Base: Base{Uuid: uuid}, UserUuid: userUuid}).First(matter)
this.PanicError(db.Error) this.PanicError(db.Error)
@ -139,7 +130,6 @@ func (this *MatterDao) CheckByUuidAndUserUuid(uuid string, userUuid string) *Mat
} }
//统计某个用户的某个文件夹下的某个名字的文件(或文件夹)数量。
func (this *MatterDao) CountByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) int { func (this *MatterDao) CountByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) int {
var matter Matter var matter Matter
@ -170,7 +160,6 @@ func (this *MatterDao) CountByUserUuidAndPuuidAndDirAndName(userUuid string, puu
return count return count
} }
//统计某个用户的某个文件夹下的某个名字的文件(或文件夹)数量。
func (this *MatterDao) FindByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) *Matter { func (this *MatterDao) FindByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) *Matter {
var matter = &Matter{} var matter = &Matter{}
@ -204,7 +193,6 @@ func (this *MatterDao) FindByUserUuidAndPuuidAndDirAndName(userUuid string, puui
return matter return matter
} }
//获取某个用户的某个文件夹下的某个名字的文件(或文件夹)列表
func (this *MatterDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) []*Matter { func (this *MatterDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) []*Matter {
var matters []*Matter var matters []*Matter
@ -217,13 +205,11 @@ func (this *MatterDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string, puui
return matters return matters
} }
//获取某个文件夹下所有的文件和子文件
func (this *MatterDao) ListByPuuidAndUserUuid(puuid string, userUuid string, sortArray []builder.OrderPair) []*Matter { func (this *MatterDao) ListByPuuidAndUserUuid(puuid string, userUuid string, sortArray []builder.OrderPair) []*Matter {
var matters []*Matter var matters []*Matter
if sortArray == nil { if sortArray == nil {
//顺序按照文件夹,创建时间
sortArray = []builder.OrderPair{ sortArray = []builder.OrderPair{
{ {
Key: "dir", Key: "dir",
@ -242,7 +228,6 @@ func (this *MatterDao) ListByPuuidAndUserUuid(puuid string, userUuid string, sor
return matters return matters
} }
//根据uuid查找对应的Matters
func (this *MatterDao) ListByUuids(uuids []string, sortArray []builder.OrderPair) []*Matter { func (this *MatterDao) ListByUuids(uuids []string, sortArray []builder.OrderPair) []*Matter {
var matters []*Matter var matters []*Matter
@ -252,7 +237,6 @@ func (this *MatterDao) ListByUuids(uuids []string, sortArray []builder.OrderPair
return matters return matters
} }
//获取某个文件夹下所有的文件和子文件
func (this *MatterDao) Page(page int, pageSize int, puuid string, userUuid string, name string, dir string, extensions []string, sortArray []builder.OrderPair) *Pager { func (this *MatterDao) Page(page int, pageSize int, puuid string, userUuid string, name string, dir string, extensions []string, sortArray []builder.OrderPair) *Pager {
var wp = &builder.WherePair{} var wp = &builder.WherePair{}
@ -300,7 +284,6 @@ func (this *MatterDao) Page(page int, pageSize int, puuid string, userUuid strin
return pager return pager
} }
//创建
func (this *MatterDao) Create(matter *Matter) *Matter { func (this *MatterDao) Create(matter *Matter) *Matter {
timeUUID, _ := uuid.NewV4() timeUUID, _ := uuid.NewV4()
@ -314,7 +297,6 @@ func (this *MatterDao) Create(matter *Matter) *Matter {
return matter return matter
} }
//修改一个文件
func (this *MatterDao) Save(matter *Matter) *Matter { func (this *MatterDao) Save(matter *Matter) *Matter {
matter.UpdateTime = time.Now() matter.UpdateTime = time.Now()
@ -324,13 +306,12 @@ func (this *MatterDao) Save(matter *Matter) *Matter {
return matter return matter
} }
//计数器加一 //download time add 1
func (this *MatterDao) TimesIncrement(matterUuid string) { func (this *MatterDao) TimesIncrement(matterUuid string) {
db := core.CONTEXT.GetDB().Model(&Matter{}).Where("uuid = ?", matterUuid).Update("times", gorm.Expr("times + 1")) db := core.CONTEXT.GetDB().Model(&Matter{}).Where("uuid = ?", matterUuid).Update("times", gorm.Expr("times + 1"))
this.PanicError(db.Error) this.PanicError(db.Error)
} }
//获取一个文件夹中直系文件/文件夹的总大小 puuid可以传root
func (this *MatterDao) SizeByPuuidAndUserUuid(matterUuid string, userUuid string) int64 { func (this *MatterDao) SizeByPuuidAndUserUuid(matterUuid string, userUuid string) int64 {
var wp = &builder.WherePair{Query: "puuid = ? AND user_uuid = ?", Args: []interface{}{matterUuid, userUuid}} var wp = &builder.WherePair{Query: "puuid = ? AND user_uuid = ?", Args: []interface{}{matterUuid, userUuid}}
@ -351,15 +332,14 @@ func (this *MatterDao) SizeByPuuidAndUserUuid(matterUuid string, userUuid string
return sumSize return sumSize
} }
//统计某个文件/文件夹的大小(会自动往上统计,直到根目录) // compute route size. It will compute upward until root directory
func (this *MatterDao) ComputeRouteSize(matterUuid string, userUuid string) { func (this *MatterDao) ComputeRouteSize(matterUuid string, userUuid string) {
//如果更新到了根目录,那么更新到用户身上。 //if to root directory, then update to user's info.
if matterUuid == MATTER_ROOT { if matterUuid == MATTER_ROOT {
size := this.SizeByPuuidAndUserUuid(MATTER_ROOT, userUuid) size := this.SizeByPuuidAndUserUuid(MATTER_ROOT, userUuid)
//更新用户文件的总大小。
db := core.CONTEXT.GetDB().Model(&User{}).Where("uuid = ?", userUuid).Update("total_size", size) db := core.CONTEXT.GetDB().Model(&User{}).Where("uuid = ?", userUuid).Update("total_size", size)
this.PanicError(db.Error) this.PanicError(db.Error)
@ -368,28 +348,27 @@ func (this *MatterDao) ComputeRouteSize(matterUuid string, userUuid string) {
matter := this.CheckByUuid(matterUuid) matter := this.CheckByUuid(matterUuid)
//只有文件夹才去统计 //only compute dir
if matter.Dir { if matter.Dir {
//计算该目录下的直系文件/文件夹总大小 //compute the total size.
size := this.SizeByPuuidAndUserUuid(matterUuid, userUuid) size := this.SizeByPuuidAndUserUuid(matterUuid, userUuid)
//大小有变化才更新 //when changed, we update
if matter.Size != size { if matter.Size != size {
//更新大小。
db := core.CONTEXT.GetDB().Model(&Matter{}).Where("uuid = ?", matterUuid).Update("size", size) db := core.CONTEXT.GetDB().Model(&Matter{}).Where("uuid = ?", matterUuid).Update("size", size)
this.PanicError(db.Error) this.PanicError(db.Error)
} }
} }
//更新自己的上一级目录。 //update parent recursively.
this.ComputeRouteSize(matter.Puuid, userUuid) this.ComputeRouteSize(matter.Puuid, userUuid)
} }
//删除一个文件,数据库中删除,物理磁盘上删除。 //delete a file from db and disk.
func (this *MatterDao) Delete(matter *Matter) { func (this *MatterDao) Delete(matter *Matter) {
//目录的话递归删除。 // recursive if dir
if matter.Dir { if matter.Dir {
matters := this.ListByPuuidAndUserUuid(matter.Uuid, matter.UserUuid, nil) matters := this.ListByPuuidAndUserUuid(matter.Uuid, matter.UserUuid, nil)
@ -397,37 +376,34 @@ func (this *MatterDao) Delete(matter *Matter) {
this.Delete(f) this.Delete(f)
} }
//删除数据库中文件夹本身 //delete from db.
db := core.CONTEXT.GetDB().Delete(&matter) db := core.CONTEXT.GetDB().Delete(&matter)
this.PanicError(db.Error) this.PanicError(db.Error)
//从磁盘中删除该文件夹。 //delete dir from disk.
util.DeleteEmptyDir(matter.AbsolutePath()) util.DeleteEmptyDir(matter.AbsolutePath())
} else { } else {
//删除数据库中文件记录 //delete from db.
db := core.CONTEXT.GetDB().Delete(&matter) db := core.CONTEXT.GetDB().Delete(&matter)
this.PanicError(db.Error) this.PanicError(db.Error)
//删除对应的缓存图片。 //delete its image cache.
this.imageCacheDao.DeleteByMatterUuid(matter.Uuid) this.imageCacheDao.DeleteByMatterUuid(matter.Uuid)
//删除所有的分享文件 //delete all the share.
this.bridgeDao.DeleteByMatterUuid(matter.Uuid) this.bridgeDao.DeleteByMatterUuid(matter.Uuid)
//删除文件 //delete from disk.
err := os.Remove(matter.AbsolutePath()) err := os.Remove(matter.AbsolutePath())
if err != nil { if err != nil {
this.logger.Error("删除磁盘上的文件出错 %s", err.Error()) this.logger.Error("occur error when deleting file. %v", err)
} }
//由于目录和物理结构一一对应,这里不能删除上级文件夹。
} }
} }
//获取一段时间中,总的数量
func (this *MatterDao) CountBetweenTime(startTime time.Time, endTime time.Time) int64 { func (this *MatterDao) CountBetweenTime(startTime time.Time, endTime time.Time) int64 {
var count int64 var count int64
db := core.CONTEXT.GetDB().Model(&Matter{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Count(&count) db := core.CONTEXT.GetDB().Model(&Matter{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Count(&count)
@ -435,12 +411,18 @@ func (this *MatterDao) CountBetweenTime(startTime time.Time, endTime time.Time)
return count return count
} }
//获取一段时间中文件总大小
func (this *MatterDao) SizeBetweenTime(startTime time.Time, endTime time.Time) int64 { func (this *MatterDao) SizeBetweenTime(startTime time.Time, endTime time.Time) int64 {
//TODO: 所有函数汇总的SQL均需要先count询问再处理。 var wp = &builder.WherePair{Query: "create_time >= ? AND create_time <= ?", Args: []interface{}{startTime, endTime}}
var count int64
db := core.CONTEXT.GetDB().Model(&Matter{}).Where(wp.Query, wp.Args...).Count(&count)
if count == 0 {
return 0
}
var size int64 var size int64
db := core.CONTEXT.GetDB().Model(&Matter{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("SUM(size)") db = core.CONTEXT.GetDB().Model(&Matter{}).Where(wp.Query, wp.Args...).Select("SUM(size)")
this.PanicError(db.Error) this.PanicError(db.Error)
row := db.Row() row := db.Row()
err := row.Scan(&size) err := row.Scan(&size)
@ -448,7 +430,6 @@ func (this *MatterDao) SizeBetweenTime(startTime time.Time, endTime time.Time) i
return size return size
} }
//根据userUuid和path来查找
func (this *MatterDao) findByUserUuidAndPath(userUuid string, path string) *Matter { func (this *MatterDao) findByUserUuidAndPath(userUuid string, path string) *Matter {
var wp = &builder.WherePair{Query: "user_uuid = ? AND path = ?", Args: []interface{}{userUuid, path}} var wp = &builder.WherePair{Query: "user_uuid = ? AND path = ?", Args: []interface{}{userUuid, path}}
@ -467,7 +448,6 @@ func (this *MatterDao) findByUserUuidAndPath(userUuid string, path string) *Matt
return matter return matter
} }
//根据userUuid和path来查找
func (this *MatterDao) checkByUserUuidAndPath(userUuid string, path string) *Matter { func (this *MatterDao) checkByUserUuidAndPath(userUuid string, path string) *Matter {
if path == "" { if path == "" {
@ -481,7 +461,6 @@ func (this *MatterDao) checkByUserUuidAndPath(userUuid string, path string) *Mat
return matter return matter
} }
//获取一个文件夹中文件总大小
func (this *MatterDao) SumSizeByUserUuidAndPath(userUuid string, path string) int64 { func (this *MatterDao) SumSizeByUserUuidAndPath(userUuid string, path string) int64 {
var wp = &builder.WherePair{Query: "user_uuid = ? AND path like ?", Args: []interface{}{userUuid, path + "%"}} var wp = &builder.WherePair{Query: "user_uuid = ? AND path like ?", Args: []interface{}{userUuid, path + "%"}}
@ -503,7 +482,6 @@ func (this *MatterDao) SumSizeByUserUuidAndPath(userUuid string, path string) in
} }
//一个文件夹中的数量
func (this *MatterDao) CountByUserUuidAndPath(userUuid string, path string) int64 { func (this *MatterDao) CountByUserUuidAndPath(userUuid string, path string) int64 {
var wp = &builder.WherePair{Query: "user_uuid = ? AND path like ?", Args: []interface{}{userUuid, path + "%"}} var wp = &builder.WherePair{Query: "user_uuid = ? AND path like ?", Args: []interface{}{userUuid, path + "%"}}
@ -516,9 +494,9 @@ func (this *MatterDao) CountByUserUuidAndPath(userUuid string, path string) int6
} }
//执行清理操作 //System cleanup.
func (this *MatterDao) Cleanup() { func (this *MatterDao) Cleanup() {
this.logger.Info("[MatterDao]执行清理清除数据库中所有Matter记录。删除磁盘中所有Matter文件。") this.logger.Info("[MatterDao] clean up. Delete all Matter record in db and on disk.")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Matter{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Matter{})
this.PanicError(db.Error) this.PanicError(db.Error)

View File

@ -7,9 +7,9 @@ import (
) )
const ( const (
//根目录的uuid //root matter's uuid
MATTER_ROOT = "root" MATTER_ROOT = "root"
//cache文件夹名称 //cache directory name.
MATTER_CACHE = "cache" MATTER_CACHE = "cache"
//压缩文件的临时目录 //压缩文件的临时目录
MATTER_ZIP = "zip" MATTER_ZIP = "zip"

View File

@ -32,11 +32,9 @@ type MatterService struct {
preferenceService *PreferenceService preferenceService *PreferenceService
} }
//初始化方法
func (this *MatterService) Init() { func (this *MatterService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.matterDao) b := core.CONTEXT.GetBean(this.matterDao)
if b, ok := b.(*MatterDao); ok { if b, ok := b.(*MatterDao); ok {
this.matterDao = b this.matterDao = b
@ -368,20 +366,6 @@ func (this *MatterService) Upload(request *http.Request, file io.Reader, user *U
return matter return matter
} }
//上传文件
func (this *MatterService) AtomicUpload(request *http.Request, file io.Reader, user *User, dirMatter *Matter, filename string, privacy bool) *Matter {
if user == nil {
panic(result.BadRequest("user cannot be nil."))
}
//操作锁
this.userService.MatterLock(user.Uuid)
defer this.userService.MatterUnlock(user.Uuid)
return this.Upload(request, file, user, dirMatter, filename, privacy)
}
//内部创建文件,不带操作锁。 //内部创建文件,不带操作锁。
func (this *MatterService) createDirectory(request *http.Request, dirMatter *Matter, name string, user *User) *Matter { func (this *MatterService) createDirectory(request *http.Request, dirMatter *Matter, name string, user *User) *Matter {

View File

@ -14,11 +14,9 @@ type PreferenceController struct {
preferenceService *PreferenceService preferenceService *PreferenceService
} }
//初始化方法
func (this *PreferenceController) Init() { func (this *PreferenceController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.preferenceDao) b := core.CONTEXT.GetBean(this.preferenceDao)
if b, ok := b.(*PreferenceDao); ok { if b, ok := b.(*PreferenceDao); ok {
this.preferenceDao = b this.preferenceDao = b
@ -31,12 +29,10 @@ func (this *PreferenceController) Init() {
} }
//注册自己的路由。
func (this *PreferenceController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *PreferenceController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
//每个Controller需要主动注册自己的路由。
routeMap["/api/preference/ping"] = this.Wrap(this.Ping, USER_ROLE_GUEST) routeMap["/api/preference/ping"] = this.Wrap(this.Ping, USER_ROLE_GUEST)
routeMap["/api/preference/fetch"] = this.Wrap(this.Fetch, USER_ROLE_GUEST) routeMap["/api/preference/fetch"] = this.Wrap(this.Fetch, USER_ROLE_GUEST)
routeMap["/api/preference/edit"] = this.Wrap(this.Edit, USER_ROLE_ADMINISTRATOR) routeMap["/api/preference/edit"] = this.Wrap(this.Edit, USER_ROLE_ADMINISTRATOR)

View File

@ -11,7 +11,7 @@ type PreferenceDao struct {
BaseDao BaseDao
} }
//按照Id查询偏好设置 //find by uuid. if not found return nil.
func (this *PreferenceDao) Fetch() *Preference { func (this *PreferenceDao) Fetch() *Preference {
// Read // Read
@ -57,10 +57,10 @@ func (this *PreferenceDao) Save(preference *Preference) *Preference {
return preference return preference
} }
//执行清理操作 //System cleanup.
func (this *PreferenceDao) Cleanup() { func (this *PreferenceDao) Cleanup() {
this.logger.Info("[PreferenceDao]执行清理清除数据库中所有Preference记录。") this.logger.Info("[PreferenceDao] clean up. Delete all Preference")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Preference{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Preference{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -9,11 +9,9 @@ type PreferenceService struct {
preference *Preference preference *Preference
} }
//初始化方法
func (this *PreferenceService) Init() { func (this *PreferenceService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.preferenceDao) b := core.CONTEXT.GetBean(this.preferenceDao)
if b, ok := b.(*PreferenceDao); ok { if b, ok := b.(*PreferenceDao); ok {
this.preferenceDao = b this.preferenceDao = b
@ -38,10 +36,10 @@ func (this *PreferenceService) Reset() {
} }
//执行清理操作 //System cleanup.
func (this *PreferenceService) Cleanup() { func (this *PreferenceService) Cleanup() {
this.logger.Info("[PreferenceService]执行清理重置缓存中的preference。") this.logger.Info("[PreferenceService] clean up. Delete all preference")
this.Reset() this.Reset()
} }

View File

@ -2,6 +2,7 @@ package rest
import ( import (
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/nu7hatch/gouuid" "github.com/nu7hatch/gouuid"
"time" "time"
) )
@ -10,26 +11,27 @@ type SessionDao struct {
BaseDao BaseDao
} }
//按照Id查询session. //find by uuid. if not found return nil.
func (this *SessionDao) FindByUuid(uuid string) *Session { func (this *SessionDao) FindByUuid(uuid string) *Session {
var entity = &Session{}
// Read db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(entity)
var session = &Session{}
db := core.CONTEXT.GetDB().Where(&Session{Base: Base{Uuid: uuid}}).First(session)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else {
panic(db.Error)
} }
return session }
return entity
} }
//按照Id查询session. //find by uuid. if not found panic NotFound error
func (this *SessionDao) CheckByUuid(uuid string) *Session { func (this *SessionDao) CheckByUuid(uuid string) *Session {
entity := this.FindByUuid(uuid)
// Read if entity == nil {
var session = &Session{} panic(result.NotFound("not found record with uuid = %s", uuid))
db := core.CONTEXT.GetDB().Where(&Session{Base: Base{Uuid: uuid}}).First(session) }
this.PanicError(db.Error) return entity
return session
} }
//创建一个session并且持久化到数据库中。 //创建一个session并且持久化到数据库中。
@ -67,9 +69,9 @@ func (this *SessionDao) Delete(uuid string) {
} }
//执行清理操作 //System cleanup.
func (this *SessionDao) Cleanup() { func (this *SessionDao) Cleanup() {
this.logger.Info("[SessionDao]执行清理清除数据库中所有Session记录。") this.logger.Info("[SessionDao] clean up. Delete all Session")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Session{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Session{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -9,11 +9,9 @@ type SessionService struct {
sessionDao *SessionDao sessionDao *SessionDao
} }
//初始化方法
func (this *SessionService) Init() { func (this *SessionService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.userDao) b := core.CONTEXT.GetBean(this.userDao)
if b, ok := b.(*UserDao); ok { if b, ok := b.(*UserDao); ok {
this.userDao = b this.userDao = b
@ -26,10 +24,10 @@ func (this *SessionService) Init() {
} }
//执行清理操作 //System cleanup.
func (this *SessionService) Cleanup() { func (this *SessionService) Cleanup() {
this.logger.Info("[SessionService]执行清理清除缓存中所有Session记录共%d条。", core.CONTEXT.GetSessionCache().Count()) this.logger.Info("[SessionService] clean up. Delete all Session. total:%d", core.CONTEXT.GetSessionCache().Count())
core.CONTEXT.GetSessionCache().Truncate() core.CONTEXT.GetSessionCache().Truncate()
} }

View File

@ -21,11 +21,9 @@ type ShareController struct {
shareService *ShareService shareService *ShareService
} }
//初始化方法
func (this *ShareController) Init() { func (this *ShareController) Init() {
this.BaseController.Init() this.BaseController.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.shareDao) b := core.CONTEXT.GetBean(this.shareDao)
if b, ok := b.(*ShareDao); ok { if b, ok := b.(*ShareDao); ok {
this.shareDao = b this.shareDao = b
@ -53,12 +51,10 @@ func (this *ShareController) Init() {
} }
//注册自己的路由。
func (this *ShareController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *ShareController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
//每个Controller需要主动注册自己的路由。
routeMap["/api/share/create"] = this.Wrap(this.Create, USER_ROLE_USER) routeMap["/api/share/create"] = this.Wrap(this.Create, USER_ROLE_USER)
routeMap["/api/share/delete"] = this.Wrap(this.Delete, USER_ROLE_USER) routeMap["/api/share/delete"] = this.Wrap(this.Delete, USER_ROLE_USER)
routeMap["/api/share/delete/batch"] = this.Wrap(this.DeleteBatch, USER_ROLE_USER) routeMap["/api/share/delete/batch"] = this.Wrap(this.DeleteBatch, USER_ROLE_USER)
@ -170,7 +166,6 @@ func (this *ShareController) Create(writer http.ResponseWriter, request *http.Re
return this.Success(share) return this.Success(share)
} }
//删除一条记录
func (this *ShareController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult { func (this *ShareController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuid := request.FormValue("uuid") uuid := request.FormValue("uuid")
@ -297,7 +292,7 @@ func (this *ShareController) Browse(writer http.ResponseWriter, request *http.Re
user := this.findUser(request) user := this.findUser(request)
share := this.shareService.CheckShare(shareUuid, code, user) share := this.shareService.CheckShare(shareUuid, code, user)
bridges := this.bridgeDao.ListByShareUuid(share.Uuid) bridges := this.bridgeDao.FindByShareUuid(share.Uuid)
if puuid == MATTER_ROOT { if puuid == MATTER_ROOT {
@ -379,7 +374,7 @@ func (this *ShareController) Zip(writer http.ResponseWriter, request *http.Reque
//下载分享全部内容。 //下载分享全部内容。
share := this.shareService.CheckShare(shareUuid, code, user) share := this.shareService.CheckShare(shareUuid, code, user)
bridges := this.bridgeDao.ListByShareUuid(share.Uuid) bridges := this.bridgeDao.FindByShareUuid(share.Uuid)
var matterUuids []string var matterUuids []string
for _, bridge := range bridges { for _, bridge := range bridges {
matterUuids = append(matterUuids, bridge.MatterUuid) matterUuids = append(matterUuids, bridge.MatterUuid)

View File

@ -3,6 +3,7 @@ package rest
import ( import (
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/builder" "github.com/eyebluecn/tank/code/tool/builder"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/nu7hatch/gouuid" "github.com/nu7hatch/gouuid"
@ -13,28 +14,27 @@ type ShareDao struct {
BaseDao BaseDao
} }
//按照Id查询文件 //find by uuid. if not found return nil.
func (this *ShareDao) FindByUuid(uuid string) *Share { func (this *ShareDao) FindByUuid(uuid string) *Share {
var entity = &Share{}
// Read db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(entity)
var share Share
db := core.CONTEXT.GetDB().Where(&Share{Base: Base{Uuid: uuid}}).First(&share)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else {
panic(db.Error)
} }
return &share }
return entity
} }
//按照Id查询文件 //find by uuid. if not found panic NotFound error
func (this *ShareDao) CheckByUuid(uuid string) *Share { func (this *ShareDao) CheckByUuid(uuid string) *Share {
entity := this.FindByUuid(uuid)
// Read if entity == nil {
var share Share panic(result.NotFound("not found record with uuid = %s", uuid))
db := core.CONTEXT.GetDB().Where(&Share{Base: Base{Uuid: uuid}}).First(&share) }
this.PanicError(db.Error) return entity
return &share
} }
//按分页条件获取分页 //按分页条件获取分页
@ -85,7 +85,6 @@ func (this *ShareDao) Save(share *Share) *Share {
return share return share
} }
//删除一条记录
func (this *ShareDao) Delete(share *Share) { func (this *ShareDao) Delete(share *Share) {
db := core.CONTEXT.GetDB().Delete(&share) db := core.CONTEXT.GetDB().Delete(&share)
@ -93,9 +92,9 @@ func (this *ShareDao) Delete(share *Share) {
} }
//执行清理操作 //System cleanup.
func (this *ShareDao) Cleanup() { func (this *ShareDao) Cleanup() {
this.logger.Info("[ShareDao]执行清理清除数据库中所有Share记录。") this.logger.Info("[ShareDao] clean up. Delete all Share")
db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Share{}) db := core.CONTEXT.GetDB().Where("uuid is not null").Delete(Share{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -16,11 +16,9 @@ type ShareService struct {
userDao *UserDao userDao *UserDao
} }
//初始化方法
func (this *ShareService) Init() { func (this *ShareService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.shareDao) b := core.CONTEXT.GetBean(this.shareDao)
if b, ok := b.(*ShareDao); ok { if b, ok := b.(*ShareDao); ok {
this.shareDao = b this.shareDao = b

View File

@ -2,6 +2,7 @@ package rest
import ( import (
"github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/nu7hatch/gouuid" "github.com/nu7hatch/gouuid"
"time" "time"
) )
@ -10,18 +11,27 @@ type UploadTokenDao struct {
BaseDao BaseDao
} }
//按照Id查询 //find by uuid. if not found return nil.
func (this *UploadTokenDao) FindByUuid(uuid string) *UploadToken { func (this *UploadTokenDao) FindByUuid(uuid string) *UploadToken {
var entity = &UploadToken{}
// Read db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(entity)
var uploadToken = &UploadToken{}
db := core.CONTEXT.GetDB().Where(&UploadToken{Base: Base{Uuid: uuid}}).First(uploadToken)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else {
panic(db.Error)
}
}
return entity
} }
return uploadToken //find by uuid. if not found panic NotFound error
func (this *UploadTokenDao) CheckByUuid(uuid string) *UploadToken {
entity := this.FindByUuid(uuid)
if entity == nil {
panic(result.NotFound("not found record with uuid = %s", uuid))
}
return entity
} }
//创建一个session并且持久化到数据库中。 //创建一个session并且持久化到数据库中。

View File

@ -17,7 +17,6 @@ type UserController struct {
preferenceService *PreferenceService preferenceService *PreferenceService
} }
//初始化方法
func (this *UserController) Init() { func (this *UserController) Init() {
this.BaseController.Init() this.BaseController.Init()
@ -27,12 +26,10 @@ func (this *UserController) Init() {
} }
} }
//注册自己的路由。
func (this *UserController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) { func (this *UserController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request)) routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
//每个Controller需要主动注册自己的路由。
routeMap["/api/user/login"] = this.Wrap(this.Login, USER_ROLE_GUEST) routeMap["/api/user/login"] = this.Wrap(this.Login, USER_ROLE_GUEST)
routeMap["/api/user/authentication/login"] = this.Wrap(this.AuthenticationLogin, USER_ROLE_GUEST) routeMap["/api/user/authentication/login"] = this.Wrap(this.AuthenticationLogin, USER_ROLE_GUEST)
routeMap["/api/user/register"] = this.Wrap(this.Register, USER_ROLE_GUEST) routeMap["/api/user/register"] = this.Wrap(this.Register, USER_ROLE_GUEST)

View File

@ -12,7 +12,6 @@ type UserDao struct {
BaseDao BaseDao
} }
//初始化方法
func (this *UserDao) Init() { func (this *UserDao) Init() {
this.BaseDao.Init() this.BaseDao.Init()
} }
@ -37,30 +36,27 @@ func (this *UserDao) Create(user *User) *User {
return user return user
} }
//按照Id查询用户找不到返回nil //find by uuid. if not found return nil.
func (this *UserDao) FindByUuid(uuid string) *User { func (this *UserDao) FindByUuid(uuid string) *User {
var entity = &User{}
// Read db := core.CONTEXT.GetDB().Where("uuid = ?", uuid).First(entity)
var user *User = &User{}
db := core.CONTEXT.GetDB().Where(&User{Base: Base{Uuid: uuid}}).First(user)
if db.Error != nil { if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil return nil
} else {
panic(db.Error)
} }
return user }
return entity
} }
//按照Id查询用户,找不到抛panic //find by uuid. if not found panic NotFound error
func (this *UserDao) CheckByUuid(uuid string) *User { func (this *UserDao) CheckByUuid(uuid string) *User {
entity := this.FindByUuid(uuid)
if uuid == "" { if entity == nil {
panic("uuid必须指定") panic(result.NotFound("not found record with uuid = %s", uuid))
} }
return entity
// Read
var user = &User{}
db := core.CONTEXT.GetDB().Where(&User{Base: Base{Uuid: uuid}}).First(user)
this.PanicError(db.Error)
return user
} }
//查询用户。 //查询用户。
@ -139,9 +135,9 @@ func (this *UserDao) Save(user *User) *User {
return user return user
} }
//执行清理操作 //System cleanup.
func (this *UserDao) Cleanup() { func (this *UserDao) Cleanup() {
this.logger.Info("[UserDao]执行清理清除数据库中所有User记录。") this.logger.Info("[UserDao] clean up. Delete all User")
db := core.CONTEXT.GetDB().Where("uuid is not null and role != ?", USER_ROLE_ADMINISTRATOR).Delete(User{}) db := core.CONTEXT.GetDB().Where("uuid is not null and role != ?", USER_ROLE_ADMINISTRATOR).Delete(User{})
this.PanicError(db.Error) this.PanicError(db.Error)
} }

View File

@ -20,11 +20,9 @@ type UserService struct {
locker *cache.Table locker *cache.Table
} }
//初始化方法
func (this *UserService) Init() { func (this *UserService) Init() {
this.BaseBean.Init() this.BaseBean.Init()
//手动装填本实例的Bean. 这里必须要用中间变量方可。
b := core.CONTEXT.GetBean(this.userDao) b := core.CONTEXT.GetBean(this.userDao)
if b, ok := b.(*UserDao); ok { if b, ok := b.(*UserDao); ok {
this.userDao = b this.userDao = b

View File

@ -23,23 +23,18 @@ type CodeWrapper struct {
} }
var ( var (
OK = &CodeWrapper{Code: "OK", HttpStatus: http.StatusOK, Description: "成功"} OK = &CodeWrapper{Code: "OK", HttpStatus: http.StatusOK, Description: "ok"}
BAD_REQUEST = &CodeWrapper{Code: "BAD_REQUEST", HttpStatus: http.StatusBadRequest, Description: "请求不合法"} BAD_REQUEST = &CodeWrapper{Code: "BAD_REQUEST", HttpStatus: http.StatusBadRequest, Description: "bad request"}
CAPTCHA_ERROR = &CodeWrapper{Code: "CAPTCHA_ERROR", HttpStatus: http.StatusBadRequest, Description: "验证码错误"} NEED_SHARE_CODE = &CodeWrapper{Code: "NEED_SHARE_CODE", HttpStatus: http.StatusUnauthorized, Description: "share code required"}
NEED_CAPTCHA = &CodeWrapper{Code: "NEED_CAPTCHA", HttpStatus: http.StatusBadRequest, Description: "验证码必填"} SHARE_CODE_ERROR = &CodeWrapper{Code: "SHARE_CODE_ERROR", HttpStatus: http.StatusUnauthorized, Description: "share code error"}
NEED_SHARE_CODE = &CodeWrapper{Code: "NEED_SHARE_CODE", HttpStatus: http.StatusUnauthorized, Description: "分享提取码必填"} LOGIN = &CodeWrapper{Code: "LOGIN", HttpStatus: http.StatusUnauthorized, Description: "not login"}
SHARE_CODE_ERROR = &CodeWrapper{Code: "SHARE_CODE_ERROR", HttpStatus: http.StatusUnauthorized, Description: "分享提取码错误"} USER_DISABLED = &CodeWrapper{Code: "USER_DISABLED", HttpStatus: http.StatusForbidden, Description: "user disabled"}
USERNAME_PASSWORD_ERROR = &CodeWrapper{Code: "USERNAME_PASSWORD_ERROR", HttpStatus: http.StatusBadRequest, Description: "用户名或密码错误"} UNAUTHORIZED = &CodeWrapper{Code: "UNAUTHORIZED", HttpStatus: http.StatusUnauthorized, Description: "unauthorized"}
PARAMS_ERROR = &CodeWrapper{Code: "PARAMS_ERROR", HttpStatus: http.StatusBadRequest, Description: "用户名或密码错误"} NOT_FOUND = &CodeWrapper{Code: "NOT_FOUND", HttpStatus: http.StatusNotFound, Description: "404 not found"}
LOGIN = &CodeWrapper{Code: "LOGIN", HttpStatus: http.StatusUnauthorized, Description: "未登录,禁止访问"} RANGE_NOT_SATISFIABLE = &CodeWrapper{Code: "RANGE_NOT_SATISFIABLE", HttpStatus: http.StatusRequestedRangeNotSatisfiable, Description: "range not satisfiable"}
LOGIN_EXPIRE = &CodeWrapper{Code: "LOGIN_EXPIRE", HttpStatus: http.StatusUnauthorized, Description: "登录过期,请重新登录"} NOT_INSTALLED = &CodeWrapper{Code: "NOT_INSTALLED", HttpStatus: http.StatusInternalServerError, Description: "application not installed"}
USER_DISABLED = &CodeWrapper{Code: "USER_DISABLED", HttpStatus: http.StatusForbidden, Description: "账户被禁用,禁止访问"} SERVER = &CodeWrapper{Code: "SERVER", HttpStatus: http.StatusInternalServerError, Description: "server error"}
UNAUTHORIZED = &CodeWrapper{Code: "UNAUTHORIZED", HttpStatus: http.StatusUnauthorized, Description: "没有权限,禁止访问"} UNKNOWN = &CodeWrapper{Code: "UNKNOWN", HttpStatus: http.StatusInternalServerError, Description: "server unknow error"}
NOT_FOUND = &CodeWrapper{Code: "NOT_FOUND", HttpStatus: http.StatusNotFound, Description: "内容不存在"}
RANGE_NOT_SATISFIABLE = &CodeWrapper{Code: "RANGE_NOT_SATISFIABLE", HttpStatus: http.StatusRequestedRangeNotSatisfiable, Description: "文件范围读取错误"}
NOT_INSTALLED = &CodeWrapper{Code: "NOT_INSTALLED", HttpStatus: http.StatusInternalServerError, Description: "系统尚未安装"}
SERVER = &CodeWrapper{Code: "SERVER", HttpStatus: http.StatusInternalServerError, Description: "服务器出错"}
UNKNOWN = &CodeWrapper{Code: "UNKNOWN", HttpStatus: http.StatusInternalServerError, Description: "服务器未知错误"}
) )
//根据 CodeWrapper来获取对应的HttpStatus //根据 CodeWrapper来获取对应的HttpStatus
@ -48,22 +43,12 @@ func FetchHttpStatus(code string) int {
return OK.HttpStatus return OK.HttpStatus
} else if code == BAD_REQUEST.Code { } else if code == BAD_REQUEST.Code {
return BAD_REQUEST.HttpStatus return BAD_REQUEST.HttpStatus
} else if code == CAPTCHA_ERROR.Code {
return CAPTCHA_ERROR.HttpStatus
} else if code == NEED_CAPTCHA.Code {
return NEED_CAPTCHA.HttpStatus
} else if code == NEED_SHARE_CODE.Code { } else if code == NEED_SHARE_CODE.Code {
return NEED_SHARE_CODE.HttpStatus return NEED_SHARE_CODE.HttpStatus
} else if code == SHARE_CODE_ERROR.Code { } else if code == SHARE_CODE_ERROR.Code {
return SHARE_CODE_ERROR.HttpStatus return SHARE_CODE_ERROR.HttpStatus
} else if code == USERNAME_PASSWORD_ERROR.Code {
return USERNAME_PASSWORD_ERROR.HttpStatus
} else if code == PARAMS_ERROR.Code {
return PARAMS_ERROR.HttpStatus
} else if code == LOGIN.Code { } else if code == LOGIN.Code {
return LOGIN.HttpStatus return LOGIN.HttpStatus
} else if code == LOGIN_EXPIRE.Code {
return LOGIN_EXPIRE.HttpStatus
} else if code == USER_DISABLED.Code { } else if code == USER_DISABLED.Code {
return USER_DISABLED.HttpStatus return USER_DISABLED.HttpStatus
} else if code == UNAUTHORIZED.Code { } else if code == UNAUTHORIZED.Code {