Refine the rest structure.
This commit is contained in:
426
code/rest/alien_controller.go
Normal file
426
code/rest/alien_controller.go
Normal file
@ -0,0 +1,426 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
type AlienController struct {
|
||||
BaseController
|
||||
uploadTokenDao *UploadTokenDao
|
||||
downloadTokenDao *DownloadTokenDao
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
alienService *AlienService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *AlienController) Init() {
|
||||
this.BaseController.Init()
|
||||
|
||||
//手动装填本实例的Bean.
|
||||
b := CONTEXT.GetBean(this.uploadTokenDao)
|
||||
if c, ok := b.(*UploadTokenDao); ok {
|
||||
this.uploadTokenDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.downloadTokenDao)
|
||||
if c, ok := b.(*DownloadTokenDao); ok {
|
||||
this.downloadTokenDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterDao)
|
||||
if c, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterService)
|
||||
if c, ok := b.(*MatterService); ok {
|
||||
this.matterService = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheDao)
|
||||
if c, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheService)
|
||||
if c, ok := b.(*ImageCacheService); ok {
|
||||
this.imageCacheService = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.alienService)
|
||||
if c, ok := b.(*AlienService); ok {
|
||||
this.alienService = c
|
||||
}
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *AlienController) RegisterRoutes() 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_GUEST)
|
||||
routeMap["/api/alien/fetch/download/token"] = this.Wrap(this.FetchDownloadToken, USER_ROLE_GUEST)
|
||||
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/crawl/token"] = this.Wrap(this.CrawlToken, USER_ROLE_GUEST)
|
||||
routeMap["/api/alien/crawl/direct"] = this.Wrap(this.CrawlDirect, USER_ROLE_GUEST)
|
||||
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//处理一些特殊的接口,比如参数包含在路径中,一般情况下,controller不将参数放在url路径中
|
||||
func (this *AlienController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
|
||||
|
||||
path := request.URL.Path
|
||||
|
||||
//匹配 /api/alien/preview/{uuid}/{filename} (响应头不包含 content-disposition)
|
||||
reg := regexp.MustCompile(`^/api/alien/preview/([^/]+)/([^/]+)$`)
|
||||
strs := reg.FindStringSubmatch(path)
|
||||
if len(strs) == 3 {
|
||||
var f = func(writer http.ResponseWriter, request *http.Request) {
|
||||
this.Preview(writer, request, strs[1], strs[2])
|
||||
}
|
||||
return f, true
|
||||
}
|
||||
|
||||
//匹配 /api/alien/download/{uuid}/{filename} (响应头包含 content-disposition)
|
||||
reg = regexp.MustCompile(`^/api/alien/download/([^/]+)/([^/]+)$`)
|
||||
strs = reg.FindStringSubmatch(path)
|
||||
if len(strs) == 3 {
|
||||
var f = func(writer http.ResponseWriter, request *http.Request) {
|
||||
this.Download(writer, request, strs[1], strs[2])
|
||||
}
|
||||
return f, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
//直接从cookie中获取用户信息,或者使用邮箱和密码获取用户
|
||||
func (this *AlienController) CheckRequestUser(writer http.ResponseWriter, request *http.Request) *User {
|
||||
|
||||
//根据用户登录信息取
|
||||
user := this.findUser(writer, request)
|
||||
if user != nil {
|
||||
return user;
|
||||
}
|
||||
|
||||
email := request.FormValue("email")
|
||||
if email == "" {
|
||||
panic("邮箱必填啦")
|
||||
}
|
||||
|
||||
password := request.FormValue("password")
|
||||
if password == "" {
|
||||
panic("密码必填")
|
||||
}
|
||||
|
||||
//验证用户身份合法性。
|
||||
user = this.userDao.FindByEmail(email)
|
||||
if user == nil {
|
||||
panic(`邮箱或密码错误`)
|
||||
} else {
|
||||
if !util.MatchBcrypt(password, user.Password) {
|
||||
panic(`邮箱或密码错误`)
|
||||
}
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
//系统中的用户x要获取一个UploadToken,用于提供给x信任的用户上传文件。
|
||||
func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
//文件名。
|
||||
filename := request.FormValue("filename")
|
||||
if filename == "" {
|
||||
panic("文件名必填")
|
||||
} else if m, _ := regexp.MatchString(`[<>|*?/\\]`, filename); m {
|
||||
panic(fmt.Sprintf(`【%s】不符合要求,文件名中不能包含以下特殊符号:< > | * ? / \`, filename))
|
||||
}
|
||||
|
||||
//什么时间后过期,默认24h
|
||||
expireStr := request.FormValue("expire")
|
||||
expire := 24 * 60 * 60
|
||||
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 {
|
||||
if privacyStr == TRUE {
|
||||
privacy = true
|
||||
} else if privacyStr == "false" {
|
||||
privacy = false
|
||||
} else {
|
||||
panic(`文件公有性不符合规范`)
|
||||
}
|
||||
}
|
||||
|
||||
//文件大小
|
||||
sizeStr := request.FormValue("size")
|
||||
var size int64
|
||||
if sizeStr == "" {
|
||||
panic(`文件大小必填`)
|
||||
} else {
|
||||
|
||||
var err error
|
||||
size, err = strconv.ParseInt(sizeStr, 10, 64)
|
||||
if err != nil {
|
||||
panic(`文件大小不符合规范`)
|
||||
}
|
||||
if size < 1 {
|
||||
panic(`文件大小不符合规范`)
|
||||
}
|
||||
}
|
||||
|
||||
//文件夹路径,以 / 开头。
|
||||
dir := request.FormValue("dir")
|
||||
|
||||
user := this.CheckRequestUser(writer, request)
|
||||
dirMatter := this.matterService.CreateDirectories(user, dir)
|
||||
|
||||
mm, _ := time.ParseDuration(fmt.Sprintf("%ds", expire))
|
||||
uploadToken := &UploadToken{
|
||||
UserUuid: user.Uuid,
|
||||
FolderUuid: dirMatter.Uuid,
|
||||
MatterUuid: "",
|
||||
ExpireTime: time.Now().Add(mm),
|
||||
Filename: filename,
|
||||
Privacy: privacy,
|
||||
Size: size,
|
||||
Ip: util.GetIpAddress(request),
|
||||
}
|
||||
|
||||
uploadToken = this.uploadTokenDao.Create(uploadToken)
|
||||
|
||||
return this.Success(uploadToken)
|
||||
|
||||
}
|
||||
|
||||
//系统中的用户x 拿着某个文件的uuid来确认是否其信任的用户已经上传好了。
|
||||
func (this *AlienController) Confirm(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
matterUuid := request.FormValue("matterUuid")
|
||||
if matterUuid == "" {
|
||||
panic("matterUuid必填")
|
||||
}
|
||||
|
||||
user := this.CheckRequestUser(writer, request)
|
||||
|
||||
matter := this.matterDao.CheckByUuid(matterUuid)
|
||||
if matter.UserUuid != user.Uuid {
|
||||
panic("文件不属于你")
|
||||
}
|
||||
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//系统中的用户x 信任的用户上传文件。这个接口需要支持跨域。
|
||||
func (this *AlienController) Upload(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
//允许跨域请求。
|
||||
this.allowCORS(writer)
|
||||
if request.Method == "OPTIONS" {
|
||||
return this.Success("OK")
|
||||
}
|
||||
|
||||
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")
|
||||
this.PanicError(err)
|
||||
defer func() {
|
||||
e := file.Close()
|
||||
this.PanicError(e)
|
||||
}()
|
||||
|
||||
if handler.Filename != uploadToken.Filename {
|
||||
panic("文件名称不正确")
|
||||
}
|
||||
|
||||
if handler.Size != uploadToken.Size {
|
||||
panic("文件大小不正确")
|
||||
}
|
||||
|
||||
dirMatter := this.matterDao.CheckWithRootByUuid(uploadToken.FolderUuid, user)
|
||||
|
||||
matter := this.matterService.AtomicUpload(file, user, dirMatter, uploadToken.Filename, uploadToken.Privacy)
|
||||
|
||||
//更新这个uploadToken的信息.
|
||||
uploadToken.ExpireTime = time.Now()
|
||||
this.uploadTokenDao.Save(uploadToken)
|
||||
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//给一个指定的url,从该url中去拉取文件回来。此处采用uploadToken的模式。
|
||||
func (this *AlienController) CrawlToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
//允许跨域请求。
|
||||
this.allowCORS(writer)
|
||||
if request.Method == "OPTIONS" {
|
||||
return this.Success("OK")
|
||||
}
|
||||
|
||||
uploadTokenUuid := request.FormValue("uploadTokenUuid")
|
||||
url := request.FormValue("url")
|
||||
|
||||
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)
|
||||
|
||||
dirMatter := this.matterDao.CheckWithRootByUuid(uploadToken.FolderUuid, user)
|
||||
|
||||
matter := this.matterService.AtomicCrawl(url, uploadToken.Filename, user, dirMatter, uploadToken.Privacy)
|
||||
|
||||
//更新这个uploadToken的信息.
|
||||
uploadToken.ExpireTime = time.Now()
|
||||
this.uploadTokenDao.Save(uploadToken)
|
||||
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//通过一个url直接上传,无需借助uploadToken.
|
||||
func (this *AlienController) CrawlDirect(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
//文件名。
|
||||
filename := request.FormValue("filename")
|
||||
//文件公有或私有
|
||||
privacyStr := request.FormValue("privacy")
|
||||
//文件夹路径,以 / 开头。
|
||||
dir := request.FormValue("dir")
|
||||
url := request.FormValue("url")
|
||||
|
||||
if filename == "" {
|
||||
panic("文件名必填")
|
||||
} else if m, _ := regexp.MatchString(`[<>|*?/\\]`, filename); m {
|
||||
panic(fmt.Sprintf(`【%s】不符合要求,文件名中不能包含以下特殊符号:< > | * ? / \`, filename))
|
||||
}
|
||||
|
||||
var privacy bool
|
||||
if privacyStr == "" {
|
||||
panic(`文件公有性必填`)
|
||||
} else {
|
||||
if privacyStr == TRUE {
|
||||
privacy = true
|
||||
} else if privacyStr == FALSE {
|
||||
privacy = false
|
||||
} else {
|
||||
panic(`文件公有性不符合规范`)
|
||||
}
|
||||
}
|
||||
|
||||
user := this.CheckRequestUser(writer, request)
|
||||
dirMatter := this.matterService.CreateDirectories(user, dir)
|
||||
|
||||
matter := this.matterService.AtomicCrawl(url, filename, user, dirMatter, privacy)
|
||||
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//系统中的用户x要获取一个DownloadToken,用于提供给x信任的用户下载文件。
|
||||
func (this *AlienController) FetchDownloadToken(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
matterUuid := request.FormValue("matterUuid")
|
||||
if matterUuid == "" {
|
||||
panic("matterUuid必填")
|
||||
}
|
||||
|
||||
user := this.CheckRequestUser(writer, request)
|
||||
|
||||
matter := this.matterDao.CheckByUuid(matterUuid)
|
||||
if matter.UserUuid != user.Uuid {
|
||||
panic("文件不属于你")
|
||||
}
|
||||
if matter.Dir {
|
||||
panic("不支持下载文件夹")
|
||||
}
|
||||
|
||||
//什么时间后过期,默认24h
|
||||
expireStr := request.FormValue("expire")
|
||||
expire := 24 * 60 * 60
|
||||
if expireStr != "" {
|
||||
var err error
|
||||
expire, err = strconv.Atoi(expireStr)
|
||||
if err != nil {
|
||||
panic(`过期时间不符合规范`)
|
||||
}
|
||||
if expire < 1 {
|
||||
panic(`过期时间不符合规范`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mm, _ := time.ParseDuration(fmt.Sprintf("%ds", expire))
|
||||
downloadToken := &DownloadToken{
|
||||
UserUuid: user.Uuid,
|
||||
MatterUuid: matterUuid,
|
||||
ExpireTime: time.Now().Add(mm),
|
||||
Ip: util.GetIpAddress(request),
|
||||
}
|
||||
|
||||
downloadToken = this.downloadTokenDao.Create(downloadToken)
|
||||
|
||||
return this.Success(downloadToken)
|
||||
|
||||
}
|
||||
|
||||
//预览一个文件。既可以使用登录的方式,也可以使用授权的方式
|
||||
func (this *AlienController) Preview(writer http.ResponseWriter, request *http.Request, uuid string, filename string) {
|
||||
|
||||
this.alienService.PreviewOrDownload(writer, request, uuid, filename, false)
|
||||
}
|
||||
|
||||
//下载一个文件。既可以使用登录的方式,也可以使用授权的方式
|
||||
func (this *AlienController) Download(writer http.ResponseWriter, request *http.Request, uuid string, filename string) {
|
||||
|
||||
this.alienService.PreviewOrDownload(writer, request, uuid, filename, true)
|
||||
}
|
141
code/rest/alien_service.go
Normal file
141
code/rest/alien_service.go
Normal file
@ -0,0 +1,141 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
//@Service
|
||||
type AlienService struct {
|
||||
Bean
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
userDao *UserDao
|
||||
uploadTokenDao *UploadTokenDao
|
||||
downloadTokenDao *DownloadTokenDao
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *AlienService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.matterDao)
|
||||
if b, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterService)
|
||||
if b, ok := b.(*MatterService); ok {
|
||||
this.matterService = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.userDao)
|
||||
if b, ok := b.(*UserDao); ok {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.uploadTokenDao)
|
||||
if c, ok := b.(*UploadTokenDao); ok {
|
||||
this.uploadTokenDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.downloadTokenDao)
|
||||
if c, ok := b.(*DownloadTokenDao); ok {
|
||||
this.downloadTokenDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheDao)
|
||||
if c, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheService)
|
||||
if c, ok := b.(*ImageCacheService); ok {
|
||||
this.imageCacheService = c
|
||||
}
|
||||
}
|
||||
|
||||
//预览或者下载的统一处理.
|
||||
func (this *AlienService) PreviewOrDownload(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
uuid string,
|
||||
filename string,
|
||||
withContentDisposition bool) {
|
||||
|
||||
matter := this.matterDao.CheckByUuid(uuid)
|
||||
|
||||
//判断是否是文件夹
|
||||
if matter.Dir {
|
||||
panic("不支持下载文件夹")
|
||||
}
|
||||
|
||||
if matter.Name != filename {
|
||||
panic("文件信息错误")
|
||||
}
|
||||
|
||||
//验证用户的权限问题。
|
||||
//文件如果是私有的才需要权限
|
||||
if matter.Privacy {
|
||||
|
||||
//1.如果带有downloadTokenUuid那么就按照token的信息去获取。
|
||||
downloadTokenUuid := request.FormValue("downloadTokenUuid")
|
||||
if downloadTokenUuid != "" {
|
||||
downloadToken := this.downloadTokenDao.CheckByUuid(downloadTokenUuid)
|
||||
if downloadToken.ExpireTime.Before(time.Now()) {
|
||||
panic("downloadToken已失效")
|
||||
}
|
||||
|
||||
if downloadToken.MatterUuid != uuid {
|
||||
panic("token和文件信息不一致")
|
||||
}
|
||||
|
||||
tokenUser := this.userDao.CheckByUuid(downloadToken.UserUuid)
|
||||
if matter.UserUuid != tokenUser.Uuid {
|
||||
panic(result.CODE_WRAPPER_UNAUTHORIZED)
|
||||
}
|
||||
|
||||
//下载之后立即过期掉。如果是分块下载的,必须以最终获取到完整的数据为准。
|
||||
downloadToken.ExpireTime = time.Now()
|
||||
this.downloadTokenDao.Save(downloadToken)
|
||||
|
||||
} else {
|
||||
|
||||
//判断文件的所属人是否正确
|
||||
operator := this.findUser(writer, request)
|
||||
if operator == nil || (operator.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != operator.Uuid) {
|
||||
panic(result.CODE_WRAPPER_UNAUTHORIZED)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//对图片处理。
|
||||
needProcess, imageResizeM, imageResizeW, imageResizeH := this.imageCacheService.ResizeParams(request)
|
||||
if needProcess {
|
||||
|
||||
//如果是图片,那么能用缓存就用缓存
|
||||
imageCache := this.imageCacheDao.FindByMatterUuidAndMode(matter.Uuid, fmt.Sprintf("%s_%d_%d", imageResizeM, imageResizeW, imageResizeH))
|
||||
if imageCache == nil {
|
||||
imageCache = this.imageCacheService.cacheImage(writer, request, matter)
|
||||
}
|
||||
|
||||
//直接使用缓存中的信息
|
||||
this.matterService.DownloadFile(writer, request, GetUserCacheRootDir(imageCache.Username)+imageCache.Path, imageCache.Name, withContentDisposition)
|
||||
|
||||
} else {
|
||||
this.matterService.DownloadFile(writer, request, matter.AbsolutePath(), matter.Name, withContentDisposition)
|
||||
}
|
||||
|
||||
//文件下载次数加一,为了加快访问速度,异步进行
|
||||
go util.SafeMethod(func() {
|
||||
this.matterDao.TimesIncrement(uuid)
|
||||
})
|
||||
|
||||
}
|
126
code/rest/base_controller.go
Normal file
126
code/rest/base_controller.go
Normal file
@ -0,0 +1,126 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/json-iterator/go"
|
||||
"go/types"
|
||||
"net/http"
|
||||
"tank/code/tool/result"
|
||||
)
|
||||
|
||||
type IController interface {
|
||||
IBean
|
||||
//注册自己固定的路由。
|
||||
RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request)
|
||||
//处理一些特殊的路由。
|
||||
HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool)
|
||||
}
|
||||
type BaseController struct {
|
||||
Bean
|
||||
userDao *UserDao
|
||||
sessionDao *SessionDao
|
||||
}
|
||||
|
||||
func (this *BaseController) Init() {
|
||||
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean.
|
||||
b := CONTEXT.GetBean(this.userDao)
|
||||
if b, ok := b.(*UserDao); ok {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.sessionDao)
|
||||
if b, ok := b.(*SessionDao); ok {
|
||||
this.sessionDao = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
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))
|
||||
}
|
||||
|
||||
//处理一些特殊的接口,比如参数包含在路径中,一般情况下,controller不将参数放在url路径中
|
||||
func (this *BaseController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
//需要进行登录验证的wrap包装
|
||||
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) {
|
||||
|
||||
//writer和request赋值给自己。
|
||||
|
||||
var webResult *result.WebResult = nil
|
||||
|
||||
//只有游客接口不需要登录
|
||||
if qualifiedRole != USER_ROLE_GUEST {
|
||||
user := this.checkUser(writer, request)
|
||||
|
||||
if user.Status == USER_STATUS_DISABLED {
|
||||
//判断用户是否被禁用。
|
||||
webResult = result.ConstWebResult(result.CODE_WRAPPER_USER_DISABLED)
|
||||
} else {
|
||||
if qualifiedRole == USER_ROLE_ADMINISTRATOR && user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
webResult = result.ConstWebResult(result.CODE_WRAPPER_UNAUTHORIZED)
|
||||
} else {
|
||||
webResult = f(writer, request)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
webResult = f(writer, request)
|
||||
}
|
||||
|
||||
//输出的是json格式
|
||||
if webResult != nil {
|
||||
//返回的内容申明是json,utf-8
|
||||
writer.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
||||
|
||||
//用json的方式输出返回值。
|
||||
b, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(webResult)
|
||||
|
||||
this.PanicError(err)
|
||||
|
||||
writer.WriteHeader(result.FetchHttpStatus(webResult.Code))
|
||||
|
||||
_, err = fmt.Fprintf(writer, string(b))
|
||||
this.PanicError(err)
|
||||
} else {
|
||||
//输出的内容是二进制的。
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//返回成功的结果。支持放置三种类型 1.字符串 2. WebResult对象 3.空指针 4.任意类型
|
||||
func (this *BaseController) Success(data interface{}) *result.WebResult {
|
||||
var webResult *result.WebResult = nil
|
||||
if value, ok := data.(string); ok {
|
||||
//返回一句普通的消息
|
||||
webResult = &result.WebResult{Code: result.CODE_WRAPPER_OK.Code, Msg: value}
|
||||
} else if value, ok := data.(*result.WebResult); ok {
|
||||
//返回一个webResult对象
|
||||
webResult = value
|
||||
} else if _, ok := data.(types.Nil); ok {
|
||||
//返回一个空指针
|
||||
webResult = result.ConstWebResult(result.CODE_WRAPPER_OK)
|
||||
} else {
|
||||
//返回的类型不明确。
|
||||
webResult = &result.WebResult{Code: result.CODE_WRAPPER_OK.Code, Data: data}
|
||||
}
|
||||
return webResult
|
||||
}
|
||||
|
||||
//允许跨域请求
|
||||
func (this *BaseController) allowCORS(writer http.ResponseWriter) {
|
||||
writer.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
writer.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
|
||||
writer.Header().Add("Access-Control-Max-Age", "3600")
|
||||
}
|
26
code/rest/base_dao.go
Normal file
26
code/rest/base_dao.go
Normal file
@ -0,0 +1,26 @@
|
||||
package rest
|
||||
|
||||
import "tank/code/tool/builder"
|
||||
|
||||
type BaseDao struct {
|
||||
Bean
|
||||
}
|
||||
|
||||
//根据一个sortMap,获取到order字符串
|
||||
func (this *BaseDao) GetSortString(sortArray []builder.OrderPair) string {
|
||||
|
||||
if sortArray == nil || len(sortArray) == 0 {
|
||||
return ""
|
||||
}
|
||||
str := ""
|
||||
for _, pair := range sortArray {
|
||||
if pair.Value == "DESC" || pair.Value == "ASC" {
|
||||
if str != "" {
|
||||
str = str + ","
|
||||
}
|
||||
str = str + " " + pair.Key + " " + pair.Value
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
64
code/rest/base_model.go
Normal file
64
code/rest/base_model.go
Normal file
@ -0,0 +1,64 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"tank/code/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TRUE = "true"
|
||||
FALSE = "false"
|
||||
)
|
||||
|
||||
type Time time.Time
|
||||
|
||||
type IBase interface {
|
||||
//返回其对应的数据库表名
|
||||
TableName() string
|
||||
}
|
||||
|
||||
//Mysql 5.5只支持一个CURRENT_TIMESTAMP的默认值,因此时间的默认值都使用蓝眼云盘第一个发布版本时间 2018-01-01 00:00:00
|
||||
type Base struct {
|
||||
Uuid string `json:"uuid" gorm:"type:char(36);primary_key;unique"`
|
||||
Sort int64 `json:"sort" gorm:"type:bigint(20) not null"`
|
||||
UpdateTime time.Time `json:"updateTime" gorm:"type:timestamp not null;default:CURRENT_TIMESTAMP"`
|
||||
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 {
|
||||
return config.TABLE_PREFIX + "base"
|
||||
}
|
||||
|
||||
//分页类
|
||||
type Pager struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
TotalItems int `json:"totalItems"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func NewPager(page int, pageSize int, totalItems int, data interface{}) *Pager {
|
||||
|
||||
return &Pager{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalItems: totalItems,
|
||||
TotalPages: int(math.Ceil(float64(totalItems) / float64(pageSize))),
|
||||
Data: data,
|
||||
}
|
||||
}
|
85
code/rest/bean.go
Normal file
85
code/rest/bean.go
Normal file
@ -0,0 +1,85 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"tank/code/config"
|
||||
"tank/code/logger"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
)
|
||||
|
||||
type IBean interface {
|
||||
//初始化方法
|
||||
Init()
|
||||
//系统清理方法
|
||||
Cleanup()
|
||||
//所有配置都加载完成后调用的方法,包括数据库加载完毕
|
||||
ConfigPost()
|
||||
//快速的Panic方法
|
||||
PanicError(err error)
|
||||
}
|
||||
|
||||
type Bean struct {
|
||||
logger *logger.Logger
|
||||
}
|
||||
|
||||
func (this *Bean) Init() {
|
||||
this.logger = logger.LOGGER
|
||||
}
|
||||
|
||||
func (this *Bean) ConfigPost() {
|
||||
|
||||
}
|
||||
|
||||
//系统大清理,一般时产品即将上线时,清除脏数据,只执行一次。
|
||||
func (this *Bean) Cleanup() {
|
||||
|
||||
}
|
||||
|
||||
//处理错误的统一方法 可以省去if err!=nil 这段代码
|
||||
func (this *Bean) PanicError(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
//能找到一个user就找到一个
|
||||
func (this *Bean) findUser(writer http.ResponseWriter, request *http.Request) *User {
|
||||
|
||||
|
||||
//验证用户是否已经登录。
|
||||
//登录身份有效期以数据库中记录的为准
|
||||
sessionId := util.GetSessionUuidFromRequest(request, config.COOKIE_AUTH_KEY)
|
||||
if sessionId == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
//去缓存中捞取看看
|
||||
cacheItem, err := CONTEXT.SessionCache.Value(sessionId)
|
||||
if err != nil {
|
||||
this.logger.Warn("获取缓存时出错了" + err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
if cacheItem == nil || cacheItem.Data() == nil {
|
||||
this.logger.Warn("cache item中已经不存在了 ")
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := cacheItem.Data().(*User); ok {
|
||||
return value
|
||||
} else {
|
||||
this.logger.Error("cache item中的类型不是*User ")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//获取当前登录的用户,找不到就返回登录错误
|
||||
func (this *Bean) checkUser(writer http.ResponseWriter, request *http.Request) *User {
|
||||
if this.findUser(writer, request) == nil {
|
||||
panic(result.ConstWebResult(result.CODE_WRAPPER_LOGIN))
|
||||
} else {
|
||||
return this.findUser(writer, request)
|
||||
}
|
||||
}
|
198
code/rest/context.go
Normal file
198
code/rest/context.go
Normal file
@ -0,0 +1,198 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jinzhu/gorm"
|
||||
"reflect"
|
||||
"tank/code/config"
|
||||
"tank/code/logger"
|
||||
cache2 "tank/code/tool/cache"
|
||||
)
|
||||
|
||||
//全局唯一的上下文(在main函数中初始化)
|
||||
var CONTEXT = &Context{}
|
||||
|
||||
//上下文,管理数据库连接,管理所有路由请求,管理所有的单例component.
|
||||
type Context struct {
|
||||
//数据库连接
|
||||
DB *gorm.DB
|
||||
//session缓存
|
||||
SessionCache *cache2.Table
|
||||
//各类的Bean Map。这里面是包含ControllerMap中所有元素
|
||||
BeanMap map[string]IBean
|
||||
//只包含了Controller的map
|
||||
ControllerMap map[string]IController
|
||||
//处理所有路由请求
|
||||
Router *Router
|
||||
}
|
||||
|
||||
//初始化上下文
|
||||
func (this *Context) Init() {
|
||||
|
||||
//创建一个用于存储session的缓存。
|
||||
this.SessionCache = cache2.NewTable()
|
||||
|
||||
//初始化Map
|
||||
this.BeanMap = make(map[string]IBean)
|
||||
this.ControllerMap = make(map[string]IController)
|
||||
|
||||
//注册各类Beans.在这个方法里面顺便把Controller装入ControllerMap中去。
|
||||
this.registerBeans()
|
||||
|
||||
//初始化每个bean.
|
||||
this.initBeans()
|
||||
|
||||
//初始化Router. 这个方法要在Bean注册好了之后才能。
|
||||
this.Router = NewRouter()
|
||||
|
||||
//如果数据库信息配置好了,就直接打开数据库连接 同时执行Bean的ConfigPost方法
|
||||
this.InstallOk()
|
||||
|
||||
}
|
||||
|
||||
func (this *Context) OpenDb() {
|
||||
|
||||
var err error = nil
|
||||
this.DB, err = gorm.Open("mysql", config.CONFIG.MysqlUrl)
|
||||
|
||||
if err != nil {
|
||||
logger.LOGGER.Panic("failed to connect mysql database")
|
||||
}
|
||||
|
||||
//是否打开sql日志(在调试阶段可以打开,以方便查看执行的SQL)
|
||||
this.DB.LogMode(false)
|
||||
}
|
||||
|
||||
func (this *Context) CloseDb() {
|
||||
|
||||
if this.DB != nil {
|
||||
err := this.DB.Close()
|
||||
if err != nil {
|
||||
fmt.Println("关闭数据库连接出错", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//注册一个Bean
|
||||
func (this *Context) registerBean(bean IBean) {
|
||||
|
||||
typeOf := reflect.TypeOf(bean)
|
||||
typeName := typeOf.String()
|
||||
|
||||
if element, ok := bean.(IBean); ok {
|
||||
|
||||
err := fmt.Sprintf("【%s】已经被注册了,跳过。", typeName)
|
||||
if _, ok := this.BeanMap[typeName]; ok {
|
||||
logger.LOGGER.Error(fmt.Sprintf(err))
|
||||
} else {
|
||||
this.BeanMap[typeName] = element
|
||||
|
||||
//看看是不是controller类型,如果是,那么单独放在ControllerMap中。
|
||||
if controller, ok1 := bean.(IController); ok1 {
|
||||
this.ControllerMap[typeName] = controller
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.LOGGER.Panic("注册的【%s】不是Bean类型。", typeName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册各个Beans
|
||||
func (this *Context) registerBeans() {
|
||||
|
||||
//alien
|
||||
this.registerBean(new(AlienController))
|
||||
this.registerBean(new(AlienService))
|
||||
|
||||
//dashboard
|
||||
this.registerBean(new(DashboardController))
|
||||
this.registerBean(new(DashboardDao))
|
||||
this.registerBean(new(DashboardService))
|
||||
|
||||
//downloadToken
|
||||
this.registerBean(new(DownloadTokenDao))
|
||||
|
||||
//imageCache
|
||||
this.registerBean(new(ImageCacheController))
|
||||
this.registerBean(new(ImageCacheDao))
|
||||
this.registerBean(new(ImageCacheService))
|
||||
|
||||
//install
|
||||
this.registerBean(new(InstallController))
|
||||
|
||||
//matter
|
||||
this.registerBean(new(MatterController))
|
||||
this.registerBean(new(MatterDao))
|
||||
this.registerBean(new(MatterService))
|
||||
|
||||
//preference
|
||||
this.registerBean(new(PreferenceController))
|
||||
this.registerBean(new(PreferenceDao))
|
||||
this.registerBean(new(PreferenceService))
|
||||
|
||||
//footprint
|
||||
this.registerBean(new(FootprintController))
|
||||
this.registerBean(new(FootprintDao))
|
||||
this.registerBean(new(FootprintService))
|
||||
|
||||
//session
|
||||
this.registerBean(new(SessionDao))
|
||||
this.registerBean(new(SessionService))
|
||||
|
||||
//uploadToken
|
||||
this.registerBean(new(UploadTokenDao))
|
||||
|
||||
//user
|
||||
this.registerBean(new(UserController))
|
||||
this.registerBean(new(UserDao))
|
||||
this.registerBean(new(UserService))
|
||||
|
||||
//webdav
|
||||
this.registerBean(new(DavController))
|
||||
this.registerBean(new(DavService))
|
||||
|
||||
}
|
||||
|
||||
//从Map中获取某个Bean.
|
||||
func (this *Context) GetBean(bean IBean) IBean {
|
||||
|
||||
typeOf := reflect.TypeOf(bean)
|
||||
typeName := typeOf.String()
|
||||
|
||||
if val, ok := this.BeanMap[typeName]; ok {
|
||||
return val
|
||||
} else {
|
||||
logger.LOGGER.Panic("【%s】没有注册。", typeName)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//初始化每个Bean
|
||||
func (this *Context) initBeans() {
|
||||
|
||||
for _, bean := range this.BeanMap {
|
||||
bean.Init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//系统如果安装好了就调用这个方法。
|
||||
func (this *Context) InstallOk() {
|
||||
|
||||
if config.CONFIG.Installed {
|
||||
this.OpenDb()
|
||||
|
||||
for _, bean := range this.BeanMap {
|
||||
bean.ConfigPost()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//销毁的方法
|
||||
func (this *Context) Destroy() {
|
||||
this.CloseDb()
|
||||
}
|
104
code/rest/dashboard_controller.go
Normal file
104
code/rest/dashboard_controller.go
Normal file
@ -0,0 +1,104 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"tank/code/tool/builder"
|
||||
"tank/code/tool/result"
|
||||
)
|
||||
|
||||
type DashboardController struct {
|
||||
BaseController
|
||||
dashboardDao *DashboardDao
|
||||
dashboardService *DashboardService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *DashboardController) Init() {
|
||||
this.BaseController.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.dashboardDao)
|
||||
if b, ok := b.(*DashboardDao); ok {
|
||||
this.dashboardDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.dashboardService)
|
||||
if b, ok := b.(*DashboardService); ok {
|
||||
this.dashboardService = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *DashboardController) RegisterRoutes() 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/active/ip/top10"] = this.Wrap(this.ActiveIpTop10, USER_ROLE_ADMINISTRATOR)
|
||||
|
||||
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 {
|
||||
|
||||
//如果是根目录,那么就传入root.
|
||||
pageStr := request.FormValue("page")
|
||||
pageSizeStr := request.FormValue("pageSize")
|
||||
orderCreateTime := request.FormValue("orderCreateTime")
|
||||
orderUpdateTime := request.FormValue("orderUpdateTime")
|
||||
orderSort := request.FormValue("orderSort")
|
||||
orderDt := request.FormValue("orderDt")
|
||||
|
||||
var page int
|
||||
if pageStr != "" {
|
||||
page, _ = strconv.Atoi(pageStr)
|
||||
}
|
||||
|
||||
pageSize := 200
|
||||
if pageSizeStr != "" {
|
||||
tmp, err := strconv.Atoi(pageSizeStr)
|
||||
if err == nil {
|
||||
pageSize = tmp
|
||||
}
|
||||
}
|
||||
|
||||
sortArray := []builder.OrderPair{
|
||||
{
|
||||
key: "create_time",
|
||||
value: orderCreateTime,
|
||||
},
|
||||
{
|
||||
key: "update_time",
|
||||
value: orderUpdateTime,
|
||||
},
|
||||
{
|
||||
key: "sort",
|
||||
value: orderSort,
|
||||
},
|
||||
{
|
||||
key: "dt",
|
||||
value: orderDt,
|
||||
},
|
||||
}
|
||||
|
||||
pager := this.dashboardDao.Page(page, pageSize, "", sortArray)
|
||||
|
||||
return this.Success(pager)
|
||||
}
|
||||
|
||||
|
||||
func (this *DashboardController) ActiveIpTop10(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
list := this.dashboardDao.ActiveIpTop10()
|
||||
return this.Success(list)
|
||||
}
|
120
code/rest/dashboard_dao.go
Normal file
120
code/rest/dashboard_dao.go
Normal file
@ -0,0 +1,120 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"tank/code/tool/builder"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DashboardDao struct {
|
||||
BaseDao
|
||||
}
|
||||
|
||||
//创建
|
||||
func (this *DashboardDao) Create(dashboard *Dashboard) *Dashboard {
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
dashboard.Uuid = string(timeUUID.String())
|
||||
dashboard.CreateTime = time.Now()
|
||||
dashboard.UpdateTime = time.Now()
|
||||
dashboard.Sort = time.Now().UnixNano() / 1e6
|
||||
db := CONTEXT.DB.Create(dashboard)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return dashboard
|
||||
}
|
||||
|
||||
//修改一条记录
|
||||
func (this *DashboardDao) Save(dashboard *Dashboard) *Dashboard {
|
||||
|
||||
dashboard.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.Save(dashboard)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return dashboard
|
||||
}
|
||||
|
||||
//删除一条记录
|
||||
func (this *DashboardDao) Delete(dashboard *Dashboard) {
|
||||
|
||||
db := CONTEXT.DB.Delete(&dashboard)
|
||||
this.PanicError(db.Error)
|
||||
}
|
||||
|
||||
//按照dt查询
|
||||
func (this *DashboardDao) FindByDt(dt string) *Dashboard {
|
||||
|
||||
// Read
|
||||
var dashboard Dashboard
|
||||
db := CONTEXT.DB.Where(&Dashboard{Dt: dt}).First(&dashboard)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return &dashboard
|
||||
}
|
||||
|
||||
//获取某个文件夹下所有的文件和子文件
|
||||
func (this *DashboardDao) Page(page int, pageSize int, dt string, sortArray []builder.OrderPair) *Pager {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
if dt != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "dt = ?", Args: []interface{}{dt}})
|
||||
}
|
||||
|
||||
var conditionDB *gorm.DB
|
||||
conditionDB = CONTEXT.DB.Model(&Dashboard{}).Where(wp.Query, wp.Args...)
|
||||
|
||||
count := 0
|
||||
db := conditionDB.Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
var dashboards []*Dashboard
|
||||
db = conditionDB.Order(this.GetSortString(sortArray)).Offset(page * pageSize).Limit(pageSize).Find(&dashboards)
|
||||
this.PanicError(db.Error)
|
||||
pager := NewPager(page, pageSize, count, dashboards)
|
||||
|
||||
return pager
|
||||
}
|
||||
|
||||
//获取最活跃的前10个ip
|
||||
func (this *DashboardDao) ActiveIpTop10() []*DashboardIpTimes {
|
||||
|
||||
var dashboardIpTimes []*DashboardIpTimes
|
||||
|
||||
sortArray := []builder.OrderPair{
|
||||
{
|
||||
key: "times",
|
||||
value: "DESC",
|
||||
},
|
||||
}
|
||||
rows, err := CONTEXT.DB.Model(&Footprint{}).
|
||||
Select("ip,COUNT(uuid) as times").
|
||||
Group("ip").
|
||||
Order(this.GetSortString(sortArray)).
|
||||
Offset(0).
|
||||
Limit(10).
|
||||
Rows()
|
||||
|
||||
this.PanicError(err)
|
||||
for rows.Next() {
|
||||
var ip string;
|
||||
var times int64 = 0;
|
||||
rows.Scan(&ip, ×)
|
||||
item := &DashboardIpTimes{
|
||||
Ip: ip,
|
||||
Times: times,
|
||||
}
|
||||
dashboardIpTimes = append(dashboardIpTimes, item)
|
||||
}
|
||||
|
||||
return dashboardIpTimes
|
||||
}
|
||||
|
||||
//执行清理操作
|
||||
func (this *DashboardDao) Cleanup() {
|
||||
this.logger.Info("[DashboardDao]执行清理:清除数据库中所有Dashboard记录。")
|
||||
db := CONTEXT.DB.Where("uuid is not null").Delete(Dashboard{})
|
||||
this.PanicError(db.Error)
|
||||
}
|
33
code/rest/dashboard_model.go
Normal file
33
code/rest/dashboard_model.go
Normal file
@ -0,0 +1,33 @@
|
||||
package rest
|
||||
|
||||
import "tank/code/config"
|
||||
|
||||
/**
|
||||
* 系统的所有访问记录均记录在此
|
||||
*/
|
||||
type Dashboard struct {
|
||||
Base
|
||||
InvokeNum int64 `json:"invokeNum" gorm:"type:bigint(20) not null"` //当日访问量
|
||||
TotalInvokeNum int64 `json:"totalInvokeNum" gorm:"type:bigint(20) not null;default:0"` //截至目前总访问量
|
||||
Uv int64 `json:"uv" gorm:"type:bigint(20) not null;default:0"` //当日UV
|
||||
TotalUv int64 `json:"totalUv" gorm:"type:bigint(20) not null;default:0"` //截至目前总UV
|
||||
MatterNum int64 `json:"matterNum" gorm:"type:bigint(20) not null;default:0"` //文件数量
|
||||
TotalMatterNum int64 `json:"totalMatterNum" gorm:"type:bigint(20) not null;default:0"` //截至目前文件数量
|
||||
FileSize int64 `json:"fileSize" gorm:"type:bigint(20) not null;default:0"` //当日文件大小
|
||||
TotalFileSize int64 `json:"totalFileSize" gorm:"type:bigint(20) not null;default:0"` //截至目前文件总大小
|
||||
AvgCost int64 `json:"avgCost" gorm:"type:bigint(20) not null;default:0"` //请求平均耗时 ms
|
||||
Dt string `json:"dt" gorm:"type:varchar(45) not null;index:idx_dt"` //日期
|
||||
}
|
||||
|
||||
// set File's table name to be `profiles`
|
||||
func (this *Dashboard) TableName() string {
|
||||
return config.TABLE_PREFIX + "dashboard"
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计IP活跃数的
|
||||
*/
|
||||
type DashboardIpTimes struct {
|
||||
Ip string `json:"ip"`
|
||||
Times int64 `json:"times"`
|
||||
}
|
141
code/rest/dashboard_service.go
Normal file
141
code/rest/dashboard_service.go
Normal file
@ -0,0 +1,141 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"tank/code/tool/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
//@Service
|
||||
type DashboardService struct {
|
||||
Bean
|
||||
dashboardDao *DashboardDao
|
||||
footprintDao *FootprintDao
|
||||
matterDao *MatterDao
|
||||
imageCacheDao *ImageCacheDao
|
||||
userDao *UserDao
|
||||
//每天凌晨定时整理器
|
||||
maintainTimer *time.Timer
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *DashboardService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.dashboardDao)
|
||||
if b, ok := b.(*DashboardDao); ok {
|
||||
this.dashboardDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.footprintDao)
|
||||
if b, ok := b.(*FootprintDao); ok {
|
||||
this.footprintDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterDao)
|
||||
if b, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheDao)
|
||||
if b, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.userDao)
|
||||
if b, ok := b.(*UserDao); ok {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//系统启动,数据库配置完毕后会调用该方法
|
||||
func (this *DashboardService) ConfigPost() {
|
||||
|
||||
//立即执行数据清洗任务
|
||||
go util.SafeMethod(this.maintain)
|
||||
}
|
||||
|
||||
//每日清洗离线数据表。
|
||||
func (this *DashboardService) maintain() {
|
||||
|
||||
//准备好下次维护日志的时间。
|
||||
now := time.Now()
|
||||
nextTime := util.FirstMinuteOfDay(util.Tomorrow())
|
||||
duration := nextTime.Sub(now)
|
||||
this.logger.Info("每日数据汇总,下次时间:%s ", util.ConvertTimeToDateTimeString(nextTime))
|
||||
this.maintainTimer = time.AfterFunc(duration, func() {
|
||||
go util.SafeMethod(this.maintain)
|
||||
})
|
||||
|
||||
//准备日期开始结尾
|
||||
startTime := util.FirstSecondOfDay(util.Yesterday())
|
||||
endTime := util.LastSecondOfDay(util.Yesterday())
|
||||
dt := util.ConvertTimeToDateString(startTime)
|
||||
longTimeAgo := time.Now()
|
||||
longTimeAgo = longTimeAgo.AddDate(-20, 0, 0)
|
||||
|
||||
this.logger.Info("统计汇总表 %s -> %s", util.ConvertTimeToDateTimeString(startTime), util.ConvertTimeToDateTimeString(endTime))
|
||||
|
||||
//判断昨天的记录是否已经生成,如果生成了就直接删除掉
|
||||
dbDashboard := this.dashboardDao.FindByDt(dt)
|
||||
if dbDashboard != nil {
|
||||
this.logger.Info(" %s 的汇总已经存在了,删除以进行更新", dt)
|
||||
this.dashboardDao.Delete(dbDashboard)
|
||||
}
|
||||
|
||||
invokeNum := this.footprintDao.CountBetweenTime(startTime, endTime)
|
||||
this.logger.Info("调用数:%d", invokeNum)
|
||||
|
||||
totalInvokeNum := this.footprintDao.CountBetweenTime(longTimeAgo, endTime)
|
||||
this.logger.Info("历史总调用数:%d", totalInvokeNum)
|
||||
|
||||
uv := this.footprintDao.UvBetweenTime(startTime, endTime)
|
||||
this.logger.Info("UV:%d", uv)
|
||||
|
||||
totalUv := this.footprintDao.UvBetweenTime(longTimeAgo, endTime)
|
||||
this.logger.Info("历史总UV:%d", totalUv)
|
||||
|
||||
matterNum := this.matterDao.CountBetweenTime(startTime, endTime)
|
||||
this.logger.Info("文件数量数:%d", matterNum)
|
||||
|
||||
totalMatterNum := this.matterDao.CountBetweenTime(longTimeAgo, endTime)
|
||||
this.logger.Info("历史文件总数:%d", totalMatterNum)
|
||||
|
||||
var matterSize int64
|
||||
if matterNum != 0 {
|
||||
matterSize = this.matterDao.SizeBetweenTime(startTime, endTime)
|
||||
}
|
||||
this.logger.Info("文件大小:%d", matterSize)
|
||||
|
||||
var totalMatterSize int64
|
||||
if totalMatterNum != 0 {
|
||||
totalMatterSize = this.matterDao.SizeBetweenTime(longTimeAgo, endTime)
|
||||
}
|
||||
this.logger.Info("历史文件总大小:%d", totalMatterSize)
|
||||
|
||||
cacheSize := this.imageCacheDao.SizeBetweenTime(startTime, endTime)
|
||||
this.logger.Info("缓存大小:%d", cacheSize)
|
||||
|
||||
totalCacheSize := this.imageCacheDao.SizeBetweenTime(longTimeAgo, endTime)
|
||||
this.logger.Info("历史缓存总大小:%d", totalCacheSize)
|
||||
|
||||
avgCost := this.footprintDao.AvgCostBetweenTime(startTime, endTime)
|
||||
this.logger.Info("平均耗时:%d ms", avgCost)
|
||||
|
||||
dashboard := &Dashboard{
|
||||
InvokeNum: invokeNum,
|
||||
TotalInvokeNum: totalInvokeNum,
|
||||
Uv: uv,
|
||||
TotalUv: totalUv,
|
||||
MatterNum: matterNum,
|
||||
TotalMatterNum: totalMatterNum,
|
||||
FileSize: matterSize + cacheSize,
|
||||
TotalFileSize: totalMatterSize + totalCacheSize,
|
||||
AvgCost: avgCost,
|
||||
Dt: dt,
|
||||
}
|
||||
|
||||
this.dashboardDao.Create(dashboard)
|
||||
|
||||
}
|
174
code/rest/dav_controller.go
Normal file
174
code/rest/dav_controller.go
Normal file
@ -0,0 +1,174 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
)
|
||||
|
||||
/**
|
||||
*
|
||||
* WebDav协议文档
|
||||
* https://tools.ietf.org/html/rfc4918
|
||||
* http://www.webdav.org/specs/rfc4918.html
|
||||
*
|
||||
*/
|
||||
|
||||
type DavController struct {
|
||||
BaseController
|
||||
uploadTokenDao *UploadTokenDao
|
||||
downloadTokenDao *DownloadTokenDao
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
davService *DavService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *DavController) Init() {
|
||||
this.BaseController.Init()
|
||||
|
||||
//手动装填本实例的Bean.
|
||||
b := CONTEXT.GetBean(this.uploadTokenDao)
|
||||
if c, ok := b.(*UploadTokenDao); ok {
|
||||
this.uploadTokenDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.downloadTokenDao)
|
||||
if c, ok := b.(*DownloadTokenDao); ok {
|
||||
this.downloadTokenDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterDao)
|
||||
if c, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterService)
|
||||
if c, ok := b.(*MatterService); ok {
|
||||
this.matterService = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheDao)
|
||||
if c, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheService)
|
||||
if c, ok := b.(*ImageCacheService); ok {
|
||||
this.imageCacheService = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.davService)
|
||||
if c, ok := b.(*DavService); ok {
|
||||
this.davService = c
|
||||
}
|
||||
}
|
||||
|
||||
//通过BasicAuth的方式授权。
|
||||
func (this *DavController) CheckCurrentUser(writer http.ResponseWriter, request *http.Request) *User {
|
||||
|
||||
username, password, ok := request.BasicAuth()
|
||||
if !ok {
|
||||
//要求前端使用Basic的形式授权
|
||||
writer.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||
|
||||
panic(result.ConstWebResult(result.CODE_WRAPPER_LOGIN))
|
||||
|
||||
}
|
||||
|
||||
user := this.userDao.FindByUsername(username)
|
||||
if user == nil {
|
||||
panic(result.BadRequest("邮箱或密码错误"))
|
||||
} else {
|
||||
if !util.MatchBcrypt(password, user.Password) {
|
||||
panic(result.BadRequest("邮箱或密码错误"))
|
||||
}
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *DavController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
|
||||
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//处理一些特殊的接口,比如参数包含在路径中,一般情况下,controller不将参数放在url路径中
|
||||
func (this *DavController) HandleRoutes(writer http.ResponseWriter, request *http.Request) (func(writer http.ResponseWriter, request *http.Request), bool) {
|
||||
|
||||
path := request.URL.Path
|
||||
|
||||
//匹配 /api/dav{subPath}
|
||||
pattern := fmt.Sprintf(`^%s(.*)$`, WEBDAV_PREFFIX)
|
||||
reg := regexp.MustCompile(pattern)
|
||||
strs := reg.FindStringSubmatch(path)
|
||||
if len(strs) == 2 {
|
||||
var f = func(writer http.ResponseWriter, request *http.Request) {
|
||||
subPath := strs[1]
|
||||
//保证subPath不是以/结尾的。
|
||||
//最后多余的/要去掉
|
||||
if strings.HasSuffix(subPath, "/") {
|
||||
subPath = subPath[0 : len(subPath)-1]
|
||||
}
|
||||
this.Index(writer, request, subPath)
|
||||
}
|
||||
return f, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
//完成系统安装
|
||||
func (this *DavController) Index(writer http.ResponseWriter, request *http.Request, subPath string) {
|
||||
|
||||
/*打印所有HEADER以及请求参数*/
|
||||
|
||||
fmt.Printf("\n------ 请求: %s -- %s ------\n", request.URL, subPath)
|
||||
|
||||
fmt.Printf("\n------Method:------\n")
|
||||
fmt.Println(request.Method)
|
||||
|
||||
fmt.Printf("\n------Header:------\n")
|
||||
for key, value := range request.Header {
|
||||
fmt.Printf("%s = %s\n", key, value)
|
||||
}
|
||||
|
||||
fmt.Printf("\n------请求参数:------\n")
|
||||
for key, value := range request.Form {
|
||||
fmt.Printf("%s = %s\n", key, value)
|
||||
}
|
||||
|
||||
fmt.Printf("\n------Body:------\n")
|
||||
//ioutil.ReadAll 不可重复读,第二次读的时候就什么都没有了。
|
||||
|
||||
bodyBytes, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
fmt.Println("读取body时出错" + err.Error())
|
||||
}
|
||||
fmt.Println(string(bodyBytes))
|
||||
|
||||
//关闭之后再重新赋值
|
||||
err = request.Body.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
|
||||
fmt.Println("------------------")
|
||||
|
||||
//获取请求者
|
||||
user := this.CheckCurrentUser(writer, request)
|
||||
|
||||
this.davService.HandleDav(writer, request, user, subPath)
|
||||
|
||||
}
|
123
code/rest/dav_model.go
Normal file
123
code/rest/dav_model.go
Normal file
@ -0,0 +1,123 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"tank/code/tool/dav"
|
||||
"tank/code/tool/dav/xml"
|
||||
)
|
||||
|
||||
//访问前缀,这个是特殊入口
|
||||
var WEBDAV_PREFFIX = "/api/dav"
|
||||
|
||||
//动态的文件属性
|
||||
type LiveProp struct {
|
||||
findFn func(user *User, matter *Matter) string
|
||||
dir bool
|
||||
}
|
||||
|
||||
//所有的动态属性定义及其值的获取方式
|
||||
var LivePropMap = map[xml.Name]LiveProp{
|
||||
{Space: "DAV:", Local: "resourcetype"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
if matter.Dir {
|
||||
return `<D:collection xmlns:D="DAV:"/>`
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
},
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "displayname"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
if path.Clean("/"+matter.Name) == "/" {
|
||||
return ""
|
||||
} else {
|
||||
return dav.EscapeXML(matter.Name)
|
||||
}
|
||||
},
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "getcontentlength"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
return strconv.FormatInt(matter.Size, 10)
|
||||
},
|
||||
dir: false,
|
||||
},
|
||||
{Space: "DAV:", Local: "getlastmodified"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
return matter.UpdateTime.UTC().Format(http.TimeFormat)
|
||||
},
|
||||
// http://webdav.org/specs/rfc4918.html#PROPERTY_getlastmodified
|
||||
// suggests that getlastmodified should only apply to GETable
|
||||
// resources, and this package does not support GET on directories.
|
||||
//
|
||||
// Nonetheless, some WebDAV clients expect child directories to be
|
||||
// sortable by getlastmodified date, so this value is true, not false.
|
||||
// See golang.org/issue/15334.
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "creationdate"}: {
|
||||
findFn: nil,
|
||||
dir: false,
|
||||
},
|
||||
{Space: "DAV:", Local: "getcontentlanguage"}: {
|
||||
findFn: nil,
|
||||
dir: false,
|
||||
},
|
||||
{Space: "DAV:", Local: "getcontenttype"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
if matter.Dir {
|
||||
return ""
|
||||
} else {
|
||||
return dav.EscapeXML(matter.Name)
|
||||
}
|
||||
},
|
||||
dir: false,
|
||||
},
|
||||
{Space: "DAV:", Local: "getetag"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
return fmt.Sprintf(`"%x%x"`, matter.UpdateTime.UnixNano(), matter.Size)
|
||||
},
|
||||
// findETag implements ETag as the concatenated hex values of a file's
|
||||
// modification time and size. This is not a reliable synchronization
|
||||
// mechanism for directories, so we do not advertise getetag for DAV
|
||||
// collections.
|
||||
dir: false,
|
||||
},
|
||||
// TODO: The lockdiscovery property requires LockSystem to list the
|
||||
// active locks on a resource.
|
||||
{Space: "DAV:", Local: "lockdiscovery"}: {},
|
||||
{Space: "DAV:", Local: "supportedlock"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
return `` +
|
||||
`<D:lockentry xmlns:D="DAV:">` +
|
||||
`<D:lockscope><D:exclusive/></D:lockscope>` +
|
||||
`<D:locktype><D:write/></D:locktype>` +
|
||||
`</D:lockentry>`
|
||||
},
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "quota-available-bytes"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
var size int64 = 0
|
||||
if user.SizeLimit >= 0 {
|
||||
size = user.SizeLimit
|
||||
} else {
|
||||
//没有限制,默认100G
|
||||
size = 100 * 1024 * 1024 * 1024
|
||||
}
|
||||
return fmt.Sprintf(`%d`, size)
|
||||
},
|
||||
dir: true,
|
||||
},
|
||||
{Space: "DAV:", Local: "quota-used-bytes"}: {
|
||||
findFn: func(user *User, matter *Matter) string {
|
||||
//已使用大小,默认0
|
||||
return fmt.Sprintf(`%d`, 0)
|
||||
},
|
||||
dir: true,
|
||||
},
|
||||
}
|
488
code/rest/dav_service.go
Normal file
488
code/rest/dav_service.go
Normal file
@ -0,0 +1,488 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
dav2 "tank/code/tool/dav"
|
||||
xml2 "tank/code/tool/dav/xml"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
)
|
||||
|
||||
/**
|
||||
*
|
||||
* WebDav协议文档
|
||||
* https://tools.ietf.org/html/rfc4918
|
||||
* 主要参考 golang.org/x/net/webdav
|
||||
* 测试机:http://www.webdav.org/neon/litmus/
|
||||
*/
|
||||
//@Service
|
||||
type DavService struct {
|
||||
Bean
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *DavService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.matterDao)
|
||||
if b, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterService)
|
||||
if b, ok := b.(*MatterService); ok {
|
||||
this.matterService = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//获取Header头中的Depth值,暂不支持 infinity
|
||||
func (this *DavService) ParseDepth(request *http.Request) int {
|
||||
|
||||
depth := 1
|
||||
if hdr := request.Header.Get("Depth"); hdr != "" {
|
||||
switch hdr {
|
||||
case "0":
|
||||
return 0
|
||||
case "1":
|
||||
return 1
|
||||
case "infinity":
|
||||
return 1
|
||||
}
|
||||
} else {
|
||||
panic(result.BadRequest("必须指定Header Depth"))
|
||||
}
|
||||
return depth
|
||||
}
|
||||
|
||||
func (this *DavService) makePropstatResponse(href string, pstats []dav2.Propstat) *dav2.Response {
|
||||
resp := dav2.Response{
|
||||
Href: []string{(&url.URL{Path: href}).EscapedPath()},
|
||||
Propstat: make([]dav2.SubPropstat, 0, len(pstats)),
|
||||
}
|
||||
for _, p := range pstats {
|
||||
var xmlErr *dav2.XmlError
|
||||
if p.XMLError != "" {
|
||||
xmlErr = &dav2.XmlError{InnerXML: []byte(p.XMLError)}
|
||||
}
|
||||
resp.Propstat = append(resp.Propstat, dav2.SubPropstat{
|
||||
Status: fmt.Sprintf("HTTP/1.1 %d %s", p.Status, dav2.StatusText(p.Status)),
|
||||
Prop: p.Props,
|
||||
ResponseDescription: p.ResponseDescription,
|
||||
Error: xmlErr,
|
||||
})
|
||||
}
|
||||
return &resp
|
||||
}
|
||||
|
||||
//从一个matter中获取其 []dav.Propstat
|
||||
func (this *DavService) PropstatsFromXmlNames(user *User, matter *Matter, xmlNames []xml2.Name) []dav2.Propstat {
|
||||
|
||||
propstats := make([]dav2.Propstat, 0)
|
||||
|
||||
var properties []dav2.Property
|
||||
|
||||
for _, xmlName := range xmlNames {
|
||||
//TODO: deadprops尚未考虑
|
||||
|
||||
// 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) {
|
||||
innerXML := liveProp.findFn(user, matter)
|
||||
|
||||
properties = append(properties, dav2.Property{
|
||||
XMLName: xmlName,
|
||||
InnerXML: []byte(innerXML),
|
||||
})
|
||||
} else {
|
||||
this.logger.Info("%s的%s无法完成", matter.Path, xmlName.Local)
|
||||
}
|
||||
}
|
||||
|
||||
if len(properties) == 0 {
|
||||
panic(result.BadRequest("请求的属性项无法解析!"))
|
||||
}
|
||||
|
||||
okPropstat := dav2.Propstat{Status: http.StatusOK, Props: properties}
|
||||
|
||||
propstats = append(propstats, okPropstat)
|
||||
|
||||
return propstats
|
||||
|
||||
}
|
||||
|
||||
//从一个matter中获取所有的propsNames
|
||||
func (this *DavService) AllPropXmlNames(matter *Matter) []xml2.Name {
|
||||
|
||||
pnames := make([]xml2.Name, 0)
|
||||
for pn, prop := range LivePropMap {
|
||||
if prop.findFn != nil && (prop.dir || !matter.Dir) {
|
||||
pnames = append(pnames, pn)
|
||||
}
|
||||
}
|
||||
|
||||
return pnames
|
||||
}
|
||||
|
||||
//从一个matter中获取其 []dav.Propstat
|
||||
func (this *DavService) Propstats(user *User, matter *Matter, propfind *dav2.Propfind) []dav2.Propstat {
|
||||
|
||||
propstats := make([]dav2.Propstat, 0)
|
||||
if propfind.Propname != nil {
|
||||
panic(result.BadRequest("propfind.Propname != nil 尚未处理"))
|
||||
} else if propfind.Allprop != nil {
|
||||
|
||||
//TODO: 如果include中还有内容,那么包含进去。
|
||||
xmlNames := this.AllPropXmlNames(matter)
|
||||
|
||||
propstats = this.PropstatsFromXmlNames(user, matter, xmlNames)
|
||||
|
||||
} else {
|
||||
propstats = this.PropstatsFromXmlNames(user, matter, propfind.Prop)
|
||||
}
|
||||
|
||||
return propstats
|
||||
|
||||
}
|
||||
|
||||
//列出文件夹或者目录详情
|
||||
func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("PROPFIND %s\n", subPath)
|
||||
|
||||
//获取请求的层数。暂不支持 infinity
|
||||
depth := this.ParseDepth(request)
|
||||
|
||||
//读取请求参数。按照用户的参数请求返回内容。
|
||||
propfind := dav2.ReadPropfind(request.Body)
|
||||
|
||||
//寻找符合条件的matter.
|
||||
//如果是空或者/就是请求根目录
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
|
||||
var matters []*Matter
|
||||
if depth == 0 {
|
||||
matters = []*Matter{matter}
|
||||
} else {
|
||||
// len(matters) == 0 表示该文件夹下面是空文件夹
|
||||
matters = this.matterDao.List(matter.Uuid, user.Uuid, nil)
|
||||
|
||||
//将当前的matter添加到头部
|
||||
matters = append([]*Matter{matter}, matters...)
|
||||
}
|
||||
|
||||
//准备一个输出结果的Writer
|
||||
multiStatusWriter := &dav2.MultiStatusWriter{Writer: writer}
|
||||
|
||||
for _, matter := range matters {
|
||||
|
||||
fmt.Printf("处理Matter %s\n", matter.Path)
|
||||
|
||||
propstats := this.Propstats(user, matter, propfind)
|
||||
path := fmt.Sprintf("%s%s", WEBDAV_PREFFIX, matter.Path)
|
||||
response := this.makePropstatResponse(path, propstats)
|
||||
|
||||
err := multiStatusWriter.Write(response)
|
||||
this.PanicError(err)
|
||||
}
|
||||
|
||||
//闭合
|
||||
err := multiStatusWriter.Close()
|
||||
this.PanicError(err)
|
||||
|
||||
fmt.Printf("%v %v \n", subPath, propfind.Prop)
|
||||
|
||||
}
|
||||
|
||||
//请求文件详情(下载)
|
||||
func (this *DavService) HandleGetHeadPost(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("GET %s\n", subPath)
|
||||
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
|
||||
//如果是文件夹,相当于是 Propfind
|
||||
if matter.Dir {
|
||||
this.HandlePropfind(writer, request, user, subPath)
|
||||
return
|
||||
}
|
||||
|
||||
//下载一个文件。
|
||||
this.matterService.DownloadFile(writer, request, matter.AbsolutePath(), matter.Name, false)
|
||||
|
||||
}
|
||||
|
||||
//上传文件
|
||||
func (this *DavService) HandlePut(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("PUT %s\n", subPath)
|
||||
|
||||
filename := util.GetFilenameOfPath(subPath)
|
||||
dirPath := util.GetDirOfPath(subPath)
|
||||
|
||||
//寻找符合条件的matter.
|
||||
dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user)
|
||||
|
||||
//如果存在,那么先删除再说。
|
||||
srcMatter := this.matterDao.findByUserUuidAndPath(user.Uuid, subPath)
|
||||
if srcMatter != nil {
|
||||
this.matterService.AtomicDelete(srcMatter)
|
||||
}
|
||||
|
||||
this.matterService.AtomicUpload(request.Body, user, dirMatter, filename, true)
|
||||
|
||||
}
|
||||
|
||||
//删除文件
|
||||
func (this *DavService) HandleDelete(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("DELETE %s\n", subPath)
|
||||
|
||||
//寻找符合条件的matter.
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
|
||||
this.matterService.AtomicDelete(matter)
|
||||
}
|
||||
|
||||
//创建文件夹
|
||||
func (this *DavService) HandleMkcol(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("MKCOL %s\n", subPath)
|
||||
|
||||
thisDirName := util.GetFilenameOfPath(subPath)
|
||||
dirPath := util.GetDirOfPath(subPath)
|
||||
|
||||
//寻找符合条件的matter.
|
||||
dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user)
|
||||
|
||||
this.matterService.AtomicCreateDirectory(dirMatter, thisDirName, user)
|
||||
|
||||
}
|
||||
|
||||
//跨域请求的OPTIONS询问
|
||||
func (this *DavService) HandleOptions(w http.ResponseWriter, r *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("OPTIONS %s\n", subPath)
|
||||
|
||||
//寻找符合条件的matter.
|
||||
matter := this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
|
||||
allow := "OPTIONS, LOCK, PUT, MKCOL"
|
||||
if matter.Dir {
|
||||
allow = "OPTIONS, LOCK, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND"
|
||||
} else {
|
||||
allow = "OPTIONS, LOCK, GET, HEAD, POST, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND, PUT"
|
||||
}
|
||||
|
||||
w.Header().Set("Allow", allow)
|
||||
// http://www.webdav.org/specs/rfc4918.html#dav.compliance.classes
|
||||
w.Header().Set("DAV", "1, 2")
|
||||
// http://msdn.microsoft.com/en-au/library/cc250217.aspx
|
||||
w.Header().Set("MS-Author-Via", "DAV")
|
||||
|
||||
}
|
||||
|
||||
//移动或者复制的准备工作
|
||||
func (this *DavService) prepareMoveCopy(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
user *User, subPath string) (
|
||||
srcMatter *Matter,
|
||||
destDirMatter *Matter,
|
||||
srcDirPath string,
|
||||
destinationDirPath string,
|
||||
destinationName string,
|
||||
overwrite bool) {
|
||||
|
||||
//解析出目标路径。
|
||||
destinationStr := request.Header.Get("Destination")
|
||||
|
||||
//解析出Overwrite。
|
||||
overwriteStr := request.Header.Get("Overwrite")
|
||||
|
||||
//有前缀的目标path
|
||||
var fullDestinationPath string
|
||||
//去掉前缀的目标path
|
||||
var destinationPath string
|
||||
|
||||
if destinationStr == "" {
|
||||
panic(result.BadRequest("Header Destination必填"))
|
||||
}
|
||||
|
||||
//如果是重命名,那么就不是http开头了。
|
||||
if strings.HasPrefix(destinationStr, WEBDAV_PREFFIX) {
|
||||
fullDestinationPath = destinationStr
|
||||
} else {
|
||||
destinationUrl, err := url.Parse(destinationStr)
|
||||
this.PanicError(err)
|
||||
if destinationUrl.Host != request.Host {
|
||||
panic(result.BadRequest("Destination Host不一致. %s %s != %s", destinationStr, destinationUrl.Host, request.Host))
|
||||
}
|
||||
fullDestinationPath = destinationUrl.Path
|
||||
}
|
||||
|
||||
//去除 路径中的相对路径 比如 /a/b/../ => /a/
|
||||
fullDestinationPath = path.Clean(fullDestinationPath)
|
||||
|
||||
//去除前缀
|
||||
pattern := fmt.Sprintf(`^%s(.*)$`, WEBDAV_PREFFIX)
|
||||
reg := regexp.MustCompile(pattern)
|
||||
strs := reg.FindStringSubmatch(fullDestinationPath)
|
||||
if len(strs) == 2 {
|
||||
destinationPath = strs[1]
|
||||
} else {
|
||||
panic(result.BadRequest("目标前缀必须为:%s", WEBDAV_PREFFIX))
|
||||
}
|
||||
|
||||
destinationName = util.GetFilenameOfPath(destinationPath)
|
||||
destinationDirPath = util.GetDirOfPath(destinationPath)
|
||||
srcDirPath = util.GetDirOfPath(subPath)
|
||||
|
||||
overwrite = false
|
||||
if overwriteStr == "T" {
|
||||
overwrite = true
|
||||
}
|
||||
|
||||
//如果前后一致,那么相当于没有改变
|
||||
if destinationPath == subPath {
|
||||
return
|
||||
}
|
||||
|
||||
//源matter.
|
||||
//寻找符合条件的matter.
|
||||
srcMatter = this.matterDao.CheckWithRootByPath(subPath, user)
|
||||
|
||||
//如果是空或者/就是请求根目录
|
||||
if srcMatter.Uuid == MATTER_ROOT {
|
||||
panic(result.BadRequest("你不能移动根目录!"))
|
||||
}
|
||||
|
||||
//寻找目标文件夹matter
|
||||
destDirMatter = this.matterDao.CheckWithRootByPath(destinationDirPath, user)
|
||||
|
||||
return srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite
|
||||
|
||||
}
|
||||
|
||||
//移动或者重命名
|
||||
func (this *DavService) HandleMove(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("MOVE %s\n", subPath)
|
||||
|
||||
srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath)
|
||||
//移动到新目录中去。
|
||||
if destinationDirPath == srcDirPath {
|
||||
//文件夹没变化,相当于重命名。
|
||||
this.matterService.AtomicRename(srcMatter, destinationName, user)
|
||||
} else {
|
||||
this.matterService.AtomicMove(srcMatter, destDirMatter, overwrite)
|
||||
}
|
||||
|
||||
this.logger.Info("完成移动 %s => %s", subPath, destDirMatter.Path)
|
||||
}
|
||||
|
||||
//复制文件/文件夹
|
||||
func (this *DavService) HandleCopy(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
fmt.Printf("COPY %s\n", subPath)
|
||||
|
||||
srcMatter, destDirMatter, _, _, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath)
|
||||
|
||||
//复制到新目录中去。
|
||||
this.matterService.AtomicCopy(srcMatter, destDirMatter, destinationName, overwrite)
|
||||
|
||||
this.logger.Info("完成复制 %s => %s", subPath, destDirMatter.Path)
|
||||
|
||||
}
|
||||
|
||||
//加锁
|
||||
func (this *DavService) HandleLock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
panic(result.BadRequest("不支持LOCK方法"))
|
||||
}
|
||||
|
||||
//解锁
|
||||
func (this *DavService) HandleUnlock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
panic(result.BadRequest("不支持UNLOCK方法"))
|
||||
}
|
||||
|
||||
//修改文件属性
|
||||
func (this *DavService) HandleProppatch(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
panic(result.BadRequest("不支持PROPPATCH方法"))
|
||||
}
|
||||
|
||||
//处理所有的请求
|
||||
func (this *DavService) HandleDav(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
|
||||
|
||||
method := request.Method
|
||||
if method == "OPTIONS" {
|
||||
|
||||
//跨域问询
|
||||
this.HandleOptions(writer, request, user, subPath)
|
||||
|
||||
} else if method == "GET" || method == "HEAD" || method == "POST" {
|
||||
|
||||
//请求文件详情(下载)
|
||||
this.HandleGetHeadPost(writer, request, user, subPath)
|
||||
|
||||
} else if method == "DELETE" {
|
||||
|
||||
//删除文件
|
||||
this.HandleDelete(writer, request, user, subPath)
|
||||
|
||||
} else if method == "PUT" {
|
||||
|
||||
//上传文件
|
||||
this.HandlePut(writer, request, user, subPath)
|
||||
|
||||
} else if method == "MKCOL" {
|
||||
|
||||
//创建文件夹
|
||||
this.HandleMkcol(writer, request, user, subPath)
|
||||
|
||||
} else if method == "COPY" {
|
||||
|
||||
//复制文件/文件夹
|
||||
this.HandleCopy(writer, request, user, subPath)
|
||||
|
||||
} else if method == "MOVE" {
|
||||
|
||||
//移动(重命名)文件/文件夹
|
||||
this.HandleMove(writer, request, user, subPath)
|
||||
|
||||
} else if method == "LOCK" {
|
||||
|
||||
//加锁
|
||||
this.HandleLock(writer, request, user, subPath)
|
||||
|
||||
} else if method == "UNLOCK" {
|
||||
|
||||
//释放锁
|
||||
this.HandleUnlock(writer, request, user, subPath)
|
||||
|
||||
} else if method == "PROPFIND" {
|
||||
|
||||
//列出文件夹或者目录详情
|
||||
this.HandlePropfind(writer, request, user, subPath)
|
||||
|
||||
} else if method == "PROPPATCH" {
|
||||
|
||||
//修改文件属性
|
||||
this.HandleProppatch(writer, request, user, subPath)
|
||||
|
||||
} else {
|
||||
|
||||
panic(result.BadRequest("该方法还不支持。%s", method))
|
||||
|
||||
}
|
||||
|
||||
}
|
67
code/rest/download_token_dao.go
Normal file
67
code/rest/download_token_dao.go
Normal file
@ -0,0 +1,67 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DownloadTokenDao struct {
|
||||
BaseDao
|
||||
}
|
||||
|
||||
//按照Id查询
|
||||
func (this *DownloadTokenDao) FindByUuid(uuid string) *DownloadToken {
|
||||
|
||||
// Read
|
||||
var downloadToken = &DownloadToken{}
|
||||
db := CONTEXT.DB.Where(&DownloadToken{Base: Base{Uuid: uuid}}).First(downloadToken)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return downloadToken
|
||||
|
||||
}
|
||||
|
||||
//按照Id查询
|
||||
func (this *DownloadTokenDao) CheckByUuid(uuid string) *DownloadToken {
|
||||
|
||||
// Read
|
||||
var downloadToken = &DownloadToken{}
|
||||
db := CONTEXT.DB.Where(&DownloadToken{Base: Base{Uuid: uuid}}).First(downloadToken)
|
||||
this.PanicError(db.Error)
|
||||
return downloadToken
|
||||
|
||||
}
|
||||
|
||||
//创建一个session并且持久化到数据库中。
|
||||
func (this *DownloadTokenDao) Create(downloadToken *DownloadToken) *DownloadToken {
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
downloadToken.Uuid = string(timeUUID.String())
|
||||
|
||||
downloadToken.CreateTime = time.Now()
|
||||
downloadToken.UpdateTime = time.Now()
|
||||
downloadToken.Sort = time.Now().UnixNano() / 1e6
|
||||
db := CONTEXT.DB.Create(downloadToken)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return downloadToken
|
||||
}
|
||||
|
||||
//修改一个downloadToken
|
||||
func (this *DownloadTokenDao) Save(downloadToken *DownloadToken) *DownloadToken {
|
||||
|
||||
downloadToken.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.Save(downloadToken)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return downloadToken
|
||||
}
|
||||
|
||||
//执行清理操作
|
||||
func (this *DownloadTokenDao) Cleanup() {
|
||||
this.logger.Info("[DownloadTokenDao]执行清理:清除数据库中所有DownloadToken记录。")
|
||||
db := CONTEXT.DB.Where("uuid is not null").Delete(DownloadToken{})
|
||||
this.PanicError(db.Error)
|
||||
}
|
18
code/rest/download_token_model.go
Normal file
18
code/rest/download_token_model.go
Normal file
@ -0,0 +1,18 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"tank/code/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DownloadToken struct {
|
||||
Base
|
||||
UserUuid string `json:"userUuid" gorm:"type:char(36) not null"`
|
||||
MatterUuid string `json:"matterUuid" gorm:"type:char(36) not null;index:idx_mu"`
|
||||
ExpireTime time.Time `json:"expireTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
|
||||
Ip string `json:"ip" gorm:"type:varchar(128) not null"`
|
||||
}
|
||||
|
||||
func (this *DownloadToken) TableName() string {
|
||||
return config.TABLE_PREFIX + "download_token"
|
||||
}
|
127
code/rest/footprint_controller.go
Normal file
127
code/rest/footprint_controller.go
Normal file
@ -0,0 +1,127 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"tank/code/tool/builder"
|
||||
"tank/code/tool/result"
|
||||
)
|
||||
|
||||
type FootprintController struct {
|
||||
BaseController
|
||||
footprintDao *FootprintDao
|
||||
footprintService *FootprintService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *FootprintController) Init() {
|
||||
this.BaseController.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.footprintDao)
|
||||
if b, ok := b.(*FootprintDao); ok {
|
||||
this.footprintDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.footprintService)
|
||||
if b, ok := b.(*FootprintService); ok {
|
||||
this.footprintService = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *FootprintController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
|
||||
|
||||
//每个Controller需要主动注册自己的路由。
|
||||
routeMap["/api/footprint/delete"] = this.Wrap(this.Delete, USER_ROLE_USER)
|
||||
routeMap["/api/footprint/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
|
||||
routeMap["/api/footprint/page"] = this.Wrap(this.Page, USER_ROLE_USER)
|
||||
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//查看详情。
|
||||
func (this *FootprintController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
if uuid == "" {
|
||||
panic(result.BadRequest("图片缓存的uuid必填"))
|
||||
}
|
||||
|
||||
footprint := this.footprintService.Detail(uuid)
|
||||
|
||||
//验证当前之人是否有权限查看这么详细。
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
if footprint.UserUuid != user.Uuid {
|
||||
panic("没有权限查看该图片缓存")
|
||||
}
|
||||
}
|
||||
|
||||
return this.Success(footprint)
|
||||
|
||||
}
|
||||
|
||||
//按照分页的方式查询
|
||||
func (this *FootprintController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
//如果是根目录,那么就传入root.
|
||||
pageStr := request.FormValue("page")
|
||||
pageSizeStr := request.FormValue("pageSize")
|
||||
userUuid := request.FormValue("userUuid")
|
||||
orderCreateTime := request.FormValue("orderCreateTime")
|
||||
orderSize := request.FormValue("orderSize")
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
userUuid = user.Uuid
|
||||
}
|
||||
|
||||
var page int
|
||||
if pageStr != "" {
|
||||
page, _ = strconv.Atoi(pageStr)
|
||||
}
|
||||
|
||||
pageSize := 200
|
||||
if pageSizeStr != "" {
|
||||
tmp, err := strconv.Atoi(pageSizeStr)
|
||||
if err == nil {
|
||||
pageSize = tmp
|
||||
}
|
||||
}
|
||||
|
||||
sortArray := []builder.OrderPair{
|
||||
{
|
||||
key: "create_time",
|
||||
value: orderCreateTime,
|
||||
},
|
||||
{
|
||||
key: "size",
|
||||
value: orderSize,
|
||||
},
|
||||
}
|
||||
|
||||
pager := this.footprintDao.Page(page, pageSize, userUuid, sortArray)
|
||||
|
||||
return this.Success(pager)
|
||||
}
|
||||
|
||||
//删除一条记录
|
||||
func (this *FootprintController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
if uuid == "" {
|
||||
panic(result.BadRequest("uuid必填"))
|
||||
}
|
||||
|
||||
footprint := this.footprintDao.FindByUuid(uuid)
|
||||
|
||||
if footprint != nil {
|
||||
this.footprintDao.Delete(footprint)
|
||||
}
|
||||
|
||||
return this.Success("删除成功!")
|
||||
}
|
128
code/rest/footprint_dao.go
Normal file
128
code/rest/footprint_dao.go
Normal file
@ -0,0 +1,128 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm"
|
||||
"tank/code/tool/builder"
|
||||
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FootprintDao struct {
|
||||
BaseDao
|
||||
}
|
||||
|
||||
//按照Id查询文件
|
||||
func (this *FootprintDao) FindByUuid(uuid string) *Footprint {
|
||||
|
||||
// Read
|
||||
var footprint Footprint
|
||||
db := CONTEXT.DB.Where(&Footprint{Base: Base{Uuid: uuid}}).First(&footprint)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return &footprint
|
||||
}
|
||||
|
||||
//按照Id查询文件
|
||||
func (this *FootprintDao) CheckByUuid(uuid string) *Footprint {
|
||||
|
||||
// Read
|
||||
var footprint Footprint
|
||||
db := CONTEXT.DB.Where(&Footprint{Base: Base{Uuid: uuid}}).First(&footprint)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return &footprint
|
||||
|
||||
}
|
||||
|
||||
//按分页条件获取分页
|
||||
func (this *FootprintDao) Page(page int, pageSize int, userUuid string, sortArray []builder.OrderPair) *Pager {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
if userUuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}})
|
||||
}
|
||||
|
||||
var conditionDB *gorm.DB
|
||||
conditionDB = CONTEXT.DB.Model(&Footprint{}).Where(wp.Query, wp.Args...)
|
||||
|
||||
count := 0
|
||||
db := conditionDB.Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
var footprints []*Footprint
|
||||
db = conditionDB.Order(this.GetSortString(sortArray)).Offset(page * pageSize).Limit(pageSize).Find(&footprints)
|
||||
this.PanicError(db.Error)
|
||||
pager := NewPager(page, pageSize, count, footprints)
|
||||
|
||||
return pager
|
||||
}
|
||||
|
||||
//创建
|
||||
func (this *FootprintDao) Create(footprint *Footprint) *Footprint {
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
footprint.Uuid = string(timeUUID.String())
|
||||
footprint.CreateTime = time.Now()
|
||||
footprint.UpdateTime = time.Now()
|
||||
footprint.Sort = time.Now().UnixNano() / 1e6
|
||||
db := CONTEXT.DB.Create(footprint)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return footprint
|
||||
}
|
||||
|
||||
//修改一条记录
|
||||
func (this *FootprintDao) Save(footprint *Footprint) *Footprint {
|
||||
|
||||
footprint.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.Save(footprint)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return footprint
|
||||
}
|
||||
|
||||
//删除一条记录
|
||||
func (this *FootprintDao) Delete(footprint *Footprint) {
|
||||
|
||||
db := CONTEXT.DB.Delete(&footprint)
|
||||
this.PanicError(db.Error)
|
||||
}
|
||||
|
||||
//获取一段时间中,总的数量
|
||||
func (this *FootprintDao) CountBetweenTime(startTime time.Time, endTime time.Time) int64 {
|
||||
var count int64
|
||||
db := CONTEXT.DB.Model(&Footprint{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
return count
|
||||
}
|
||||
|
||||
//获取一段时间中UV的数量
|
||||
func (this *FootprintDao) UvBetweenTime(startTime time.Time, endTime time.Time) int64 {
|
||||
var count int64
|
||||
db := CONTEXT.DB.Model(&Footprint{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("COUNT(DISTINCT(ip))")
|
||||
this.PanicError(db.Error)
|
||||
row := db.Row()
|
||||
row.Scan(&count)
|
||||
return count
|
||||
}
|
||||
|
||||
//获取一段时间中平均耗时
|
||||
func (this *FootprintDao) AvgCostBetweenTime(startTime time.Time, endTime time.Time) int64 {
|
||||
var cost float64
|
||||
db := CONTEXT.DB.Model(&Footprint{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("AVG(cost)")
|
||||
this.PanicError(db.Error)
|
||||
row := db.Row()
|
||||
row.Scan(&cost)
|
||||
return int64(cost)
|
||||
}
|
||||
|
||||
|
||||
//执行清理操作
|
||||
func (this *FootprintDao) Cleanup() {
|
||||
this.logger.Info("[FootprintDao]执行清理:清除数据库中所有Footprint记录。")
|
||||
db := CONTEXT.DB.Where("uuid is not null").Delete(Footprint{})
|
||||
this.PanicError(db.Error)
|
||||
}
|
22
code/rest/footprint_model.go
Normal file
22
code/rest/footprint_model.go
Normal file
@ -0,0 +1,22 @@
|
||||
package rest
|
||||
|
||||
import "tank/code/config"
|
||||
|
||||
/**
|
||||
* 系统的所有访问记录均记录在此
|
||||
*/
|
||||
type Footprint struct {
|
||||
Base
|
||||
UserUuid string `json:"userUuid" gorm:"type:char(36)"`
|
||||
Ip string `json:"ip" gorm:"type:varchar(128) not null;index:idx_dt"`
|
||||
Host string `json:"host" gorm:"type:varchar(45) not null"`
|
||||
Uri string `json:"uri" gorm:"type:varchar(255) not null"`
|
||||
Params string `json:"params" gorm:"type:text"`
|
||||
Cost int64 `json:"cost" gorm:"type:bigint(20) not null;default:0"`
|
||||
Success bool `json:"success" gorm:"type:tinyint(1) not null;default:0"`
|
||||
}
|
||||
|
||||
// set File's table name to be `profiles`
|
||||
func (this *Footprint) TableName() string {
|
||||
return config.TABLE_PREFIX + "footprint"
|
||||
}
|
90
code/rest/footprint_service.go
Normal file
90
code/rest/footprint_service.go
Normal file
@ -0,0 +1,90 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"tank/code/config"
|
||||
"tank/code/tool/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
//@Service
|
||||
type FootprintService struct {
|
||||
Bean
|
||||
footprintDao *FootprintDao
|
||||
userDao *UserDao
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *FootprintService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.footprintDao)
|
||||
if b, ok := b.(*FootprintDao); ok {
|
||||
this.footprintDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.userDao)
|
||||
if b, ok := b.(*UserDao); ok {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。
|
||||
func (this *FootprintService) Detail(uuid string) *Footprint {
|
||||
|
||||
footprint := this.footprintDao.CheckByUuid(uuid)
|
||||
|
||||
return footprint
|
||||
}
|
||||
|
||||
//记录访问记录
|
||||
func (this *FootprintService) Trace(writer http.ResponseWriter, request *http.Request, duration time.Duration, success bool) {
|
||||
|
||||
params := make(map[string][]string)
|
||||
|
||||
//POST请求参数
|
||||
values := request.PostForm
|
||||
for key, val := range values {
|
||||
params[key] = val
|
||||
}
|
||||
//GET请求参数
|
||||
values1 := request.URL.Query()
|
||||
for key, val := range values1 {
|
||||
params[key] = val
|
||||
}
|
||||
|
||||
//用json的方式输出返回值。
|
||||
paramsString := "{}"
|
||||
paramsData, err := json.Marshal(params)
|
||||
if err == nil {
|
||||
paramsString = string(paramsData)
|
||||
}
|
||||
|
||||
//将文件信息存入数据库中。
|
||||
footprint := &Footprint{
|
||||
Ip: util.GetIpAddress(request),
|
||||
Host: request.Host,
|
||||
Uri: request.URL.Path,
|
||||
Params: paramsString,
|
||||
Cost: int64(duration / time.Millisecond),
|
||||
Success: success,
|
||||
}
|
||||
|
||||
//有可能DB尚且没有配置 直接打印出内容,并且退出
|
||||
if config.CONFIG.Installed {
|
||||
user := this.findUser(writer, request)
|
||||
userUuid := ""
|
||||
if user != nil {
|
||||
userUuid = user.Uuid
|
||||
}
|
||||
footprint.UserUuid = userUuid
|
||||
footprint = this.footprintDao.Create(footprint)
|
||||
}
|
||||
|
||||
//用json的方式输出返回值。
|
||||
this.logger.Info("Ip:%s Host:%s Uri:%s Params:%s Cost:%d", footprint.Ip, footprint.Host, footprint.Uri, paramsString, int64(duration/time.Millisecond))
|
||||
|
||||
}
|
173
code/rest/image_cache_controller.go
Normal file
173
code/rest/image_cache_controller.go
Normal file
@ -0,0 +1,173 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"tank/code/tool/builder"
|
||||
"tank/code/tool/result"
|
||||
)
|
||||
|
||||
type ImageCacheController struct {
|
||||
BaseController
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *ImageCacheController) Init() {
|
||||
this.BaseController.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.imageCacheDao)
|
||||
if b, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheService)
|
||||
if b, ok := b.(*ImageCacheService); ok {
|
||||
this.imageCacheService = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *ImageCacheController) RegisterRoutes() 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/batch"] = this.Wrap(this.DeleteBatch, USER_ROLE_USER)
|
||||
routeMap["/api/image/cache/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
|
||||
routeMap["/api/image/cache/page"] = this.Wrap(this.Page, USER_ROLE_USER)
|
||||
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//查看某个图片缓存的详情。
|
||||
func (this *ImageCacheController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
if uuid == "" {
|
||||
panic(result.BadRequest("图片缓存的uuid必填"))
|
||||
}
|
||||
|
||||
imageCache := this.imageCacheService.Detail(uuid)
|
||||
|
||||
//验证当前之人是否有权限查看这么详细。
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
if imageCache.UserUuid != user.Uuid {
|
||||
panic("没有权限查看该图片缓存")
|
||||
}
|
||||
}
|
||||
|
||||
return this.Success(imageCache)
|
||||
|
||||
}
|
||||
|
||||
//按照分页的方式获取某个图片缓存夹下图片缓存和子图片缓存夹的列表,通常情况下只有一页。
|
||||
func (this *ImageCacheController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
//如果是根目录,那么就传入root.
|
||||
pageStr := request.FormValue("page")
|
||||
pageSizeStr := request.FormValue("pageSize")
|
||||
orderCreateTime := request.FormValue("orderCreateTime")
|
||||
orderUpdateTime := request.FormValue("orderUpdateTime")
|
||||
orderSort := request.FormValue("orderSort")
|
||||
|
||||
userUuid := request.FormValue("userUuid")
|
||||
matterUuid := request.FormValue("matterUuid")
|
||||
orderSize := request.FormValue("orderSize")
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
userUuid = user.Uuid
|
||||
}
|
||||
|
||||
var page int
|
||||
if pageStr != "" {
|
||||
page, _ = strconv.Atoi(pageStr)
|
||||
}
|
||||
|
||||
pageSize := 200
|
||||
if pageSizeStr != "" {
|
||||
tmp, err := strconv.Atoi(pageSizeStr)
|
||||
if err == nil {
|
||||
pageSize = tmp
|
||||
}
|
||||
}
|
||||
|
||||
sortArray := []builder.OrderPair{
|
||||
{
|
||||
key: "create_time",
|
||||
value: orderCreateTime,
|
||||
},
|
||||
{
|
||||
key: "update_time",
|
||||
value: orderUpdateTime,
|
||||
},
|
||||
{
|
||||
key: "sort",
|
||||
value: orderSort,
|
||||
},
|
||||
|
||||
{
|
||||
key: "size",
|
||||
value: orderSize,
|
||||
},
|
||||
}
|
||||
|
||||
pager := this.imageCacheDao.Page(page, pageSize, userUuid, matterUuid, sortArray)
|
||||
|
||||
return this.Success(pager)
|
||||
}
|
||||
|
||||
//删除一个图片缓存
|
||||
func (this *ImageCacheController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
if uuid == "" {
|
||||
panic(result.BadRequest("图片缓存的uuid必填"))
|
||||
}
|
||||
|
||||
imageCache := this.imageCacheDao.FindByUuid(uuid)
|
||||
|
||||
//判断图片缓存的所属人是否正确
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR && imageCache.UserUuid != user.Uuid {
|
||||
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
|
||||
this.imageCacheDao.Delete(imageCache)
|
||||
|
||||
return this.Success("删除成功!")
|
||||
}
|
||||
|
||||
//删除一系列图片缓存。
|
||||
func (this *ImageCacheController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuids := request.FormValue("uuids")
|
||||
if uuids == "" {
|
||||
panic(result.BadRequest("图片缓存的uuids必填"))
|
||||
}
|
||||
|
||||
uuidArray := strings.Split(uuids, ",")
|
||||
|
||||
for _, uuid := range uuidArray {
|
||||
|
||||
imageCache := this.imageCacheDao.FindByUuid(uuid)
|
||||
|
||||
//判断图片缓存的所属人是否正确
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR && imageCache.UserUuid != user.Uuid {
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
|
||||
this.imageCacheDao.Delete(imageCache)
|
||||
|
||||
}
|
||||
|
||||
return this.Success("删除成功!")
|
||||
}
|
210
code/rest/image_cache_dao.go
Normal file
210
code/rest/image_cache_dao.go
Normal file
@ -0,0 +1,210 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"tank/code/tool/builder"
|
||||
"tank/code/tool/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ImageCacheDao struct {
|
||||
BaseDao
|
||||
}
|
||||
|
||||
//按照Id查询文件
|
||||
func (this *ImageCacheDao) FindByUuid(uuid string) *ImageCache {
|
||||
|
||||
// Read
|
||||
var imageCache ImageCache
|
||||
db := CONTEXT.DB.Where(&ImageCache{Base: Base{Uuid: uuid}}).First(&imageCache)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return &imageCache
|
||||
}
|
||||
|
||||
//按照Id查询文件
|
||||
func (this *ImageCacheDao) CheckByUuid(uuid string) *ImageCache {
|
||||
|
||||
// Read
|
||||
var imageCache ImageCache
|
||||
db := CONTEXT.DB.Where(&ImageCache{Base: Base{Uuid: uuid}}).First(&imageCache)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return &imageCache
|
||||
|
||||
}
|
||||
|
||||
//按照名字查询文件夹
|
||||
func (this *ImageCacheDao) FindByMatterUuidAndMode(matterUuid string, mode string) *ImageCache {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
if matterUuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "matter_uuid = ?", Args: []interface{}{matterUuid}})
|
||||
}
|
||||
|
||||
if mode != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "mode = ?", Args: []interface{}{mode}})
|
||||
}
|
||||
|
||||
var imageCache = &ImageCache{}
|
||||
db := CONTEXT.DB.Model(&ImageCache{}).Where(wp.Query, wp.Args...).First(imageCache)
|
||||
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return imageCache
|
||||
}
|
||||
|
||||
//按照id和userUuid来查找。找不到抛异常。
|
||||
func (this *ImageCacheDao) CheckByUuidAndUserUuid(uuid string, userUuid string) *ImageCache {
|
||||
|
||||
// Read
|
||||
var imageCache = &ImageCache{}
|
||||
db := CONTEXT.DB.Where(&ImageCache{Base: Base{Uuid: uuid}, UserUuid: userUuid}).First(imageCache)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return imageCache
|
||||
|
||||
}
|
||||
|
||||
//获取某个用户的某个文件夹下的某个名字的文件(或文件夹)列表
|
||||
func (this *ImageCacheDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string) []*ImageCache {
|
||||
|
||||
var imageCaches []*ImageCache
|
||||
|
||||
db := CONTEXT.DB.
|
||||
Where(ImageCache{UserUuid: userUuid}).
|
||||
Find(&imageCaches)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return imageCaches
|
||||
}
|
||||
|
||||
//获取某个文件夹下所有的文件和子文件
|
||||
func (this *ImageCacheDao) Page(page int, pageSize int, userUuid string, matterUuid string, sortArray []builder.OrderPair) *Pager {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
if userUuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}})
|
||||
}
|
||||
|
||||
if matterUuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "matter_uuid = ?", Args: []interface{}{matterUuid}})
|
||||
}
|
||||
|
||||
var conditionDB *gorm.DB
|
||||
conditionDB = CONTEXT.DB.Model(&ImageCache{}).Where(wp.Query, wp.Args...)
|
||||
|
||||
count := 0
|
||||
db := conditionDB.Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
var imageCaches []*ImageCache
|
||||
db = conditionDB.Order(this.GetSortString(sortArray)).Offset(page * pageSize).Limit(pageSize).Find(&imageCaches)
|
||||
this.PanicError(db.Error)
|
||||
pager := NewPager(page, pageSize, count, imageCaches)
|
||||
|
||||
return pager
|
||||
}
|
||||
|
||||
//创建
|
||||
func (this *ImageCacheDao) Create(imageCache *ImageCache) *ImageCache {
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
imageCache.Uuid = string(timeUUID.String())
|
||||
imageCache.CreateTime = time.Now()
|
||||
imageCache.UpdateTime = time.Now()
|
||||
imageCache.Sort = time.Now().UnixNano() / 1e6
|
||||
db := CONTEXT.DB.Create(imageCache)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return imageCache
|
||||
}
|
||||
|
||||
//修改一个文件
|
||||
func (this *ImageCacheDao) Save(imageCache *ImageCache) *ImageCache {
|
||||
|
||||
imageCache.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.Save(imageCache)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return imageCache
|
||||
}
|
||||
|
||||
//删除一个文件包括文件夹
|
||||
func (this *ImageCacheDao) deleteFileAndDir(imageCache *ImageCache) {
|
||||
|
||||
filePath := GetUserCacheRootDir(imageCache.Username) + imageCache.Path
|
||||
|
||||
dirPath := filepath.Dir(filePath)
|
||||
|
||||
//删除文件
|
||||
err := os.Remove(filePath)
|
||||
if err != nil {
|
||||
this.logger.Error(fmt.Sprintf("删除磁盘上的文件%s出错 %s", filePath, err.Error()))
|
||||
}
|
||||
|
||||
//如果这一层文件夹是空的,那么删除文件夹本身。
|
||||
util.DeleteEmptyDirRecursive(dirPath)
|
||||
|
||||
}
|
||||
|
||||
//删除一个文件,数据库中删除,物理磁盘上删除。
|
||||
func (this *ImageCacheDao) Delete(imageCache *ImageCache) {
|
||||
|
||||
db := CONTEXT.DB.Delete(&imageCache)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
this.deleteFileAndDir(imageCache)
|
||||
|
||||
}
|
||||
|
||||
//删除一个matter对应的所有缓存
|
||||
func (this *ImageCacheDao) DeleteByMatterUuid(matterUuid string) {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
wp = wp.And(&builder.WherePair{Query: "matter_uuid = ?", Args: []interface{}{matterUuid}})
|
||||
|
||||
//查询出即将删除的图片缓存
|
||||
var imageCaches []*ImageCache
|
||||
db := CONTEXT.DB.Where(wp.Query, wp.Args).Find(&imageCaches)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
//删除文件记录
|
||||
db = CONTEXT.DB.Where(wp.Query, wp.Args).Delete(ImageCache{})
|
||||
this.PanicError(db.Error)
|
||||
|
||||
//删除文件实体
|
||||
for _, imageCache := range imageCaches {
|
||||
this.deleteFileAndDir(imageCache)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//获取一段时间中文件总大小
|
||||
func (this *ImageCacheDao) SizeBetweenTime(startTime time.Time, endTime time.Time) int64 {
|
||||
var size int64
|
||||
db := CONTEXT.DB.Model(&ImageCache{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("SUM(size)")
|
||||
this.PanicError(db.Error)
|
||||
row := db.Row()
|
||||
row.Scan(&size)
|
||||
return size
|
||||
}
|
||||
|
||||
|
||||
//执行清理操作
|
||||
func (this *ImageCacheDao) Cleanup() {
|
||||
this.logger.Info("[ImageCacheDao]执行清理:清除数据库中所有ImageCache记录。")
|
||||
db := CONTEXT.DB.Where("uuid is not null").Delete(ImageCache{})
|
||||
this.PanicError(db.Error)
|
||||
}
|
||||
|
32
code/rest/image_cache_model.go
Normal file
32
code/rest/image_cache_model.go
Normal file
@ -0,0 +1,32 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"tank/code/config"
|
||||
)
|
||||
|
||||
/**
|
||||
* 图片缓存,对于那些处理过的图片,统一管理在这里。
|
||||
*/
|
||||
type ImageCache struct {
|
||||
Base
|
||||
Name string `json:"name" gorm:"type:varchar(255) not null"`
|
||||
UserUuid string `json:"userUuid" gorm:"type:char(36)"`
|
||||
Username string `json:"username" gorm:"type:varchar(45) not null"`
|
||||
MatterUuid string `json:"matterUuid" gorm:"type:char(36);index:idx_mu"`
|
||||
MatterName string `json:"matterName" gorm:"type:varchar(255) not null"`
|
||||
Mode string `json:"mode" gorm:"type:varchar(512)"`
|
||||
Md5 string `json:"md5" gorm:"type:varchar(45)"`
|
||||
Size int64 `json:"size" gorm:"type:bigint(20) not null;default:0"`
|
||||
Path string `json:"path" gorm:"type:varchar(512)"`
|
||||
Matter *Matter `json:"matter" gorm:"-"`
|
||||
}
|
||||
|
||||
// set File's table name to be `profiles`
|
||||
func (this *ImageCache) TableName() string {
|
||||
return config.TABLE_PREFIX + "image_cache"
|
||||
}
|
||||
|
||||
// 获取该ImageCache的绝对路径。path代表的是相对路径。
|
||||
func (this *ImageCache) AbsolutePath() string {
|
||||
return GetUserCacheRootDir(this.Username) + this.Path
|
||||
}
|
244
code/rest/image_cache_service.go
Normal file
244
code/rest/image_cache_service.go
Normal file
@ -0,0 +1,244 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/disintegration/imaging"
|
||||
"image"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"tank/code/tool/util"
|
||||
)
|
||||
|
||||
//@Service
|
||||
type ImageCacheService struct {
|
||||
Bean
|
||||
imageCacheDao *ImageCacheDao
|
||||
userDao *UserDao
|
||||
matterDao *MatterDao
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *ImageCacheService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.imageCacheDao)
|
||||
if b, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.userDao)
|
||||
if b, ok := b.(*UserDao); ok {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterDao)
|
||||
if b, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。
|
||||
func (this *ImageCacheService) Detail(uuid string) *ImageCache {
|
||||
|
||||
imageCache := this.imageCacheDao.CheckByUuid(uuid)
|
||||
|
||||
return imageCache
|
||||
}
|
||||
|
||||
//获取预处理时必要的参数
|
||||
func (this *ImageCacheService) ResizeParams(request *http.Request) (needProcess bool, resizeMode string, resizeWidth int, resizeHeight int) {
|
||||
var err error
|
||||
|
||||
//1.0 模式准备逐步废弃掉
|
||||
if request.FormValue("imageProcess") == "resize" {
|
||||
//老模式使用 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")
|
||||
arr := strings.Split(imageResizeStr, "_")
|
||||
if len(arr) != 3 {
|
||||
panic("参数不符合规范,格式要求为mode_w_h")
|
||||
}
|
||||
|
||||
imageResizeM := arr[0]
|
||||
if imageResizeM == "" {
|
||||
imageResizeM = "fit"
|
||||
} else if imageResizeM != "fit" && imageResizeM != "fill" && imageResizeM != "fixed" {
|
||||
panic("imageResizeM参数错误")
|
||||
}
|
||||
imageResizeWStr := arr[1]
|
||||
var imageResizeW int
|
||||
if imageResizeWStr != "" {
|
||||
imageResizeW, err = strconv.Atoi(imageResizeWStr)
|
||||
this.PanicError(err)
|
||||
if imageResizeW < 0 || imageResizeW > 4096 {
|
||||
panic("缩放尺寸不能超过4096")
|
||||
}
|
||||
}
|
||||
imageResizeHStr := arr[2]
|
||||
var imageResizeH int
|
||||
if imageResizeHStr != "" {
|
||||
imageResizeH, err = strconv.Atoi(imageResizeHStr)
|
||||
this.PanicError(err)
|
||||
if imageResizeH < 0 || imageResizeH > 4096 {
|
||||
panic("缩放尺寸不能超过4096")
|
||||
}
|
||||
}
|
||||
return true, imageResizeM, imageResizeW, imageResizeH
|
||||
} else {
|
||||
return false, "", 0, 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//图片预处理功能。
|
||||
func (this *ImageCacheService) ResizeImage(request *http.Request, filePath string) *image.NRGBA {
|
||||
|
||||
diskFile, err := os.Open(filePath)
|
||||
this.PanicError(err)
|
||||
defer func() {
|
||||
e := diskFile.Close()
|
||||
this.PanicError(e)
|
||||
}()
|
||||
|
||||
_, imageResizeM, imageResizeW, imageResizeH := this.ResizeParams(request)
|
||||
|
||||
//单边缩略
|
||||
if imageResizeM == "fit" {
|
||||
//将图缩略成宽度为100,高度按比例处理。
|
||||
if imageResizeW != 0 {
|
||||
src, err := imaging.Decode(diskFile)
|
||||
this.PanicError(err)
|
||||
return imaging.Resize(src, imageResizeW, 0, imaging.Lanczos)
|
||||
|
||||
} else if imageResizeH != 0 {
|
||||
//将图缩略成高度为100,宽度按比例处理。
|
||||
src, err := imaging.Decode(diskFile)
|
||||
this.PanicError(err)
|
||||
return imaging.Resize(src, 0, imageResizeH, imaging.Lanczos)
|
||||
|
||||
} else {
|
||||
panic("单边缩略必须指定宽或者高")
|
||||
}
|
||||
} else if imageResizeM == "fill" {
|
||||
//固定宽高,自动裁剪
|
||||
if imageResizeW > 0 && imageResizeH > 0 {
|
||||
src, err := imaging.Decode(diskFile)
|
||||
this.PanicError(err)
|
||||
return imaging.Fill(src, imageResizeW, imageResizeH, imaging.Center, imaging.Lanczos)
|
||||
|
||||
} else {
|
||||
panic("固定宽高,自动裁剪 必须同时指定宽和高")
|
||||
}
|
||||
} else if imageResizeM == "fixed" {
|
||||
//强制宽高缩略
|
||||
if imageResizeW > 0 && imageResizeH > 0 {
|
||||
src, err := imaging.Decode(diskFile)
|
||||
this.PanicError(err)
|
||||
return imaging.Resize(src, imageResizeW, imageResizeH, imaging.Lanczos)
|
||||
|
||||
} else {
|
||||
panic("强制宽高缩略必须同时指定宽和高")
|
||||
}
|
||||
} else {
|
||||
panic("不支持" + imageResizeM + "处理模式")
|
||||
}
|
||||
}
|
||||
|
||||
//缓存一张图片
|
||||
func (this *ImageCacheService) cacheImage(writer http.ResponseWriter, request *http.Request, matter *Matter) *ImageCache {
|
||||
|
||||
//当前的文件是否是图片,只有图片才能处理。
|
||||
extension := util.GetExtension(matter.Name)
|
||||
formats := map[string]imaging.Format{
|
||||
".jpg": imaging.JPEG,
|
||||
".jpeg": imaging.JPEG,
|
||||
".png": imaging.PNG,
|
||||
".tif": imaging.TIFF,
|
||||
".tiff": imaging.TIFF,
|
||||
".bmp": imaging.BMP,
|
||||
".gif": imaging.GIF,
|
||||
}
|
||||
|
||||
_, imageResizeM, imageResizeW, imageResizeH := this.ResizeParams(request)
|
||||
mode := fmt.Sprintf("%s_%d_%d", imageResizeM, imageResizeW, imageResizeH)
|
||||
|
||||
format, ok := formats[extension]
|
||||
if !ok {
|
||||
panic("该图片格式不支持处理")
|
||||
}
|
||||
|
||||
user := this.userDao.FindByUuid(matter.UserUuid)
|
||||
|
||||
//resize图片
|
||||
dstImage := this.ResizeImage(request, matter.AbsolutePath())
|
||||
|
||||
cacheImageName := util.GetSimpleFileName(matter.Name) + "_" + mode + extension
|
||||
cacheImageRelativePath := util.GetSimpleFileName(matter.Path) + "_" + mode + extension
|
||||
cacheImageAbsolutePath := GetUserCacheRootDir(user.Username) + util.GetSimpleFileName(matter.Path) + "_" + mode + extension
|
||||
|
||||
//创建目录。
|
||||
dir := filepath.Dir(cacheImageAbsolutePath)
|
||||
util.MakeDirAll(dir)
|
||||
|
||||
fileWriter, err := os.Create(cacheImageAbsolutePath)
|
||||
this.PanicError(err)
|
||||
defer func() {
|
||||
e := fileWriter.Close()
|
||||
this.PanicError(e)
|
||||
}()
|
||||
|
||||
//处理后的图片存放在本地
|
||||
err = imaging.Encode(fileWriter, dstImage, format)
|
||||
this.PanicError(err)
|
||||
|
||||
//获取新文件的大小
|
||||
fileInfo, err := fileWriter.Stat()
|
||||
this.PanicError(err)
|
||||
|
||||
//相关信息写到缓存中去
|
||||
imageCache := &ImageCache{
|
||||
Name: cacheImageName,
|
||||
UserUuid: matter.UserUuid,
|
||||
Username: user.Username,
|
||||
MatterUuid: matter.Uuid,
|
||||
MatterName: matter.Name,
|
||||
Mode: mode,
|
||||
Size: fileInfo.Size(),
|
||||
Path: cacheImageRelativePath,
|
||||
}
|
||||
this.imageCacheDao.Create(imageCache)
|
||||
|
||||
return imageCache
|
||||
}
|
466
code/rest/install_controller.go
Normal file
466
code/rest/install_controller.go
Normal file
@ -0,0 +1,466 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"tank/code/config"
|
||||
"tank/code/tool/builder"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
//安装程序的接口,只有安装阶段可以访问。
|
||||
type InstallController struct {
|
||||
BaseController
|
||||
uploadTokenDao *UploadTokenDao
|
||||
downloadTokenDao *DownloadTokenDao
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *InstallController) Init() {
|
||||
this.BaseController.Init()
|
||||
|
||||
//手动装填本实例的Bean.
|
||||
b := CONTEXT.GetBean(this.uploadTokenDao)
|
||||
if c, ok := b.(*UploadTokenDao); ok {
|
||||
this.uploadTokenDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.downloadTokenDao)
|
||||
if c, ok := b.(*DownloadTokenDao); ok {
|
||||
this.downloadTokenDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterDao)
|
||||
if c, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterService)
|
||||
if c, ok := b.(*MatterService); ok {
|
||||
this.matterService = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheDao)
|
||||
if c, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = c
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheService)
|
||||
if c, ok := b.(*ImageCacheService); ok {
|
||||
this.imageCacheService = c
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *InstallController) RegisterRoutes() 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/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/admin/list"] = this.Wrap(this.AdminList, USER_ROLE_GUEST)
|
||||
routeMap["/api/install/create/admin"] = this.Wrap(this.CreateAdmin, USER_ROLE_GUEST)
|
||||
routeMap["/api/install/validate/admin"] = this.Wrap(this.ValidateAdmin, USER_ROLE_GUEST)
|
||||
routeMap["/api/install/finish"] = this.Wrap(this.Finish, USER_ROLE_GUEST)
|
||||
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//获取数据库连接
|
||||
func (this *InstallController) openDbConnection(writer http.ResponseWriter, request *http.Request) *gorm.DB {
|
||||
mysqlPortStr := request.FormValue("mysqlPort")
|
||||
mysqlHost := request.FormValue("mysqlHost")
|
||||
mysqlSchema := request.FormValue("mysqlSchema")
|
||||
mysqlUsername := request.FormValue("mysqlUsername")
|
||||
mysqlPassword := request.FormValue("mysqlPassword")
|
||||
|
||||
var mysqlPort int
|
||||
if mysqlPortStr != "" {
|
||||
tmp, err := strconv.Atoi(mysqlPortStr)
|
||||
this.PanicError(err)
|
||||
mysqlPort = tmp
|
||||
}
|
||||
|
||||
mysqlUrl := util.GetMysqlUrl(mysqlPort, mysqlHost, mysqlSchema, mysqlUsername, mysqlPassword)
|
||||
|
||||
this.logger.Info("连接MySQL %s", mysqlUrl)
|
||||
|
||||
var err error = nil
|
||||
db, err := gorm.Open("mysql", mysqlUrl)
|
||||
this.PanicError(err)
|
||||
|
||||
db.LogMode(false)
|
||||
|
||||
return db
|
||||
|
||||
}
|
||||
|
||||
//关闭数据库连接
|
||||
func (this *InstallController) closeDbConnection(db *gorm.DB) {
|
||||
|
||||
if db != nil {
|
||||
err := db.Close()
|
||||
if err != nil {
|
||||
this.logger.Error("关闭数据库连接出错 %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//根据表名获取建表SQL语句
|
||||
func (this *InstallController) getCreateSQLFromFile(tableName string) string {
|
||||
|
||||
//1. 从当前安装目录db下去寻找建表文件。
|
||||
homePath := util.GetHomePath()
|
||||
filePath := homePath + "/db/" + tableName + ".sql"
|
||||
exists, err := util.PathExists(filePath)
|
||||
if err != nil {
|
||||
panic(result.Server("从安装目录判断建表语句文件是否存在时出错!"))
|
||||
}
|
||||
|
||||
//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, err = util.PathExists(filePath)
|
||||
if err != nil {
|
||||
panic(result.Server("从GOPATH判断建表语句文件是否存在时出错!"))
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
//挣扎一下,尝试获取建表语句。
|
||||
db := gormDb.Unscoped()
|
||||
scope := db.NewScope(entity)
|
||||
|
||||
tableName := scope.TableName()
|
||||
modelStruct := scope.GetModelStruct()
|
||||
allFields := modelStruct.StructFields
|
||||
var missingFields = make([]*gorm.StructField, 0)
|
||||
|
||||
if !scope.Dialect().HasTable(tableName) {
|
||||
missingFields = append(missingFields, allFields...)
|
||||
|
||||
return false, allFields, missingFields
|
||||
} else {
|
||||
|
||||
for _, field := range allFields {
|
||||
if !scope.Dialect().HasColumn(tableName, field.DBName) {
|
||||
if field.IsNormal {
|
||||
missingFields = append(missingFields, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, allFields, missingFields
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//根据表名获取建表SQL语句
|
||||
func (this *InstallController) getTableMetaList(db *gorm.DB) []*InstallTableInfo {
|
||||
|
||||
var tableNames = []IBase{&Dashboard{}, &DownloadToken{}, &Footprint{}, &ImageCache{}, &Matter{}, &Preference{}, &Session{}, &UploadToken{}, &User{}}
|
||||
var installTableInfos []*InstallTableInfo
|
||||
|
||||
for _, iBase := range tableNames {
|
||||
exist, allFields, missingFields := this.getTableMeta(db, iBase)
|
||||
installTableInfos = append(installTableInfos, &InstallTableInfo{
|
||||
Name: iBase.TableName(),
|
||||
TableExist: exist,
|
||||
AllFields: allFields,
|
||||
MissingFields: missingFields,
|
||||
})
|
||||
}
|
||||
|
||||
return installTableInfos
|
||||
}
|
||||
|
||||
//验证表结构是否完整。会直接抛出异常
|
||||
func (this *InstallController) validateTableMetaList(tableInfoList []*InstallTableInfo) {
|
||||
|
||||
for _, tableInfo := range tableInfoList {
|
||||
if tableInfo.TableExist {
|
||||
if len(tableInfo.MissingFields) != 0 {
|
||||
|
||||
var strs []string
|
||||
for _, v := range tableInfo.MissingFields {
|
||||
strs = append(strs, v.DBName)
|
||||
}
|
||||
|
||||
panic(result.BadRequest(fmt.Sprintf("%s 表的以下字段缺失:%v", tableInfo.Name, strs)))
|
||||
}
|
||||
} else {
|
||||
panic(result.BadRequest(tableInfo.Name + "表不存在"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//验证数据库连接
|
||||
func (this *InstallController) Verify(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
db := this.openDbConnection(writer, request)
|
||||
defer this.closeDbConnection(db)
|
||||
|
||||
this.logger.Info("Ping一下数据库")
|
||||
err := db.DB().Ping()
|
||||
this.PanicError(err)
|
||||
|
||||
return this.Success("OK")
|
||||
}
|
||||
|
||||
//获取需要安装的数据库表
|
||||
func (this *InstallController) TableInfoList(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
db := this.openDbConnection(writer, request)
|
||||
defer this.closeDbConnection(db)
|
||||
|
||||
return this.Success(this.getTableMetaList(db))
|
||||
}
|
||||
|
||||
//创建缺失数据库和表
|
||||
func (this *InstallController) CreateTable(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
var tableNames = []IBase{&Dashboard{}, &DownloadToken{}, &Footprint{}, &ImageCache{}, &Matter{}, &Preference{}, &Session{}, &UploadToken{}, &User{}}
|
||||
var installTableInfos []*InstallTableInfo
|
||||
|
||||
db := this.openDbConnection(writer, request)
|
||||
defer this.closeDbConnection(db)
|
||||
|
||||
for _, iBase := range tableNames {
|
||||
|
||||
//补全缺失字段或者创建数据库表
|
||||
db1 := db.AutoMigrate(iBase)
|
||||
this.PanicError(db1.Error)
|
||||
|
||||
exist, allFields, missingFields := this.getTableMeta(db, iBase)
|
||||
installTableInfos = append(installTableInfos, &InstallTableInfo{
|
||||
Name: iBase.TableName(),
|
||||
TableExist: exist,
|
||||
AllFields: allFields,
|
||||
MissingFields: missingFields,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return this.Success(installTableInfos)
|
||||
|
||||
}
|
||||
|
||||
//获取管理员列表(10条记录)
|
||||
func (this *InstallController) AdminList(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
db := this.openDbConnection(writer, request)
|
||||
defer this.closeDbConnection(db)
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
wp = wp.And(&builder.WherePair{Query: "role = ?", Args: []interface{}{USER_ROLE_ADMINISTRATOR}})
|
||||
|
||||
var users []*User
|
||||
db = db.Where(wp.Query, wp.Args...).Offset(0).Limit(10).Find(&users)
|
||||
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return this.Success(users)
|
||||
}
|
||||
|
||||
//创建管理员
|
||||
func (this *InstallController) CreateAdmin(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
db := this.openDbConnection(writer, request)
|
||||
defer this.closeDbConnection(db)
|
||||
|
||||
adminUsername := request.FormValue("adminUsername")
|
||||
adminEmail := request.FormValue("adminEmail")
|
||||
adminPassword := request.FormValue("adminPassword")
|
||||
|
||||
//验证超级管理员的信息
|
||||
if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, adminUsername); !m {
|
||||
panic(result.BadRequest(`超级管理员用户名必填,且只能包含字母,数字和'_''`))
|
||||
}
|
||||
|
||||
if len(adminPassword) < 6 {
|
||||
panic(result.BadRequest(`超级管理员密码长度至少为6位`))
|
||||
}
|
||||
|
||||
if adminEmail == "" {
|
||||
panic(result.BadRequest(`超级管理员邮箱必填`))
|
||||
}
|
||||
|
||||
//检查是否有重复。
|
||||
var count1 int64
|
||||
db1 := db.Model(&User{}).Where("email = ?", adminEmail).Count(&count1)
|
||||
this.PanicError(db1.Error)
|
||||
if count1 > 0 {
|
||||
panic(result.BadRequest(`该邮箱已存在`))
|
||||
}
|
||||
|
||||
var count2 int64
|
||||
db2 := db.Model(&User{}).Where("username = ?", adminUsername).Count(&count2)
|
||||
this.PanicError(db2.Error)
|
||||
if count2 > 0 {
|
||||
panic(result.BadRequest(`该用户名已存在`))
|
||||
}
|
||||
|
||||
user := &User{}
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
user.Uuid = string(timeUUID.String())
|
||||
user.CreateTime = time.Now()
|
||||
user.UpdateTime = time.Now()
|
||||
user.LastTime = time.Now()
|
||||
user.Sort = time.Now().UnixNano() / 1e6
|
||||
user.Role = USER_ROLE_ADMINISTRATOR
|
||||
user.Username = adminUsername
|
||||
user.Password = util.GetBcrypt(adminPassword)
|
||||
user.Email = adminEmail
|
||||
user.Phone = ""
|
||||
user.Gender = USER_GENDER_UNKNOWN
|
||||
user.SizeLimit = -1
|
||||
user.Status = USER_STATUS_OK
|
||||
|
||||
db3 := db.Create(user)
|
||||
this.PanicError(db3.Error)
|
||||
|
||||
return this.Success("OK")
|
||||
|
||||
}
|
||||
|
||||
//(如果数据库中本身存在管理员了)验证管理员
|
||||
func (this *InstallController) ValidateAdmin(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
db := this.openDbConnection(writer, request)
|
||||
defer this.closeDbConnection(db)
|
||||
|
||||
adminEmail := request.FormValue("adminEmail")
|
||||
adminPassword := request.FormValue("adminPassword")
|
||||
|
||||
//验证超级管理员的信息
|
||||
if adminEmail == "" {
|
||||
panic(result.BadRequest(`超级管理员邮箱必填`))
|
||||
}
|
||||
if len(adminPassword) < 6 {
|
||||
panic(result.BadRequest(`超级管理员密码长度至少为6位`))
|
||||
}
|
||||
|
||||
var existEmailUser = &User{}
|
||||
db = db.Where(&User{Email: adminEmail}).First(existEmailUser)
|
||||
if db.Error != nil {
|
||||
panic(result.BadRequest(fmt.Sprintf("%s对应的用户不存在", adminEmail)))
|
||||
}
|
||||
|
||||
if !util.MatchBcrypt(adminPassword, existEmailUser.Password) {
|
||||
panic(result.BadRequest("邮箱或密码错误"))
|
||||
}
|
||||
|
||||
if existEmailUser.Role != USER_ROLE_ADMINISTRATOR {
|
||||
panic(result.BadRequest("该账号不是管理员"))
|
||||
}
|
||||
|
||||
return this.Success("OK")
|
||||
|
||||
}
|
||||
|
||||
//完成系统安装
|
||||
func (this *InstallController) Finish(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
mysqlPortStr := request.FormValue("mysqlPort")
|
||||
mysqlHost := request.FormValue("mysqlHost")
|
||||
mysqlSchema := request.FormValue("mysqlSchema")
|
||||
mysqlUsername := request.FormValue("mysqlUsername")
|
||||
mysqlPassword := request.FormValue("mysqlPassword")
|
||||
|
||||
var mysqlPort int
|
||||
if mysqlPortStr != "" {
|
||||
tmp, err := strconv.Atoi(mysqlPortStr)
|
||||
this.PanicError(err)
|
||||
mysqlPort = tmp
|
||||
}
|
||||
|
||||
//要求数据库连接通畅
|
||||
db := this.openDbConnection(writer, request)
|
||||
defer this.closeDbConnection(db)
|
||||
|
||||
//要求数据库完整。
|
||||
tableMetaList := this.getTableMetaList(db)
|
||||
this.validateTableMetaList(tableMetaList)
|
||||
|
||||
//要求至少有一名管理员。
|
||||
var count1 int64
|
||||
db1 := db.Model(&User{}).Where("role = ?", USER_ROLE_ADMINISTRATOR).Count(&count1)
|
||||
this.PanicError(db1.Error)
|
||||
if count1 == 0 {
|
||||
panic(result.BadRequest(`请至少配置一名管理员`))
|
||||
}
|
||||
|
||||
var configItem = &config.ConfigItem{
|
||||
//默认监听端口号
|
||||
ServerPort: config.CONFIG.ServerPort,
|
||||
//上传的文件路径,要求不以/结尾。如果没有指定,默认在根目录下的matter文件夹中。eg: /var/www/matter
|
||||
MatterPath: config.CONFIG.MatterPath,
|
||||
//mysql相关配置。
|
||||
//数据库端口
|
||||
MysqlPort: mysqlPort,
|
||||
//数据库Host
|
||||
MysqlHost: mysqlHost,
|
||||
//数据库名字
|
||||
MysqlSchema: mysqlSchema,
|
||||
//用户名
|
||||
MysqlUsername: mysqlUsername,
|
||||
//密码
|
||||
MysqlPassword: mysqlPassword,
|
||||
}
|
||||
|
||||
//用json的方式输出返回值。为了让格式更好看。
|
||||
jsonStr, _ := jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(configItem, "", " ")
|
||||
|
||||
//写入到配置文件中(不能使用os.O_APPEND 否则会追加)
|
||||
filePath := util.GetConfPath() + "/tank.json"
|
||||
f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0777)
|
||||
this.PanicError(err)
|
||||
_, err = f.Write(jsonStr)
|
||||
this.PanicError(err)
|
||||
err = f.Close()
|
||||
this.PanicError(err)
|
||||
|
||||
//通知配置文件安装完毕。
|
||||
config.CONFIG.InstallOk()
|
||||
|
||||
//通知全局上下文,说系统安装好了
|
||||
CONTEXT.InstallOk()
|
||||
|
||||
return this.Success("OK")
|
||||
}
|
13
code/rest/install_model.go
Normal file
13
code/rest/install_model.go
Normal file
@ -0,0 +1,13 @@
|
||||
package rest
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
/**
|
||||
* 表名对应的表结构
|
||||
*/
|
||||
type InstallTableInfo struct {
|
||||
Name string `json:"name"`
|
||||
TableExist bool `json:"tableExist"`
|
||||
AllFields []*gorm.StructField `json:"allFields"`
|
||||
MissingFields []*gorm.StructField `json:"missingFields"`
|
||||
}
|
444
code/rest/matter_controller.go
Normal file
444
code/rest/matter_controller.go
Normal file
@ -0,0 +1,444 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"tank/code/tool/builder"
|
||||
"tank/code/tool/result"
|
||||
)
|
||||
|
||||
type MatterController struct {
|
||||
BaseController
|
||||
matterDao *MatterDao
|
||||
matterService *MatterService
|
||||
downloadTokenDao *DownloadTokenDao
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
}
|
||||
|
||||
//初始化方法 start to develop v3.
|
||||
func (this *MatterController) Init() {
|
||||
this.BaseController.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.matterDao)
|
||||
if b, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.matterService)
|
||||
if b, ok := b.(*MatterService); ok {
|
||||
this.matterService = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.downloadTokenDao)
|
||||
if b, ok := b.(*DownloadTokenDao); ok {
|
||||
this.downloadTokenDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheDao)
|
||||
if b, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = b
|
||||
}
|
||||
b = CONTEXT.GetBean(this.imageCacheService)
|
||||
if b, ok := b.(*ImageCacheService); ok {
|
||||
this.imageCacheService = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *MatterController) RegisterRoutes() 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/upload"] = this.Wrap(this.Upload, USER_ROLE_USER)
|
||||
routeMap["/api/matter/crawl"] = this.Wrap(this.Crawl, USER_ROLE_USER)
|
||||
routeMap["/api/matter/delete"] = this.Wrap(this.Delete, USER_ROLE_USER)
|
||||
routeMap["/api/matter/delete/batch"] = this.Wrap(this.DeleteBatch, USER_ROLE_USER)
|
||||
routeMap["/api/matter/rename"] = this.Wrap(this.Rename, USER_ROLE_USER)
|
||||
routeMap["/api/matter/change/privacy"] = this.Wrap(this.ChangePrivacy, USER_ROLE_USER)
|
||||
routeMap["/api/matter/move"] = this.Wrap(this.Move, USER_ROLE_USER)
|
||||
routeMap["/api/matter/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
|
||||
routeMap["/api/matter/page"] = this.Wrap(this.Page, USER_ROLE_USER)
|
||||
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//查看某个文件的详情。
|
||||
func (this *MatterController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
if uuid == "" {
|
||||
panic(result.BadRequest("文件的uuid必填"))
|
||||
}
|
||||
|
||||
matter := this.matterService.Detail(uuid)
|
||||
|
||||
//验证当前之人是否有权限查看这么详细。
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
if matter.UserUuid != user.Uuid {
|
||||
panic("没有权限查看该文件")
|
||||
}
|
||||
}
|
||||
|
||||
return this.Success(matter)
|
||||
|
||||
}
|
||||
|
||||
//按照分页的方式获取某个文件夹下文件和子文件夹的列表,通常情况下只有一页。
|
||||
func (this *MatterController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
//如果是根目录,那么就传入root.
|
||||
pageStr := request.FormValue("page")
|
||||
pageSizeStr := request.FormValue("pageSize")
|
||||
orderCreateTime := request.FormValue("orderCreateTime")
|
||||
orderUpdateTime := request.FormValue("orderUpdateTime")
|
||||
orderSort := request.FormValue("orderSort")
|
||||
orderTimes := request.FormValue("orderTimes")
|
||||
|
||||
puuid := request.FormValue("puuid")
|
||||
userUuid := request.FormValue("userUuid")
|
||||
name := request.FormValue("name")
|
||||
dir := request.FormValue("dir")
|
||||
alien := request.FormValue("alien")
|
||||
orderDir := request.FormValue("orderDir")
|
||||
orderSize := request.FormValue("orderSize")
|
||||
orderName := request.FormValue("orderName")
|
||||
extensionsStr := request.FormValue("extensions")
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
userUuid = user.Uuid
|
||||
}
|
||||
|
||||
var page int
|
||||
if pageStr != "" {
|
||||
page, _ = strconv.Atoi(pageStr)
|
||||
}
|
||||
|
||||
pageSize := 200
|
||||
if pageSizeStr != "" {
|
||||
tmp, err := strconv.Atoi(pageSizeStr)
|
||||
if err == nil {
|
||||
pageSize = tmp
|
||||
}
|
||||
}
|
||||
|
||||
//筛选后缀名
|
||||
var extensions []string
|
||||
if extensionsStr != "" {
|
||||
extensions = strings.Split(extensionsStr, ",")
|
||||
}
|
||||
|
||||
sortArray := []builder.OrderPair{
|
||||
{
|
||||
key: "dir",
|
||||
value: orderDir,
|
||||
},
|
||||
{
|
||||
key: "create_time",
|
||||
value: orderCreateTime,
|
||||
},
|
||||
{
|
||||
key: "update_time",
|
||||
value: orderUpdateTime,
|
||||
},
|
||||
{
|
||||
key: "sort",
|
||||
value: orderSort,
|
||||
},
|
||||
{
|
||||
key: "size",
|
||||
value: orderSize,
|
||||
},
|
||||
{
|
||||
key: "name",
|
||||
value: orderName,
|
||||
},
|
||||
{
|
||||
key: "times",
|
||||
value: orderTimes,
|
||||
},
|
||||
}
|
||||
|
||||
pager := this.matterDao.Page(page, pageSize, puuid, userUuid, name, dir, alien, extensions, sortArray)
|
||||
|
||||
return this.Success(pager)
|
||||
}
|
||||
|
||||
//创建一个文件夹。
|
||||
func (this *MatterController) CreateDirectory(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
puuid := request.FormValue("puuid")
|
||||
name := request.FormValue("name")
|
||||
userUuid := request.FormValue("userUuid")
|
||||
|
||||
//管理员可以指定给某个用户创建文件夹。
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
userUuid = user.Uuid
|
||||
}
|
||||
user = this.userDao.CheckByUuid(userUuid)
|
||||
|
||||
//找到父级matter
|
||||
var dirMatter *Matter
|
||||
if puuid == MATTER_ROOT {
|
||||
dirMatter = NewRootMatter(user)
|
||||
} else {
|
||||
dirMatter = this.matterDao.CheckByUuid(puuid)
|
||||
}
|
||||
|
||||
matter := this.matterService.AtomicCreateDirectory(dirMatter, name, user);
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//上传文件
|
||||
func (this *MatterController) Upload(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
userUuid := request.FormValue("userUuid")
|
||||
puuid := request.FormValue("puuid")
|
||||
privacyStr := request.FormValue("privacy")
|
||||
file, handler, err := request.FormFile("file")
|
||||
this.PanicError(err)
|
||||
defer func() {
|
||||
err := file.Close()
|
||||
this.PanicError(err)
|
||||
}()
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
//管理员可以传到指定用户的目录下。
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
userUuid = user.Uuid
|
||||
}
|
||||
user = this.userDao.CheckByUuid(userUuid)
|
||||
|
||||
privacy := privacyStr == TRUE
|
||||
|
||||
err = request.ParseMultipartForm(32 << 20)
|
||||
this.PanicError(err)
|
||||
|
||||
//对于IE浏览器,filename可能包含了路径。
|
||||
fileName := handler.Filename
|
||||
pos := strings.LastIndex(fileName, "\\")
|
||||
if pos != -1 {
|
||||
fileName = fileName[pos+1:]
|
||||
}
|
||||
pos = strings.LastIndex(fileName, "/")
|
||||
if pos != -1 {
|
||||
fileName = fileName[pos+1:]
|
||||
}
|
||||
|
||||
dirMatter := this.matterDao.CheckWithRootByUuid(puuid, user)
|
||||
|
||||
matter := this.matterService.AtomicUpload(file, user, dirMatter, fileName, privacy)
|
||||
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//从一个Url中去爬取资源
|
||||
func (this *MatterController) Crawl(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
userUuid := request.FormValue("userUuid")
|
||||
puuid := request.FormValue("puuid")
|
||||
url := request.FormValue("url")
|
||||
privacyStr := request.FormValue("privacy")
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR {
|
||||
userUuid = user.Uuid
|
||||
} else {
|
||||
if userUuid == "" {
|
||||
userUuid = user.Uuid
|
||||
}
|
||||
}
|
||||
|
||||
user = this.userDao.CheckByUuid(userUuid)
|
||||
|
||||
dirMatter := this.matterDao.CheckWithRootByUuid(puuid, user)
|
||||
|
||||
privacy := false
|
||||
if privacyStr == TRUE {
|
||||
privacy = true
|
||||
}
|
||||
|
||||
if url == "" || (!strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://")) {
|
||||
panic("资源url必填,并且应该以http://或者https://开头")
|
||||
}
|
||||
|
||||
filename := request.FormValue("filename")
|
||||
if filename == "" {
|
||||
panic("文件名必传")
|
||||
}
|
||||
|
||||
matter := this.matterService.AtomicCrawl(url, filename, user, dirMatter, privacy)
|
||||
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//删除一个文件
|
||||
func (this *MatterController) Delete(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
if uuid == "" {
|
||||
panic(result.BadRequest("文件的uuid必填"))
|
||||
}
|
||||
|
||||
matter := this.matterDao.CheckByUuid(uuid)
|
||||
|
||||
//判断文件的所属人是否正确
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != user.Uuid {
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
|
||||
this.matterService.AtomicDelete(matter)
|
||||
|
||||
return this.Success("删除成功!")
|
||||
}
|
||||
|
||||
//删除一系列文件。
|
||||
func (this *MatterController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuids := request.FormValue("uuids")
|
||||
if uuids == "" {
|
||||
panic(result.BadRequest("文件的uuids必填"))
|
||||
}
|
||||
|
||||
uuidArray := strings.Split(uuids, ",")
|
||||
|
||||
for _, uuid := range uuidArray {
|
||||
|
||||
matter := this.matterDao.FindByUuid(uuid)
|
||||
|
||||
//如果matter已经是nil了,直接跳过
|
||||
if matter == nil {
|
||||
this.logger.Warn("%s 对应的文件记录已经不存在了", uuid)
|
||||
continue
|
||||
}
|
||||
|
||||
//判断文件的所属人是否正确
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != user.Uuid {
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
|
||||
this.matterService.AtomicDelete(matter)
|
||||
|
||||
}
|
||||
|
||||
return this.Success("删除成功!")
|
||||
}
|
||||
|
||||
//重命名一个文件或一个文件夹
|
||||
func (this *MatterController) Rename(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
name := request.FormValue("name")
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
|
||||
//找出该文件或者文件夹
|
||||
matter := this.matterDao.CheckByUuid(uuid)
|
||||
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != user.Uuid {
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
|
||||
this.matterService.AtomicRename(matter, name, user)
|
||||
|
||||
return this.Success(matter)
|
||||
}
|
||||
|
||||
//改变一个文件的公私有属性
|
||||
func (this *MatterController) ChangePrivacy(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
uuid := request.FormValue("uuid")
|
||||
privacyStr := request.FormValue("privacy")
|
||||
privacy := false
|
||||
if privacyStr == TRUE {
|
||||
privacy = true
|
||||
}
|
||||
//找出该文件或者文件夹
|
||||
matter := this.matterDao.CheckByUuid(uuid)
|
||||
|
||||
if matter.Privacy == privacy {
|
||||
panic("公私有属性没有改变!")
|
||||
}
|
||||
|
||||
//权限验证
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR && matter.UserUuid != user.Uuid {
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
|
||||
matter.Privacy = privacy
|
||||
this.matterDao.Save(matter)
|
||||
|
||||
return this.Success("设置成功")
|
||||
}
|
||||
|
||||
//将一个文件夹或者文件移入到另一个文件夹下。
|
||||
func (this *MatterController) Move(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
srcUuidsStr := request.FormValue("srcUuids")
|
||||
destUuid := request.FormValue("destUuid")
|
||||
userUuid := request.FormValue("userUuid")
|
||||
|
||||
var srcUuids []string
|
||||
//验证参数。
|
||||
if srcUuidsStr == "" {
|
||||
panic(result.BadRequest("srcUuids参数必填"))
|
||||
} else {
|
||||
srcUuids = strings.Split(srcUuidsStr, ",")
|
||||
}
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR || userUuid == "" {
|
||||
userUuid = user.Uuid
|
||||
}
|
||||
|
||||
user = this.userDao.CheckByUuid(userUuid)
|
||||
|
||||
//验证dest是否有问题
|
||||
var destMatter = this.matterDao.CheckWithRootByUuid(destUuid, user)
|
||||
if !destMatter.Dir {
|
||||
panic(result.BadRequest("目标不是文件夹"))
|
||||
}
|
||||
|
||||
if user.Role != USER_ROLE_ADMINISTRATOR && destMatter.UserUuid != user.Uuid {
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
|
||||
var srcMatters []*Matter
|
||||
//验证src是否有问题。
|
||||
for _, uuid := range srcUuids {
|
||||
//找出该文件或者文件夹
|
||||
srcMatter := this.matterDao.CheckByUuid(uuid)
|
||||
|
||||
if srcMatter.Puuid == destMatter.Uuid {
|
||||
panic(result.BadRequest("没有进行移动,操作无效!"))
|
||||
}
|
||||
|
||||
//判断同级文件夹中是否有同名的文件
|
||||
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, destMatter.Uuid, srcMatter.Dir, srcMatter.Name)
|
||||
|
||||
if count > 0 {
|
||||
panic(result.BadRequest("【" + srcMatter.Name + "】在目标文件夹已经存在了,操作失败。"))
|
||||
}
|
||||
|
||||
//判断和目标文件夹是否是同一个主人。
|
||||
if srcMatter.UserUuid != destMatter.UserUuid {
|
||||
panic("文件和目标文件夹的拥有者不是同一人")
|
||||
}
|
||||
|
||||
srcMatters = append(srcMatters, srcMatter)
|
||||
}
|
||||
|
||||
this.matterService.AtomicMoveBatch(srcMatters, destMatter)
|
||||
|
||||
return this.Success(nil)
|
||||
}
|
372
code/rest/matter_dao.go
Normal file
372
code/rest/matter_dao.go
Normal file
@ -0,0 +1,372 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"os"
|
||||
"tank/code/config"
|
||||
"tank/code/tool/builder"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MatterDao struct {
|
||||
BaseDao
|
||||
imageCacheDao *ImageCacheDao
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *MatterDao) Init() {
|
||||
this.BaseDao.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.imageCacheDao)
|
||||
if b, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = b
|
||||
}
|
||||
}
|
||||
|
||||
//按照Id查询文件
|
||||
func (this *MatterDao) FindByUuid(uuid string) *Matter {
|
||||
|
||||
// Read
|
||||
var matter Matter
|
||||
db := CONTEXT.DB.Where(&Matter{Base: Base{Uuid: uuid}}).First(&matter)
|
||||
if db.Error != nil {
|
||||
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
|
||||
return nil
|
||||
} else {
|
||||
this.PanicError(db.Error)
|
||||
}
|
||||
}
|
||||
return &matter
|
||||
}
|
||||
|
||||
//按照Id查询文件
|
||||
func (this *MatterDao) CheckByUuid(uuid string) *Matter {
|
||||
matter := this.FindByUuid(uuid)
|
||||
if matter == nil {
|
||||
panic(result.NotFound("%s 对应的matter不存在", uuid))
|
||||
}
|
||||
return matter
|
||||
}
|
||||
|
||||
//按照uuid查找一个文件夹,可能返回root对应的matter.
|
||||
func (this *MatterDao) CheckWithRootByUuid(uuid string, user *User) *Matter {
|
||||
|
||||
if uuid == "" {
|
||||
panic(result.BadRequest("uuid cannot be nil."))
|
||||
}
|
||||
|
||||
var matter *Matter
|
||||
if uuid == MATTER_ROOT {
|
||||
if user == nil {
|
||||
panic(result.BadRequest("user cannot be nil."))
|
||||
}
|
||||
matter = NewRootMatter(user)
|
||||
} else {
|
||||
matter = this.CheckByUuid(uuid)
|
||||
}
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//按照path查找一个matter,可能返回root对应的matter.
|
||||
func (this *MatterDao) CheckWithRootByPath(path string, user *User) *Matter {
|
||||
|
||||
var matter *Matter
|
||||
|
||||
if user == nil {
|
||||
panic(result.BadRequest("user cannot be nil."))
|
||||
}
|
||||
|
||||
//目标文件夹matter
|
||||
if path == "" || path == "/" {
|
||||
matter = NewRootMatter(user)
|
||||
} else {
|
||||
matter = this.checkByUserUuidAndPath(user.Uuid, path)
|
||||
}
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//按照名字查询文件夹
|
||||
func (this *MatterDao) FindByUserUuidAndPuuidAndNameAndDirTrue(userUuid string, puuid string, name string) *Matter {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
if userUuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}})
|
||||
}
|
||||
|
||||
if puuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "puuid = ?", Args: []interface{}{puuid}})
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "name = ?", Args: []interface{}{name}})
|
||||
}
|
||||
|
||||
wp = wp.And(&builder.WherePair{Query: "dir = ?", Args: []interface{}{1}})
|
||||
|
||||
var matter = &Matter{}
|
||||
db := CONTEXT.DB.Model(&Matter{}).Where(wp.Query, wp.Args...).First(matter)
|
||||
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//按照id和userUuid来查找。找不到抛异常。
|
||||
func (this *MatterDao) CheckByUuidAndUserUuid(uuid string, userUuid string) *Matter {
|
||||
|
||||
// Read
|
||||
var matter = &Matter{}
|
||||
db := CONTEXT.DB.Where(&Matter{Base: Base{Uuid: uuid}, UserUuid: userUuid}).First(matter)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return matter
|
||||
|
||||
}
|
||||
|
||||
//统计某个用户的某个文件夹下的某个名字的文件(或文件夹)数量。
|
||||
func (this *MatterDao) CountByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) int {
|
||||
|
||||
var matter Matter
|
||||
var count int
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
if puuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "puuid = ?", Args: []interface{}{puuid}})
|
||||
}
|
||||
|
||||
if userUuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}})
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "name = ?", Args: []interface{}{name}})
|
||||
}
|
||||
|
||||
wp = wp.And(&builder.WherePair{Query: "dir = ?", Args: []interface{}{dir}})
|
||||
|
||||
db := CONTEXT.DB.
|
||||
Model(&matter).
|
||||
Where(wp.Query, wp.Args...).
|
||||
Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
//获取某个用户的某个文件夹下的某个名字的文件(或文件夹)列表
|
||||
func (this *MatterDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) []*Matter {
|
||||
|
||||
var matters []*Matter
|
||||
|
||||
db := CONTEXT.DB.
|
||||
Where(Matter{UserUuid: userUuid, Puuid: puuid, Dir: dir, Name: name}).
|
||||
Find(&matters)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return matters
|
||||
}
|
||||
|
||||
//获取某个文件夹下所有的文件和子文件
|
||||
func (this *MatterDao) List(puuid string, userUuid string, sortArray []builder.OrderPair) []*Matter {
|
||||
var matters []*Matter
|
||||
|
||||
db := CONTEXT.DB.Where(Matter{UserUuid: userUuid, Puuid: puuid}).Order(this.GetSortString(sortArray)).Find(&matters)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return matters
|
||||
}
|
||||
|
||||
//获取某个文件夹下所有的文件和子文件
|
||||
func (this *MatterDao) Page(page int, pageSize int, puuid string, userUuid string, name string, dir string, alien string, extensions []string, sortArray []builder.OrderPair) *Pager {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
if puuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "puuid = ?", Args: []interface{}{puuid}})
|
||||
}
|
||||
|
||||
if userUuid != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}})
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "name LIKE ?", Args: []interface{}{"%" + name + "%"}})
|
||||
}
|
||||
|
||||
if dir == TRUE {
|
||||
wp = wp.And(&builder.WherePair{Query: "dir = ?", Args: []interface{}{1}})
|
||||
} else if dir == FALSE {
|
||||
wp = wp.And(&builder.WherePair{Query: "dir = ?", Args: []interface{}{0}})
|
||||
}
|
||||
|
||||
if alien == TRUE {
|
||||
wp = wp.And(&builder.WherePair{Query: "alien = ?", Args: []interface{}{1}})
|
||||
} else if alien == FALSE {
|
||||
wp = wp.And(&builder.WherePair{Query: "alien = ?", Args: []interface{}{0}})
|
||||
}
|
||||
|
||||
var conditionDB *gorm.DB
|
||||
if extensions != nil && len(extensions) > 0 {
|
||||
var orWp = &builder.WherePair{}
|
||||
|
||||
for _, v := range extensions {
|
||||
orWp = orWp.Or(&builder.WherePair{Query: "name LIKE ?", Args: []interface{}{"%." + v}})
|
||||
}
|
||||
|
||||
conditionDB = CONTEXT.DB.Model(&Matter{}).Where(wp.Query, wp.Args...).Where(orWp.Query, orWp.Args...)
|
||||
} else {
|
||||
conditionDB = CONTEXT.DB.Model(&Matter{}).Where(wp.Query, wp.Args...)
|
||||
}
|
||||
|
||||
count := 0
|
||||
db := conditionDB.Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
var matters []*Matter
|
||||
db = conditionDB.Order(this.GetSortString(sortArray)).Offset(page * pageSize).Limit(pageSize).Find(&matters)
|
||||
this.PanicError(db.Error)
|
||||
pager := NewPager(page, pageSize, count, matters)
|
||||
|
||||
return pager
|
||||
}
|
||||
|
||||
//创建
|
||||
func (this *MatterDao) Create(matter *Matter) *Matter {
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
matter.Uuid = string(timeUUID.String())
|
||||
matter.CreateTime = time.Now()
|
||||
matter.UpdateTime = time.Now()
|
||||
matter.Sort = time.Now().UnixNano() / 1e6
|
||||
db := CONTEXT.DB.Create(matter)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//修改一个文件
|
||||
func (this *MatterDao) Save(matter *Matter) *Matter {
|
||||
|
||||
matter.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.Save(matter)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//计数器加一
|
||||
func (this *MatterDao) TimesIncrement(matterUuid string) {
|
||||
db := CONTEXT.DB.Model(&Matter{}).Where("uuid = ?", matterUuid).Update("times", gorm.Expr("times + 1"))
|
||||
this.PanicError(db.Error)
|
||||
}
|
||||
|
||||
//删除一个文件,数据库中删除,物理磁盘上删除。
|
||||
func (this *MatterDao) Delete(matter *Matter) {
|
||||
|
||||
//目录的话递归删除。
|
||||
if matter.Dir {
|
||||
matters := this.List(matter.Uuid, matter.UserUuid, nil)
|
||||
|
||||
for _, f := range matters {
|
||||
this.Delete(f)
|
||||
}
|
||||
|
||||
//删除数据库中文件夹本身
|
||||
db := CONTEXT.DB.Delete(&matter)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
//从磁盘中删除该文件夹。
|
||||
util.DeleteEmptyDir(matter.AbsolutePath())
|
||||
|
||||
} else {
|
||||
|
||||
//删除数据库中文件记录
|
||||
db := CONTEXT.DB.Delete(&matter)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
//删除对应的缓存图片。
|
||||
this.imageCacheDao.DeleteByMatterUuid(matter.Uuid)
|
||||
|
||||
//删除文件
|
||||
err := os.Remove(matter.AbsolutePath())
|
||||
if err != nil {
|
||||
this.logger.Error("删除磁盘上的文件出错 %s", err.Error())
|
||||
}
|
||||
|
||||
//由于目录和物理结构一一对应,这里不能删除上级文件夹。
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//获取一段时间中,总的数量
|
||||
func (this *MatterDao) CountBetweenTime(startTime time.Time, endTime time.Time) int64 {
|
||||
var count int64
|
||||
db := CONTEXT.DB.Model(&Matter{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
return count
|
||||
}
|
||||
|
||||
//获取一段时间中文件总大小
|
||||
func (this *MatterDao) SizeBetweenTime(startTime time.Time, endTime time.Time) int64 {
|
||||
var size int64
|
||||
db := CONTEXT.DB.Model(&Matter{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("SUM(size)")
|
||||
this.PanicError(db.Error)
|
||||
row := db.Row()
|
||||
err := row.Scan(&size)
|
||||
this.PanicError(err)
|
||||
return size
|
||||
}
|
||||
|
||||
//根据userUuid和path来查找
|
||||
func (this *MatterDao) findByUserUuidAndPath(userUuid string, path string) *Matter {
|
||||
|
||||
var wp = &builder.WherePair{Query: "user_uuid = ? AND path = ?", Args: []interface{}{userUuid, path}}
|
||||
|
||||
var matter = &Matter{}
|
||||
db := CONTEXT.DB.Model(&Matter{}).Where(wp.Query, wp.Args...).First(matter)
|
||||
|
||||
if db.Error != nil {
|
||||
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
|
||||
return nil
|
||||
} else {
|
||||
this.PanicError(db.Error)
|
||||
}
|
||||
}
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//根据userUuid和path来查找
|
||||
func (this *MatterDao) checkByUserUuidAndPath(userUuid string, path string) *Matter {
|
||||
|
||||
if path == "" {
|
||||
panic(result.BadRequest("path 不能为空"))
|
||||
}
|
||||
matter := this.findByUserUuidAndPath(userUuid, path)
|
||||
if matter == nil {
|
||||
panic(result.NotFound("path = %s 不存在", path))
|
||||
}
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//执行清理操作
|
||||
func (this *MatterDao) Cleanup() {
|
||||
this.logger.Info("[MatterDao]执行清理:清除数据库中所有Matter记录。删除磁盘中所有Matter文件。")
|
||||
db := CONTEXT.DB.Where("uuid is not null").Delete(Matter{})
|
||||
this.PanicError(db.Error)
|
||||
|
||||
err := os.RemoveAll(config.CONFIG.MatterPath)
|
||||
this.PanicError(err)
|
||||
|
||||
}
|
84
code/rest/matter_model.go
Normal file
84
code/rest/matter_model.go
Normal file
@ -0,0 +1,84 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"tank/code/config"
|
||||
"tank/code/tool/util"
|
||||
)
|
||||
|
||||
const (
|
||||
//根目录的uuid
|
||||
MATTER_ROOT = "root"
|
||||
//cache文件夹名称
|
||||
MATTER_CACHE = "cache"
|
||||
//matter名称最大长度
|
||||
MATTER_NAME_MAX_LENGTH = 200
|
||||
//matter文件夹最大深度
|
||||
MATTER_NAME_MAX_DEPTH = 32
|
||||
|
||||
)
|
||||
|
||||
/**
|
||||
* 文件。
|
||||
*/
|
||||
type Matter struct {
|
||||
Base
|
||||
Puuid string `json:"puuid" gorm:"type:char(36);index:idx_puuid"`
|
||||
UserUuid string `json:"userUuid" gorm:"type:char(36);index:idx_uu"`
|
||||
Username string `json:"username" gorm:"type:varchar(45) not null"`
|
||||
Dir bool `json:"dir" gorm:"type:tinyint(1) not null;default:0"`
|
||||
Name string `json:"name" gorm:"type:varchar(255) not null"`
|
||||
Md5 string `json:"md5" gorm:"type:varchar(45)"`
|
||||
Size int64 `json:"size" gorm:"type:bigint(20) not null;default:0"`
|
||||
Privacy bool `json:"privacy" gorm:"type:tinyint(1) not null;default:0"`
|
||||
Path string `json:"path" gorm:"type:varchar(512)"`
|
||||
Times int64 `json:"times" gorm:"type:bigint(20) not null;default:0"`
|
||||
Parent *Matter `json:"parent" gorm:"-"`
|
||||
}
|
||||
|
||||
// set File's table name to be `profiles`
|
||||
func (Matter) TableName() string {
|
||||
return config.TABLE_PREFIX + "matter"
|
||||
}
|
||||
|
||||
// 获取该Matter的绝对路径。path代表的是相对路径。
|
||||
func (this *Matter) AbsolutePath() string {
|
||||
return GetUserFileRootDir(this.Username) + this.Path
|
||||
}
|
||||
|
||||
// 获取该Matter的MimeType
|
||||
func (this *Matter) MimeType() string {
|
||||
return util.GetMimeType(util.GetExtension(this.Name))
|
||||
}
|
||||
|
||||
|
||||
//创建一个 ROOT 的matter,主要用于统一化处理移动复制等内容。
|
||||
func NewRootMatter(user *User) *Matter {
|
||||
matter := &Matter{}
|
||||
matter.Uuid = MATTER_ROOT
|
||||
matter.UserUuid = user.Uuid
|
||||
matter.Username = user.Username
|
||||
matter.Dir = true
|
||||
matter.Path = ""
|
||||
matter.CreateTime = user.CreateTime
|
||||
matter.UpdateTime = user.UpdateTime
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//获取到用户文件的根目录。
|
||||
func GetUserFileRootDir(username string) (rootDirPath string) {
|
||||
|
||||
rootDirPath = fmt.Sprintf("%s/%s/%s", config.CONFIG.MatterPath, username, MATTER_ROOT)
|
||||
|
||||
return rootDirPath
|
||||
}
|
||||
|
||||
//获取到用户缓存的根目录。
|
||||
func GetUserCacheRootDir(username string) (rootDirPath string) {
|
||||
|
||||
rootDirPath = fmt.Sprintf("%s/%s/%s", config.CONFIG.MatterPath, username, MATTER_CACHE)
|
||||
|
||||
return rootDirPath
|
||||
}
|
||||
|
690
code/rest/matter_service.go
Normal file
690
code/rest/matter_service.go
Normal file
@ -0,0 +1,690 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
download2 "tank/code/tool/download"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
)
|
||||
|
||||
/**
|
||||
* 操作文件的Service
|
||||
* 以 Atomic 开头的方法带有操作锁,这种方法不能被其他的Atomic方法调用,只能提供给外部调用。
|
||||
*/
|
||||
//@Service
|
||||
type MatterService struct {
|
||||
Bean
|
||||
matterDao *MatterDao
|
||||
userDao *UserDao
|
||||
userService *UserService
|
||||
imageCacheDao *ImageCacheDao
|
||||
imageCacheService *ImageCacheService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *MatterService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.matterDao)
|
||||
if b, ok := b.(*MatterDao); ok {
|
||||
this.matterDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.userDao)
|
||||
if b, ok := b.(*UserDao); ok {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.userService)
|
||||
if b, ok := b.(*UserService); ok {
|
||||
this.userService = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheDao)
|
||||
if b, ok := b.(*ImageCacheDao); ok {
|
||||
this.imageCacheDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.imageCacheService)
|
||||
if b, ok := b.(*ImageCacheService); ok {
|
||||
this.imageCacheService = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//文件下载。支持分片下载
|
||||
func (this *MatterService) DownloadFile(
|
||||
writer http.ResponseWriter,
|
||||
request *http.Request,
|
||||
filePath string,
|
||||
filename string,
|
||||
withContentDisposition bool) {
|
||||
|
||||
download2.DownloadFile(writer, request, filePath, filename, withContentDisposition)
|
||||
}
|
||||
|
||||
//删除文件
|
||||
func (this *MatterService) AtomicDelete(matter *Matter) {
|
||||
|
||||
if matter == nil {
|
||||
panic(result.BadRequest("matter不能为nil"))
|
||||
}
|
||||
|
||||
//操作锁
|
||||
this.userService.MatterLock(matter.UserUuid)
|
||||
defer this.userService.MatterUnlock(matter.UserUuid)
|
||||
|
||||
this.matterDao.Delete(matter)
|
||||
}
|
||||
|
||||
//上传文件
|
||||
func (this *MatterService) Upload(file io.Reader, user *User, dirMatter *Matter, filename string, privacy bool) *Matter {
|
||||
|
||||
if user == nil {
|
||||
panic(result.BadRequest("user cannot be nil."))
|
||||
}
|
||||
|
||||
//验证dirMatter
|
||||
if dirMatter == nil {
|
||||
panic(result.BadRequest("dirMatter cannot be nil."))
|
||||
}
|
||||
|
||||
//文件名不能太长。
|
||||
if len(filename) > MATTER_NAME_MAX_LENGTH {
|
||||
panic(result.BadRequest("文件名不能超过%s", MATTER_NAME_MAX_LENGTH))
|
||||
}
|
||||
|
||||
//文件夹路径
|
||||
dirAbsolutePath := dirMatter.AbsolutePath()
|
||||
dirRelativePath := dirMatter.Path
|
||||
|
||||
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, dirMatter.Uuid, false, filename)
|
||||
if count > 0 {
|
||||
panic(result.BadRequest("该目录下%s已经存在了", filename))
|
||||
}
|
||||
|
||||
//获取文件应该存放在的物理路径的绝对路径和相对路径。
|
||||
fileAbsolutePath := dirAbsolutePath + "/" + filename
|
||||
fileRelativePath := dirRelativePath + "/" + filename
|
||||
|
||||
//创建父文件夹
|
||||
util.MakeDirAll(dirAbsolutePath)
|
||||
|
||||
//如果文件已经存在了,那么直接覆盖。
|
||||
exist, err := util.PathExists(fileAbsolutePath)
|
||||
this.PanicError(err)
|
||||
if exist {
|
||||
this.logger.Error("%s已经存在,将其删除", fileAbsolutePath)
|
||||
removeError := os.Remove(fileAbsolutePath)
|
||||
this.PanicError(removeError)
|
||||
}
|
||||
|
||||
destFile, err := os.OpenFile(fileAbsolutePath, os.O_WRONLY|os.O_CREATE, 0777)
|
||||
this.PanicError(err)
|
||||
|
||||
defer func() {
|
||||
err := destFile.Close()
|
||||
this.PanicError(err)
|
||||
}()
|
||||
|
||||
fileSize, err := io.Copy(destFile, file)
|
||||
this.PanicError(err)
|
||||
|
||||
this.logger.Info("上传文件 %s 大小为 %v ", filename, util.HumanFileSize(fileSize))
|
||||
|
||||
//判断用户自身上传大小的限制。
|
||||
if user.SizeLimit >= 0 {
|
||||
if fileSize > user.SizeLimit {
|
||||
//删除上传过来的内容
|
||||
err = os.Remove(fileAbsolutePath)
|
||||
this.PanicError(err)
|
||||
|
||||
panic(result.BadRequest("文件大小超出限制 %s > %s ", util.HumanFileSize(user.SizeLimit), util.HumanFileSize(fileSize)))
|
||||
}
|
||||
}
|
||||
|
||||
//将文件信息存入数据库中。
|
||||
matter := &Matter{
|
||||
Puuid: dirMatter.Uuid,
|
||||
UserUuid: user.Uuid,
|
||||
Username: user.Username,
|
||||
Dir: false,
|
||||
Name: filename,
|
||||
Md5: "",
|
||||
Size: fileSize,
|
||||
Privacy: privacy,
|
||||
Path: fileRelativePath,
|
||||
}
|
||||
matter = this.matterDao.Create(matter)
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//上传文件
|
||||
func (this *MatterService) AtomicUpload(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(file, user, dirMatter, filename, privacy)
|
||||
}
|
||||
|
||||
//内部创建文件,不带操作锁。
|
||||
func (this *MatterService) createDirectory(dirMatter *Matter, name string, user *User) *Matter {
|
||||
|
||||
//父级matter必须存在
|
||||
if dirMatter == nil {
|
||||
panic(result.BadRequest("dirMatter必须指定"))
|
||||
}
|
||||
|
||||
//必须是文件夹
|
||||
if !dirMatter.Dir {
|
||||
panic(result.BadRequest("dirMatter必须是文件夹"))
|
||||
}
|
||||
|
||||
if dirMatter.UserUuid != user.Uuid {
|
||||
|
||||
panic(result.BadRequest("dirMatter的userUuid和user不一致"))
|
||||
}
|
||||
|
||||
name = strings.TrimSpace(name)
|
||||
//验证参数。
|
||||
if name == "" {
|
||||
panic(result.BadRequest("name参数必填,并且不能全是空格"))
|
||||
}
|
||||
|
||||
if len(name) > MATTER_NAME_MAX_LENGTH {
|
||||
|
||||
panic(result.BadRequest("name长度不能超过%d", MATTER_NAME_MAX_LENGTH))
|
||||
|
||||
}
|
||||
|
||||
if m, _ := regexp.MatchString(`[<>|*?/\\]`, name); m {
|
||||
panic(result.BadRequest(`名称中不能包含以下特殊符号:< > | * ? / \`))
|
||||
}
|
||||
|
||||
//判断同级文件夹中是否有同名的文件夹
|
||||
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, dirMatter.Uuid, true, name)
|
||||
|
||||
if count > 0 {
|
||||
|
||||
panic(result.BadRequest("%s 已经存在了,请使用其他名称。", name))
|
||||
}
|
||||
|
||||
parts := strings.Split(dirMatter.Path, "/")
|
||||
this.logger.Info("%s的层数:%d", dirMatter.Name, len(parts))
|
||||
|
||||
if len(parts) > 32 {
|
||||
panic(result.BadRequest("文件夹最多%d层", MATTER_NAME_MAX_DEPTH))
|
||||
}
|
||||
|
||||
//绝对路径
|
||||
absolutePath := GetUserFileRootDir(user.Username) + dirMatter.Path + "/" + name
|
||||
|
||||
//相对路径
|
||||
relativePath := dirMatter.Path + "/" + name
|
||||
|
||||
//磁盘中创建文件夹。
|
||||
dirPath := util.MakeDirAll(absolutePath)
|
||||
this.logger.Info("Create Directory: %s", dirPath)
|
||||
|
||||
//数据库中创建文件夹。
|
||||
matter := &Matter{
|
||||
Puuid: dirMatter.Uuid,
|
||||
UserUuid: user.Uuid,
|
||||
Username: user.Username,
|
||||
Dir: true,
|
||||
Name: name,
|
||||
Path: relativePath,
|
||||
}
|
||||
|
||||
matter = this.matterDao.Create(matter)
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//在dirMatter中创建文件夹 返回刚刚创建的这个文件夹
|
||||
func (this *MatterService) AtomicCreateDirectory(dirMatter *Matter, name string, user *User) *Matter {
|
||||
|
||||
//操作锁
|
||||
this.userService.MatterLock(user.Uuid)
|
||||
defer this.userService.MatterUnlock(user.Uuid)
|
||||
|
||||
matter := this.createDirectory(dirMatter, name, user)
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//处理 移动和复制时可能存在的覆盖问题。
|
||||
func (this *MatterService) handleOverwrite(userUuid string, destinationPath string, overwrite bool) {
|
||||
|
||||
//目标matter。因为有可能已经存在了
|
||||
destMatter := this.matterDao.findByUserUuidAndPath(userUuid, destinationPath)
|
||||
//如果目标matter存在了。
|
||||
if destMatter != nil {
|
||||
//如果目标matter还存在了。
|
||||
if overwrite {
|
||||
//要求覆盖。那么删除。
|
||||
this.matterDao.Delete(destMatter)
|
||||
} else {
|
||||
panic(result.BadRequest("%s已经存在,操作失败!", destMatter.Path))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//将一个srcMatter放置到另一个destMatter(必须为文件夹)下 不关注 overwrite 和 lock.
|
||||
func (this *MatterService) move(srcMatter *Matter, destDirMatter *Matter) {
|
||||
|
||||
if srcMatter == nil {
|
||||
panic(result.BadRequest("srcMatter cannot be nil."))
|
||||
}
|
||||
|
||||
if !destDirMatter.Dir {
|
||||
panic(result.BadRequest("目标必须为文件夹"))
|
||||
}
|
||||
|
||||
if srcMatter.Dir {
|
||||
//如果源是文件夹
|
||||
destAbsolutePath := destDirMatter.AbsolutePath() + "/" + srcMatter.Name
|
||||
srcAbsolutePath := srcMatter.AbsolutePath()
|
||||
|
||||
//物理文件一口气移动
|
||||
err := os.Rename(srcAbsolutePath, destAbsolutePath)
|
||||
this.PanicError(err)
|
||||
|
||||
//修改数据库中信息
|
||||
srcMatter.Puuid = destDirMatter.Uuid
|
||||
srcMatter.Path = destDirMatter.Path + "/" + srcMatter.Name
|
||||
srcMatter = this.matterDao.Save(srcMatter)
|
||||
|
||||
//调整该文件夹下文件的Path.
|
||||
matters := this.matterDao.List(srcMatter.Uuid, srcMatter.UserUuid, nil)
|
||||
for _, m := range matters {
|
||||
this.adjustPath(m, srcMatter)
|
||||
}
|
||||
|
||||
} else {
|
||||
//如果源是普通文件
|
||||
|
||||
destAbsolutePath := destDirMatter.AbsolutePath() + "/" + srcMatter.Name
|
||||
srcAbsolutePath := srcMatter.AbsolutePath()
|
||||
|
||||
//物理文件进行移动
|
||||
err := os.Rename(srcAbsolutePath, destAbsolutePath)
|
||||
this.PanicError(err)
|
||||
|
||||
//删除对应的缓存。
|
||||
this.imageCacheDao.DeleteByMatterUuid(srcMatter.Uuid)
|
||||
|
||||
//修改数据库中信息
|
||||
srcMatter.Puuid = destDirMatter.Uuid
|
||||
srcMatter.Path = destDirMatter.Path + "/" + srcMatter.Name
|
||||
srcMatter = this.matterDao.Save(srcMatter)
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//将一个srcMatter放置到另一个destMatter(必须为文件夹)下
|
||||
func (this *MatterService) AtomicMove(srcMatter *Matter, destDirMatter *Matter, overwrite bool) {
|
||||
|
||||
if srcMatter == nil {
|
||||
panic(result.BadRequest("srcMatter cannot be nil."))
|
||||
}
|
||||
|
||||
//操作锁
|
||||
this.userService.MatterLock(srcMatter.UserUuid)
|
||||
defer this.userService.MatterUnlock(srcMatter.UserUuid)
|
||||
|
||||
if destDirMatter == nil {
|
||||
panic(result.BadRequest("destDirMatter cannot be nil."))
|
||||
}
|
||||
if !destDirMatter.Dir {
|
||||
panic(result.BadRequest("目标必须为文件夹"))
|
||||
}
|
||||
|
||||
//文件夹不能把自己移入到自己中,也不可以移入到自己的子文件夹下。
|
||||
destDirMatter = this.WrapDetail(destDirMatter)
|
||||
tmpMatter := destDirMatter
|
||||
for tmpMatter != nil {
|
||||
if srcMatter.Uuid == tmpMatter.Uuid {
|
||||
panic("文件夹不能把自己移入到自己中,也不可以移入到自己的子文件夹下。")
|
||||
}
|
||||
tmpMatter = tmpMatter.Parent
|
||||
}
|
||||
|
||||
//处理覆盖的问题
|
||||
destinationPath := destDirMatter.Path + "/" + srcMatter.Name
|
||||
this.handleOverwrite(srcMatter.UserUuid, destinationPath, overwrite)
|
||||
|
||||
//做move操作。
|
||||
this.move(srcMatter, destDirMatter)
|
||||
}
|
||||
|
||||
//将一个srcMatter放置到另一个destMatter(必须为文件夹)下
|
||||
func (this *MatterService) AtomicMoveBatch(srcMatters []*Matter, destDirMatter *Matter) {
|
||||
|
||||
if destDirMatter == nil {
|
||||
panic(result.BadRequest("destDirMatter cannot be nil."))
|
||||
}
|
||||
|
||||
//操作锁
|
||||
this.userService.MatterLock(destDirMatter.UserUuid)
|
||||
defer this.userService.MatterUnlock(destDirMatter.UserUuid)
|
||||
|
||||
if srcMatters == nil {
|
||||
panic(result.BadRequest("srcMatters cannot be nil."))
|
||||
}
|
||||
|
||||
if !destDirMatter.Dir {
|
||||
panic(result.BadRequest("目标必须为文件夹"))
|
||||
}
|
||||
|
||||
//文件夹不能把自己移入到自己中,也不可以移入到自己的子文件夹下。
|
||||
destDirMatter = this.WrapDetail(destDirMatter)
|
||||
for _, srcMatter := range srcMatters {
|
||||
|
||||
tmpMatter := destDirMatter
|
||||
for tmpMatter != nil {
|
||||
if srcMatter.Uuid == tmpMatter.Uuid {
|
||||
panic("文件夹不能把自己移入到自己中,也不可以移入到自己的子文件夹下。")
|
||||
}
|
||||
tmpMatter = tmpMatter.Parent
|
||||
}
|
||||
}
|
||||
|
||||
for _, srcMatter := range srcMatters {
|
||||
this.move(srcMatter, destDirMatter)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//内部移动一个文件(提供给Copy调用),无需关心overwrite问题。
|
||||
func (this *MatterService) copy(srcMatter *Matter, destDirMatter *Matter, name string) {
|
||||
|
||||
if srcMatter.Dir {
|
||||
|
||||
//如果源是文件夹
|
||||
|
||||
//在目标地址创建新文件夹。
|
||||
newMatter := &Matter{
|
||||
Puuid: destDirMatter.Uuid,
|
||||
UserUuid: srcMatter.UserUuid,
|
||||
Username: srcMatter.Username,
|
||||
Dir: srcMatter.Dir,
|
||||
Name: name,
|
||||
Md5: "",
|
||||
Size: srcMatter.Size,
|
||||
Privacy: srcMatter.Privacy,
|
||||
Path: destDirMatter.Path + "/" + name,
|
||||
}
|
||||
|
||||
newMatter = this.matterDao.Create(newMatter)
|
||||
|
||||
//复制子文件或文件夹
|
||||
matters := this.matterDao.List(srcMatter.Uuid, srcMatter.UserUuid, nil)
|
||||
for _, m := range matters {
|
||||
this.copy(m, newMatter, m.Name)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
//如果源是普通文件
|
||||
destAbsolutePath := destDirMatter.AbsolutePath() + "/" + name
|
||||
srcAbsolutePath := srcMatter.AbsolutePath()
|
||||
|
||||
//物理文件进行复制
|
||||
util.CopyFile(srcAbsolutePath, destAbsolutePath)
|
||||
|
||||
//创建新文件的数据库信息。
|
||||
newMatter := &Matter{
|
||||
Puuid: destDirMatter.Uuid,
|
||||
UserUuid: srcMatter.UserUuid,
|
||||
Username: srcMatter.Username,
|
||||
Dir: srcMatter.Dir,
|
||||
Name: name,
|
||||
Md5: "",
|
||||
Size: srcMatter.Size,
|
||||
Privacy: srcMatter.Privacy,
|
||||
Path: destDirMatter.Path + "/" + name,
|
||||
}
|
||||
newMatter = this.matterDao.Create(newMatter)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//将一个srcMatter复制到另一个destMatter(必须为文件夹)下,名字叫做name
|
||||
func (this *MatterService) AtomicCopy(srcMatter *Matter, destDirMatter *Matter, name string, overwrite bool) {
|
||||
|
||||
if srcMatter == nil {
|
||||
panic(result.BadRequest("srcMatter cannot be nil."))
|
||||
}
|
||||
|
||||
//操作锁
|
||||
this.userService.MatterLock(srcMatter.UserUuid)
|
||||
defer this.userService.MatterUnlock(srcMatter.UserUuid)
|
||||
|
||||
if !destDirMatter.Dir {
|
||||
panic(result.BadRequest("目标必须为文件夹"))
|
||||
}
|
||||
|
||||
destinationPath := destDirMatter.Path + "/" + name
|
||||
this.handleOverwrite(srcMatter.UserUuid, destinationPath, overwrite)
|
||||
|
||||
this.copy(srcMatter, destDirMatter, name)
|
||||
}
|
||||
|
||||
//将一个matter 重命名为 name
|
||||
func (this *MatterService) AtomicRename(matter *Matter, name string, user *User) {
|
||||
|
||||
if user == nil {
|
||||
panic(result.BadRequest("user cannot be nil"))
|
||||
}
|
||||
|
||||
//操作锁
|
||||
this.userService.MatterLock(user.Uuid)
|
||||
defer this.userService.MatterUnlock(user.Uuid)
|
||||
|
||||
//验证参数。
|
||||
if name == "" {
|
||||
panic(result.BadRequest("name参数必填"))
|
||||
}
|
||||
if m, _ := regexp.MatchString(`[<>|*?/\\]`, name); m {
|
||||
panic(result.BadRequest(`名称中不能包含以下特殊符号:< > | * ? / \`))
|
||||
}
|
||||
|
||||
if len(name) > 200 {
|
||||
panic("name长度不能超过200")
|
||||
}
|
||||
|
||||
if name == matter.Name {
|
||||
panic(result.BadRequest("新名称和旧名称一样,操作失败!"))
|
||||
}
|
||||
|
||||
//判断同级文件夹中是否有同名的文件
|
||||
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, matter.Puuid, matter.Dir, name)
|
||||
|
||||
if count > 0 {
|
||||
panic(result.BadRequest("【" + name + "】已经存在了,请使用其他名称。"))
|
||||
}
|
||||
|
||||
if matter.Dir {
|
||||
//如果源是文件夹
|
||||
|
||||
oldAbsolutePath := matter.AbsolutePath()
|
||||
absoluteDirPath := util.GetDirOfPath(oldAbsolutePath)
|
||||
relativeDirPath := util.GetDirOfPath(matter.Path)
|
||||
newAbsolutePath := absoluteDirPath + "/" + name
|
||||
|
||||
//物理文件一口气移动
|
||||
err := os.Rename(oldAbsolutePath, newAbsolutePath)
|
||||
this.PanicError(err)
|
||||
|
||||
//修改数据库中信息
|
||||
matter.Name = name
|
||||
matter.Path = relativeDirPath + "/" + name
|
||||
matter = this.matterDao.Save(matter)
|
||||
|
||||
//调整该文件夹下文件的Path.
|
||||
matters := this.matterDao.List(matter.Uuid, matter.UserUuid, nil)
|
||||
for _, m := range matters {
|
||||
this.adjustPath(m, matter)
|
||||
}
|
||||
|
||||
} else {
|
||||
//如果源是普通文件
|
||||
|
||||
oldAbsolutePath := matter.AbsolutePath()
|
||||
absoluteDirPath := util.GetDirOfPath(oldAbsolutePath)
|
||||
relativeDirPath := util.GetDirOfPath(matter.Path)
|
||||
newAbsolutePath := absoluteDirPath + "/" + name
|
||||
|
||||
//物理文件进行移动
|
||||
err := os.Rename(oldAbsolutePath, newAbsolutePath)
|
||||
this.PanicError(err)
|
||||
|
||||
//删除对应的缓存。
|
||||
this.imageCacheDao.DeleteByMatterUuid(matter.Uuid)
|
||||
|
||||
//修改数据库中信息
|
||||
matter.Name = name
|
||||
matter.Path = relativeDirPath + "/" + name
|
||||
matter = this.matterDao.Save(matter)
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//根据一个文件夹路径,依次创建,找到最后一个文件夹的matter,如果中途出错,返回err.
|
||||
func (this *MatterService) CreateDirectories(user *User, dirPath string) *Matter {
|
||||
|
||||
if dirPath == "" {
|
||||
panic(`文件夹不能为空`)
|
||||
} else if dirPath[0:1] != "/" {
|
||||
panic(`文件夹必须以/开头`)
|
||||
} else if strings.Index(dirPath, "//") != -1 {
|
||||
panic(`文件夹不能出现连续的//`)
|
||||
} else if m, _ := regexp.MatchString(`[<>|*?\\]`, dirPath); m {
|
||||
panic(`文件夹中不能包含以下特殊符号:< > | * ? \`)
|
||||
}
|
||||
|
||||
if dirPath == "/" {
|
||||
return NewRootMatter(user)
|
||||
}
|
||||
|
||||
//如果最后一个符号为/自动忽略
|
||||
if dirPath[len(dirPath)-1] == '/' {
|
||||
dirPath = dirPath[:len(dirPath)-1]
|
||||
}
|
||||
|
||||
//递归找寻文件的上级目录uuid.
|
||||
folders := strings.Split(dirPath, "/")
|
||||
|
||||
if len(folders) > MATTER_NAME_MAX_DEPTH {
|
||||
panic(result.BadRequest("文件夹最多%d层。", MATTER_NAME_MAX_DEPTH))
|
||||
}
|
||||
|
||||
var dirMatter *Matter
|
||||
for k, name := range folders {
|
||||
|
||||
//split的第一个元素为空字符串,忽略。
|
||||
if k == 0 {
|
||||
dirMatter = NewRootMatter(user)
|
||||
continue
|
||||
}
|
||||
|
||||
dirMatter = this.createDirectory(dirMatter, name, user)
|
||||
}
|
||||
|
||||
return dirMatter
|
||||
}
|
||||
|
||||
//包装某个matter的详情。会把父级依次倒着装进去。如果中途出错,直接抛出异常。
|
||||
func (this *MatterService) WrapDetail(matter *Matter) *Matter {
|
||||
|
||||
if matter == nil {
|
||||
panic(result.BadRequest("matter cannot be nil."))
|
||||
}
|
||||
|
||||
//组装file的内容,展示其父组件。
|
||||
puuid := matter.Puuid
|
||||
tmpMatter := matter
|
||||
for puuid != MATTER_ROOT {
|
||||
pFile := this.matterDao.CheckByUuid(puuid)
|
||||
tmpMatter.Parent = pFile
|
||||
tmpMatter = pFile
|
||||
puuid = pFile.Puuid
|
||||
}
|
||||
|
||||
return matter
|
||||
}
|
||||
|
||||
//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。
|
||||
func (this *MatterService) Detail(uuid string) *Matter {
|
||||
matter := this.matterDao.CheckByUuid(uuid)
|
||||
return this.WrapDetail(matter)
|
||||
}
|
||||
|
||||
//去指定的url中爬文件
|
||||
func (this *MatterService) AtomicCrawl(url string, filename string, user *User, dirMatter *Matter, privacy bool) *Matter {
|
||||
|
||||
if user == nil {
|
||||
panic(result.BadRequest("user cannot be nil."))
|
||||
}
|
||||
|
||||
//操作锁
|
||||
this.userService.MatterLock(user.Uuid)
|
||||
defer this.userService.MatterUnlock(user.Uuid)
|
||||
|
||||
if url == "" || (!strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://")) {
|
||||
panic("资源url必填,并且应该以http://或者https://开头")
|
||||
}
|
||||
|
||||
//从指定的url下载一个文件。参考:https://golangcode.com/download-a-file-from-a-url/
|
||||
resp, err := http.Get(url)
|
||||
this.PanicError(err)
|
||||
|
||||
return this.Upload(resp.Body, user, dirMatter, filename, privacy)
|
||||
}
|
||||
|
||||
//调整一个Matter的path值
|
||||
func (this *MatterService) adjustPath(matter *Matter, parentMatter *Matter) {
|
||||
|
||||
if matter.Dir {
|
||||
//如果源是文件夹
|
||||
|
||||
//首先调整好自己
|
||||
matter.Path = parentMatter.Path + "/" + matter.Name
|
||||
matter = this.matterDao.Save(matter)
|
||||
|
||||
//调整该文件夹下文件的Path.
|
||||
matters := this.matterDao.List(matter.Uuid, matter.UserUuid, nil)
|
||||
for _, m := range matters {
|
||||
this.adjustPath(m, matter)
|
||||
}
|
||||
|
||||
} else {
|
||||
//如果源是普通文件
|
||||
|
||||
//删除该文件的所有缓存
|
||||
this.imageCacheDao.DeleteByMatterUuid(matter.Uuid)
|
||||
|
||||
//调整path
|
||||
matter.Path = parentMatter.Path + "/" + matter.Name
|
||||
matter = this.matterDao.Save(matter)
|
||||
}
|
||||
|
||||
}
|
103
code/rest/preference_controller.go
Normal file
103
code/rest/preference_controller.go
Normal file
@ -0,0 +1,103 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
)
|
||||
|
||||
type PreferenceController struct {
|
||||
BaseController
|
||||
preferenceDao *PreferenceDao
|
||||
preferenceService *PreferenceService
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *PreferenceController) Init() {
|
||||
this.BaseController.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.preferenceDao)
|
||||
if b, ok := b.(*PreferenceDao); ok {
|
||||
this.preferenceDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.preferenceService)
|
||||
if b, ok := b.(*PreferenceService); ok {
|
||||
this.preferenceService = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *PreferenceController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
|
||||
|
||||
//每个Controller需要主动注册自己的路由。
|
||||
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/system_cleanup"] = this.Wrap(this.SystemCleanup, USER_ROLE_ADMINISTRATOR)
|
||||
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//查看某个偏好设置的详情。
|
||||
func (this *PreferenceController) Fetch(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
preference := this.preferenceService.Fetch()
|
||||
|
||||
return this.Success(preference)
|
||||
}
|
||||
|
||||
//修改
|
||||
func (this *PreferenceController) Edit(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
//验证参数。
|
||||
name := request.FormValue("name")
|
||||
if name == "" {
|
||||
panic("name参数必填")
|
||||
}
|
||||
|
||||
logoUrl := request.FormValue("logoUrl")
|
||||
faviconUrl := request.FormValue("faviconUrl")
|
||||
footerLine1 := request.FormValue("footerLine1")
|
||||
footerLine2 := request.FormValue("footerLine2")
|
||||
showAlienStr := request.FormValue("showAlien")
|
||||
|
||||
preference := this.preferenceDao.Fetch()
|
||||
preference.Name = name
|
||||
preference.LogoUrl = logoUrl
|
||||
preference.FaviconUrl = faviconUrl
|
||||
preference.FooterLine1 = footerLine1
|
||||
preference.FooterLine2 = footerLine2
|
||||
if showAlienStr == TRUE {
|
||||
preference.ShowAlien = true
|
||||
} else if showAlienStr == FALSE {
|
||||
preference.ShowAlien = false
|
||||
}
|
||||
|
||||
preference = this.preferenceDao.Save(preference)
|
||||
|
||||
//重置缓存中的偏好
|
||||
this.preferenceService.Reset()
|
||||
|
||||
return this.Success(preference)
|
||||
}
|
||||
|
||||
//清扫系统,所有数据全部丢失。一定要非常慎点,非常慎点!只在系统初始化的时候点击!
|
||||
func (this *PreferenceController) SystemCleanup(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
password := request.FormValue("password")
|
||||
|
||||
if !util.MatchBcrypt(password, user.Password) {
|
||||
panic(result.BadRequest("密码错误,不能执行!"))
|
||||
}
|
||||
|
||||
for _, bean := range CONTEXT.BeanMap {
|
||||
bean.Cleanup()
|
||||
}
|
||||
|
||||
return this.Success("OK")
|
||||
}
|
65
code/rest/preference_dao.go
Normal file
65
code/rest/preference_dao.go
Normal file
@ -0,0 +1,65 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"tank/code/tool/result"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PreferenceDao struct {
|
||||
BaseDao
|
||||
}
|
||||
|
||||
//按照Id查询偏好设置
|
||||
func (this *PreferenceDao) Fetch() *Preference {
|
||||
|
||||
// Read
|
||||
var preference = &Preference{}
|
||||
db := CONTEXT.DB.First(preference)
|
||||
if db.Error != nil {
|
||||
|
||||
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
|
||||
preference.Name = "蓝眼云盘"
|
||||
preference.ShowAlien = true
|
||||
this.Create(preference)
|
||||
return preference
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return preference
|
||||
}
|
||||
|
||||
//创建
|
||||
func (this *PreferenceDao) Create(preference *Preference) *Preference {
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
preference.Uuid = string(timeUUID.String())
|
||||
preference.CreateTime = time.Now()
|
||||
preference.UpdateTime = time.Now()
|
||||
preference.Sort = time.Now().UnixNano() / 1e6
|
||||
db := CONTEXT.DB.Create(preference)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return preference
|
||||
}
|
||||
|
||||
//修改一个偏好设置
|
||||
func (this *PreferenceDao) Save(preference *Preference) *Preference {
|
||||
|
||||
preference.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.Save(preference)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return preference
|
||||
}
|
||||
|
||||
|
||||
//执行清理操作
|
||||
func (this *PreferenceDao) Cleanup() {
|
||||
|
||||
this.logger.Info("[PreferenceDao]执行清理:清除数据库中所有Preference记录。")
|
||||
db := CONTEXT.DB.Where("uuid is not null").Delete(Preference{})
|
||||
this.PanicError(db.Error)
|
||||
}
|
18
code/rest/preference_model.go
Normal file
18
code/rest/preference_model.go
Normal file
@ -0,0 +1,18 @@
|
||||
package rest
|
||||
|
||||
import "tank/code/config"
|
||||
|
||||
type Preference struct {
|
||||
Base
|
||||
Name string `json:"name" gorm:"type:varchar(45)"`
|
||||
LogoUrl string `json:"logoUrl" gorm:"type:varchar(255)"`
|
||||
FaviconUrl string `json:"faviconUrl" gorm:"type:varchar(255)"`
|
||||
FooterLine1 string `json:"footerLine1" gorm:"type:varchar(1024)"`
|
||||
FooterLine2 string `json:"footerLine2" gorm:"type:varchar(1024)"`
|
||||
ShowAlien bool `json:"showAlien" gorm:"type:tinyint(1) not null;default:1"`
|
||||
}
|
||||
|
||||
// set File's table name to be `profiles`
|
||||
func (this *Preference) TableName() string {
|
||||
return config.TABLE_PREFIX + "preference"
|
||||
}
|
45
code/rest/preference_service.go
Normal file
45
code/rest/preference_service.go
Normal file
@ -0,0 +1,45 @@
|
||||
package rest
|
||||
|
||||
//@Service
|
||||
type PreferenceService struct {
|
||||
Bean
|
||||
preferenceDao *PreferenceDao
|
||||
preference *Preference
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *PreferenceService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.preferenceDao)
|
||||
if b, ok := b.(*PreferenceDao); ok {
|
||||
this.preferenceDao = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//获取单例的配置。
|
||||
func (this *PreferenceService) Fetch() *Preference {
|
||||
|
||||
if this.preference == nil {
|
||||
this.preference = this.preferenceDao.Fetch()
|
||||
}
|
||||
|
||||
return this.preference
|
||||
}
|
||||
|
||||
//清空单例配置。
|
||||
func (this *PreferenceService) Reset() {
|
||||
|
||||
this.preference = nil
|
||||
|
||||
}
|
||||
|
||||
//执行清理操作
|
||||
func (this *PreferenceService) Cleanup() {
|
||||
|
||||
this.logger.Info("[PreferenceService]执行清理:重置缓存中的preference。")
|
||||
|
||||
this.Reset()
|
||||
}
|
210
code/rest/router.go
Normal file
210
code/rest/router.go
Normal file
@ -0,0 +1,210 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/json-iterator/go"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"tank/code/config"
|
||||
"tank/code/logger"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
//用于处理所有前来的请求
|
||||
type Router struct {
|
||||
installController *InstallController
|
||||
footprintService *FootprintService
|
||||
userService *UserService
|
||||
routeMap map[string]func(writer http.ResponseWriter, request *http.Request)
|
||||
installRouteMap map[string]func(writer http.ResponseWriter, request *http.Request)
|
||||
}
|
||||
|
||||
//构造方法
|
||||
func NewRouter() *Router {
|
||||
router := &Router{
|
||||
routeMap: make(map[string]func(writer http.ResponseWriter, request *http.Request)),
|
||||
installRouteMap: make(map[string]func(writer http.ResponseWriter, request *http.Request)),
|
||||
}
|
||||
|
||||
//installController.
|
||||
b := CONTEXT.GetBean(router.installController)
|
||||
if b, ok := b.(*InstallController); ok {
|
||||
router.installController = b
|
||||
}
|
||||
|
||||
//装载userService.
|
||||
b = CONTEXT.GetBean(router.userService)
|
||||
if b, ok := b.(*UserService); ok {
|
||||
router.userService = b
|
||||
}
|
||||
|
||||
//装载footprintService
|
||||
b = CONTEXT.GetBean(router.footprintService)
|
||||
if b, ok := b.(*FootprintService); ok {
|
||||
router.footprintService = b
|
||||
}
|
||||
|
||||
//将Controller中的路由规则装载进来,InstallController中的除外
|
||||
for _, controller := range CONTEXT.ControllerMap {
|
||||
|
||||
if controller == router.installController {
|
||||
routes := controller.RegisterRoutes()
|
||||
for k, v := range routes {
|
||||
router.installRouteMap[k] = v
|
||||
}
|
||||
} else {
|
||||
routes := controller.RegisterRoutes()
|
||||
for k, v := range routes {
|
||||
router.routeMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return router
|
||||
|
||||
}
|
||||
|
||||
//全局的异常捕获
|
||||
func (this *Router) GlobalPanicHandler(writer http.ResponseWriter, request *http.Request, startTime time.Time) {
|
||||
if err := recover(); err != nil {
|
||||
|
||||
logger.LOGGER.Error("错误: %v", err)
|
||||
|
||||
var webResult *result.WebResult = nil
|
||||
if value, ok := err.(string); ok {
|
||||
//一个字符串,默认是请求错误。
|
||||
webResult = result.CustomWebResult(result.CODE_WRAPPER_BAD_REQUEST, value)
|
||||
} else if value, ok := err.(*result.WebResult); ok {
|
||||
//一个WebResult对象
|
||||
webResult = value
|
||||
} else if value, ok := err.(*result.CodeWrapper); ok {
|
||||
//一个WebResult对象
|
||||
webResult = result.ConstWebResult(value)
|
||||
} else if value, ok := err.(error); ok {
|
||||
//一个普通的错误对象
|
||||
webResult = result.CustomWebResult(result.CODE_WRAPPER_UNKNOWN, value.Error())
|
||||
} else {
|
||||
//其他不能识别的内容
|
||||
webResult = result.ConstWebResult(result.CODE_WRAPPER_UNKNOWN)
|
||||
}
|
||||
|
||||
//修改http code码
|
||||
writer.WriteHeader(result.FetchHttpStatus(webResult.Code))
|
||||
|
||||
//输出的是json格式 返回的内容申明是json,utf-8
|
||||
writer.Header().Set("Content-Type", "application/json;charset=UTF-8")
|
||||
|
||||
//用json的方式输出返回值。
|
||||
b, _ := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(webResult)
|
||||
|
||||
//写到输出流中
|
||||
_, err := fmt.Fprintf(writer, string(b))
|
||||
if err != nil {
|
||||
fmt.Printf("输出结果时出错了\n")
|
||||
}
|
||||
|
||||
//错误情况记录。
|
||||
go util.SafeMethod(func() {
|
||||
this.footprintService.Trace(writer, request, time.Now().Sub(startTime), false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//让Router具有处理请求的功能。
|
||||
func (this *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
//每个请求的入口在这里
|
||||
//全局异常处理。
|
||||
defer this.GlobalPanicHandler(writer, request, startTime)
|
||||
|
||||
path := request.URL.Path
|
||||
if strings.HasPrefix(path, "/api") {
|
||||
|
||||
//对于IE浏览器,会自动缓存,因此设置不缓存Header.
|
||||
writer.Header().Set("Pragma", "No-cache")
|
||||
writer.Header().Set("Cache-Control", "no-cache")
|
||||
writer.Header().Set("Expires", "0")
|
||||
|
||||
if config.CONFIG.Installed {
|
||||
//已安装的模式
|
||||
|
||||
//统一处理用户的身份信息。
|
||||
this.userService.bootstrap(writer, request)
|
||||
|
||||
if handler, ok := this.routeMap[path]; ok {
|
||||
handler(writer, request)
|
||||
} else {
|
||||
//直接将请求扔给每个controller,看看他们能不能处理,如果都不能处理,那就抛出找不到的错误
|
||||
canHandle := false
|
||||
for _, controller := range CONTEXT.ControllerMap {
|
||||
if handler, exist := controller.HandleRoutes(writer, request); exist {
|
||||
canHandle = true
|
||||
handler(writer, request)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !canHandle {
|
||||
panic(result.CustomWebResult(result.CODE_WRAPPER_NOT_FOUND, fmt.Sprintf("没有找到能够处理%s的方法", path)))
|
||||
}
|
||||
}
|
||||
|
||||
//正常的访问记录会落到这里。
|
||||
go util.SafeMethod(func() {
|
||||
this.footprintService.Trace(writer, request, time.Now().Sub(startTime), true)
|
||||
})
|
||||
|
||||
} else {
|
||||
//未安装模式
|
||||
if handler, ok := this.installRouteMap[path]; ok {
|
||||
handler(writer, request)
|
||||
} else {
|
||||
panic(result.ConstWebResult(result.CODE_WRAPPER_NOT_INSTALLED))
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
//当作静态资源处理。默认从当前文件下面的static文件夹中取东西。
|
||||
dir := util.GetHtmlPath()
|
||||
|
||||
requestURI := request.RequestURI
|
||||
if requestURI == "" || request.RequestURI == "/" {
|
||||
requestURI = "index.html"
|
||||
}
|
||||
|
||||
filePath := dir + requestURI
|
||||
exists, _ := util.PathExists(filePath)
|
||||
if !exists {
|
||||
filePath = dir + "/index.html"
|
||||
exists, _ = util.PathExists(filePath)
|
||||
if !exists {
|
||||
panic(fmt.Sprintf("404 not found:%s", filePath))
|
||||
}
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", util.GetMimeType(util.GetExtension(filePath)))
|
||||
|
||||
diskFile, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
panic("cannot get file.")
|
||||
}
|
||||
defer func() {
|
||||
err := diskFile.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
_, err = io.Copy(writer, diskFile)
|
||||
if err != nil {
|
||||
panic("cannot get file.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
78
code/rest/session_dao.go
Normal file
78
code/rest/session_dao.go
Normal file
@ -0,0 +1,78 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SessionDao struct {
|
||||
BaseDao
|
||||
}
|
||||
|
||||
//按照Id查询session.
|
||||
func (this *SessionDao) FindByUuid(uuid string) *Session {
|
||||
|
||||
// Read
|
||||
var session = &Session{}
|
||||
db := CONTEXT.DB.Where(&Session{Base: Base{Uuid: uuid}}).First(session)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return session
|
||||
}
|
||||
|
||||
//按照Id查询session.
|
||||
func (this *SessionDao) CheckByUuid(uuid string) *Session {
|
||||
|
||||
// Read
|
||||
var session = &Session{}
|
||||
db := CONTEXT.DB.Where(&Session{Base: Base{Uuid: uuid}}).First(session)
|
||||
this.PanicError(db.Error)
|
||||
return session
|
||||
}
|
||||
|
||||
//创建一个session并且持久化到数据库中。
|
||||
func (this *SessionDao) Create(session *Session) *Session {
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
session.Uuid = string(timeUUID.String())
|
||||
session.CreateTime = time.Now()
|
||||
session.UpdateTime = time.Now()
|
||||
session.Sort = time.Now().UnixNano() / 1e6
|
||||
db := CONTEXT.DB.Create(session)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
|
||||
//修改一个session
|
||||
func (this *SessionDao) Save(session *Session) *Session {
|
||||
|
||||
session.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.Save(session)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
|
||||
func (this *SessionDao) Delete(uuid string) {
|
||||
|
||||
session := this.CheckByUuid(uuid)
|
||||
|
||||
session.ExpireTime = time.Now()
|
||||
db := CONTEXT.DB.Delete(session)
|
||||
|
||||
this.PanicError(db.Error)
|
||||
|
||||
}
|
||||
|
||||
|
||||
//执行清理操作
|
||||
func (this *SessionDao) Cleanup() {
|
||||
this.logger.Info("[SessionDao]执行清理:清除数据库中所有Session记录。")
|
||||
db := CONTEXT.DB.Where("uuid is not null").Delete(Session{})
|
||||
this.PanicError(db.Error)
|
||||
}
|
18
code/rest/session_model.go
Normal file
18
code/rest/session_model.go
Normal file
@ -0,0 +1,18 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"tank/code/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
Base
|
||||
UserUuid string `json:"userUuid" gorm:"type:char(36)"`
|
||||
Ip string `json:"ip" gorm:"type:varchar(128) not null"`
|
||||
ExpireTime time.Time `json:"expireTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
|
||||
}
|
||||
|
||||
// set User's table name to be `profiles`
|
||||
func (this *Session) TableName() string {
|
||||
return config.TABLE_PREFIX + "session"
|
||||
}
|
33
code/rest/session_service.go
Normal file
33
code/rest/session_service.go
Normal file
@ -0,0 +1,33 @@
|
||||
package rest
|
||||
|
||||
//@Service
|
||||
type SessionService struct {
|
||||
Bean
|
||||
userDao *UserDao
|
||||
sessionDao *SessionDao
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *SessionService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.userDao)
|
||||
if b, ok := b.(*UserDao); ok {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.sessionDao)
|
||||
if b, ok := b.(*SessionDao); ok {
|
||||
this.sessionDao = b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//执行清理操作
|
||||
func (this *SessionService) Cleanup() {
|
||||
|
||||
this.logger.Info("[SessionService]执行清理:清除缓存中所有Session记录,共%d条。", CONTEXT.SessionCache.Count())
|
||||
|
||||
CONTEXT.SessionCache.Truncate()
|
||||
}
|
50
code/rest/upload_token_dao.go
Normal file
50
code/rest/upload_token_dao.go
Normal file
@ -0,0 +1,50 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UploadTokenDao struct {
|
||||
BaseDao
|
||||
}
|
||||
|
||||
//按照Id查询
|
||||
func (this *UploadTokenDao) FindByUuid(uuid string) *UploadToken {
|
||||
|
||||
// Read
|
||||
var uploadToken = &UploadToken{}
|
||||
db := CONTEXT.DB.Where(&UploadToken{Base: Base{Uuid: uuid}}).First(uploadToken)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return uploadToken
|
||||
|
||||
}
|
||||
|
||||
//创建一个session并且持久化到数据库中。
|
||||
func (this *UploadTokenDao) Create(uploadToken *UploadToken) *UploadToken {
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
uploadToken.Uuid = string(timeUUID.String())
|
||||
|
||||
uploadToken.CreateTime = time.Now()
|
||||
uploadToken.UpdateTime = time.Now()
|
||||
uploadToken.Sort = time.Now().UnixNano() / 1e6
|
||||
db := CONTEXT.DB.Create(uploadToken)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return uploadToken
|
||||
}
|
||||
|
||||
//修改一个uploadToken
|
||||
func (this *UploadTokenDao) Save(uploadToken *UploadToken) *UploadToken {
|
||||
|
||||
uploadToken.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.Save(uploadToken)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return uploadToken
|
||||
}
|
22
code/rest/upload_token_model.go
Normal file
22
code/rest/upload_token_model.go
Normal file
@ -0,0 +1,22 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"tank/code/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UploadToken struct {
|
||||
Base
|
||||
UserUuid string `json:"userUuid"`
|
||||
FolderUuid string `json:"folderUuid"`
|
||||
MatterUuid string `json:"matterUuid"`
|
||||
ExpireTime time.Time `json:"expireTime"`
|
||||
Filename string `json:"filename"`
|
||||
Privacy bool `json:"privacy"`
|
||||
Size int64 `json:"size"`
|
||||
Ip string `json:"ip"`
|
||||
}
|
||||
|
||||
func (this *UploadToken) TableName() string {
|
||||
return config.TABLE_PREFIX + "upload_token"
|
||||
}
|
403
code/rest/user_controller.go
Normal file
403
code/rest/user_controller.go
Normal file
@ -0,0 +1,403 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"tank/code/config"
|
||||
"tank/code/tool/builder"
|
||||
"tank/code/tool/result"
|
||||
"tank/code/tool/util"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UserController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *UserController) Init() {
|
||||
this.BaseController.Init()
|
||||
}
|
||||
|
||||
//注册自己的路由。
|
||||
func (this *UserController) RegisterRoutes() map[string]func(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
|
||||
|
||||
//每个Controller需要主动注册自己的路由。
|
||||
routeMap["/api/user/create"] = this.Wrap(this.Create, USER_ROLE_ADMINISTRATOR)
|
||||
routeMap["/api/user/edit"] = this.Wrap(this.Edit, USER_ROLE_USER)
|
||||
routeMap["/api/user/change/password"] = this.Wrap(this.ChangePassword, USER_ROLE_USER)
|
||||
routeMap["/api/user/reset/password"] = this.Wrap(this.ResetPassword, USER_ROLE_ADMINISTRATOR)
|
||||
routeMap["/api/user/login"] = this.Wrap(this.Login, USER_ROLE_GUEST)
|
||||
routeMap["/api/user/logout"] = this.Wrap(this.Logout, USER_ROLE_GUEST)
|
||||
routeMap["/api/user/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
|
||||
routeMap["/api/user/page"] = this.Wrap(this.Page, USER_ROLE_ADMINISTRATOR)
|
||||
routeMap["/api/user/disable"] = this.Wrap(this.Disable, USER_ROLE_ADMINISTRATOR)
|
||||
routeMap["/api/user/enable"] = this.Wrap(this.Enable, USER_ROLE_ADMINISTRATOR)
|
||||
|
||||
return routeMap
|
||||
}
|
||||
|
||||
//使用用户名和密码进行登录。
|
||||
//参数:
|
||||
// @email:邮箱
|
||||
// @password:密码
|
||||
func (this *UserController) Login(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
email := request.FormValue("email")
|
||||
password := request.FormValue("password")
|
||||
|
||||
if "" == email || "" == password {
|
||||
|
||||
panic(result.BadRequest("请输入邮箱和密码"))
|
||||
}
|
||||
|
||||
user := this.userDao.FindByEmail(email)
|
||||
if user == nil {
|
||||
|
||||
panic(result.BadRequest("邮箱或密码错误"))
|
||||
|
||||
} else {
|
||||
if !util.MatchBcrypt(password, user.Password) {
|
||||
|
||||
panic(result.BadRequest("邮箱或密码错误"))
|
||||
}
|
||||
}
|
||||
|
||||
//登录成功,设置Cookie。有效期30天。
|
||||
expiration := time.Now()
|
||||
expiration = expiration.AddDate(0, 0, 30)
|
||||
|
||||
//持久化用户的session.
|
||||
session := &Session{
|
||||
UserUuid: user.Uuid,
|
||||
Ip: util.GetIpAddress(request),
|
||||
ExpireTime: expiration,
|
||||
}
|
||||
session.UpdateTime = time.Now()
|
||||
session.CreateTime = time.Now()
|
||||
session = this.sessionDao.Create(session)
|
||||
|
||||
//设置用户的cookie.
|
||||
cookie := http.Cookie{
|
||||
Name: config.COOKIE_AUTH_KEY,
|
||||
Path: "/",
|
||||
Value: session.Uuid,
|
||||
Expires: expiration}
|
||||
http.SetCookie(writer, &cookie)
|
||||
|
||||
//更新用户上次登录时间和ip
|
||||
user.LastTime = time.Now()
|
||||
user.LastIp = util.GetIpAddress(request)
|
||||
this.userDao.Save(user)
|
||||
|
||||
return this.Success(user)
|
||||
}
|
||||
|
||||
//创建一个用户
|
||||
func (this *UserController) Create(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
username := request.FormValue("username")
|
||||
if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, username); !m {
|
||||
panic(`用户名必填,且只能包含字母,数字和'_''`)
|
||||
}
|
||||
password := request.FormValue("password")
|
||||
if len(password) < 6 {
|
||||
panic(`密码长度至少为6位`)
|
||||
}
|
||||
|
||||
email := request.FormValue("email")
|
||||
if email == "" {
|
||||
panic("邮箱必填!")
|
||||
}
|
||||
|
||||
avatarUrl := request.FormValue("avatarUrl")
|
||||
phone := request.FormValue("phone")
|
||||
gender := request.FormValue("gender")
|
||||
role := request.FormValue("role")
|
||||
city := request.FormValue("city")
|
||||
|
||||
//判断用户上传大小限制。
|
||||
sizeLimitStr := request.FormValue("sizeLimit")
|
||||
var sizeLimit int64 = 0
|
||||
if sizeLimitStr == "" {
|
||||
panic("用户上传限制必填!")
|
||||
} else {
|
||||
intsizeLimit, err := strconv.Atoi(sizeLimitStr)
|
||||
if err != nil {
|
||||
this.PanicError(err)
|
||||
}
|
||||
sizeLimit = int64(intsizeLimit)
|
||||
}
|
||||
|
||||
//判断重名。
|
||||
if this.userDao.CountByUsername(username) > 0 {
|
||||
panic(username + "已经被其他用户占用。")
|
||||
}
|
||||
//判断邮箱重名
|
||||
if this.userDao.CountByEmail(email) > 0 {
|
||||
panic(email + "已经被其他用户占用。")
|
||||
}
|
||||
|
||||
user := &User{
|
||||
Role: GetRole(role),
|
||||
Username: username,
|
||||
Password: util.GetBcrypt(password),
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
Gender: gender,
|
||||
City: city,
|
||||
AvatarUrl: avatarUrl,
|
||||
SizeLimit: sizeLimit,
|
||||
Status: USER_STATUS_OK,
|
||||
}
|
||||
|
||||
user = this.userDao.Create(user)
|
||||
|
||||
return this.Success(user)
|
||||
}
|
||||
|
||||
//编辑一个用户的资料。
|
||||
func (this *UserController) Edit(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
avatarUrl := request.FormValue("avatarUrl")
|
||||
uuid := request.FormValue("uuid")
|
||||
phone := request.FormValue("phone")
|
||||
gender := request.FormValue("gender")
|
||||
city := request.FormValue("city")
|
||||
|
||||
currentUser := this.checkUser(writer, request)
|
||||
user := this.userDao.CheckByUuid(uuid)
|
||||
|
||||
if currentUser.Role == USER_ROLE_ADMINISTRATOR {
|
||||
//只有管理员可以改变用户上传的大小
|
||||
//判断用户上传大小限制。
|
||||
sizeLimitStr := request.FormValue("sizeLimit")
|
||||
var sizeLimit int64 = 0
|
||||
if sizeLimitStr == "" {
|
||||
panic("用户上传限制必填!")
|
||||
} else {
|
||||
intsizeLimit, err := strconv.Atoi(sizeLimitStr)
|
||||
if err != nil {
|
||||
this.PanicError(err)
|
||||
}
|
||||
sizeLimit = int64(intsizeLimit)
|
||||
}
|
||||
user.SizeLimit = sizeLimit
|
||||
} else {
|
||||
if currentUser.Uuid != uuid {
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
}
|
||||
|
||||
user.AvatarUrl = avatarUrl
|
||||
user.Phone = phone
|
||||
user.Gender = GetGender(gender)
|
||||
user.City = city
|
||||
|
||||
user = this.userDao.Save(user)
|
||||
|
||||
return this.Success(user)
|
||||
}
|
||||
|
||||
//获取用户详情
|
||||
func (this *UserController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
|
||||
user := this.userDao.CheckByUuid(uuid)
|
||||
|
||||
return this.Success(user)
|
||||
|
||||
}
|
||||
|
||||
//退出登录
|
||||
func (this *UserController) Logout(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
//session置为过期
|
||||
sessionCookie, err := request.Cookie(config.COOKIE_AUTH_KEY)
|
||||
if err != nil {
|
||||
return this.Success("已经退出登录了!")
|
||||
}
|
||||
sessionId := sessionCookie.Value
|
||||
|
||||
user := this.findUser(writer, request)
|
||||
if user != nil {
|
||||
session := this.sessionDao.FindByUuid(sessionId)
|
||||
session.ExpireTime = time.Now()
|
||||
this.sessionDao.Save(session)
|
||||
}
|
||||
|
||||
//删掉session缓存
|
||||
_, err = CONTEXT.SessionCache.Delete(sessionId)
|
||||
if err != nil {
|
||||
this.logger.Error("删除用户session缓存时出错")
|
||||
}
|
||||
|
||||
//清空客户端的cookie.
|
||||
expiration := time.Now()
|
||||
expiration = expiration.AddDate(-1, 0, 0)
|
||||
cookie := http.Cookie{
|
||||
Name: config.COOKIE_AUTH_KEY,
|
||||
Path: "/",
|
||||
Value: sessionId,
|
||||
Expires: expiration}
|
||||
http.SetCookie(writer, &cookie)
|
||||
|
||||
return this.Success("退出成功!")
|
||||
}
|
||||
|
||||
//获取用户列表 管理员的权限。
|
||||
func (this *UserController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
pageStr := request.FormValue("page")
|
||||
pageSizeStr := request.FormValue("pageSize")
|
||||
orderCreateTime := request.FormValue("orderCreateTime")
|
||||
orderUpdateTime := request.FormValue("orderUpdateTime")
|
||||
orderSort := request.FormValue("orderSort")
|
||||
|
||||
username := request.FormValue("username")
|
||||
email := request.FormValue("email")
|
||||
phone := request.FormValue("phone")
|
||||
status := request.FormValue("status")
|
||||
orderLastTime := request.FormValue("orderLastTime")
|
||||
|
||||
var page int
|
||||
if pageStr != "" {
|
||||
page, _ = strconv.Atoi(pageStr)
|
||||
}
|
||||
|
||||
pageSize := 200
|
||||
if pageSizeStr != "" {
|
||||
tmp, err := strconv.Atoi(pageSizeStr)
|
||||
if err == nil {
|
||||
pageSize = tmp
|
||||
}
|
||||
}
|
||||
|
||||
sortArray := []builder.OrderPair{
|
||||
{
|
||||
key: "create_time",
|
||||
value: orderCreateTime,
|
||||
},
|
||||
{
|
||||
key: "update_time",
|
||||
value: orderUpdateTime,
|
||||
},
|
||||
{
|
||||
key: "sort",
|
||||
value: orderSort,
|
||||
},
|
||||
{
|
||||
key: "last_time",
|
||||
value: orderLastTime,
|
||||
},
|
||||
}
|
||||
|
||||
pager := this.userDao.Page(page, pageSize, username, email, phone, status, sortArray)
|
||||
|
||||
return this.Success(pager)
|
||||
}
|
||||
|
||||
//禁用用户
|
||||
func (this *UserController) Disable(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
|
||||
user := this.userDao.CheckByUuid(uuid)
|
||||
|
||||
loginUser := this.checkUser(writer, request)
|
||||
if uuid == loginUser.Uuid {
|
||||
panic(result.BadRequest("你不能操作自己的状态。"))
|
||||
}
|
||||
|
||||
if user.Status == USER_STATUS_DISABLED {
|
||||
panic(result.BadRequest("用户已经被禁用,操作无效。"))
|
||||
}
|
||||
|
||||
user.Status = USER_STATUS_DISABLED
|
||||
|
||||
user = this.userDao.Save(user)
|
||||
|
||||
return this.Success(user)
|
||||
|
||||
}
|
||||
|
||||
//启用用户
|
||||
func (this *UserController) Enable(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
uuid := request.FormValue("uuid")
|
||||
|
||||
user := this.userDao.CheckByUuid(uuid)
|
||||
loginUser := this.checkUser(writer, request)
|
||||
if uuid == loginUser.Uuid {
|
||||
panic(result.BadRequest("你不能操作自己的状态。"))
|
||||
}
|
||||
|
||||
if user.Status == USER_STATUS_OK {
|
||||
panic(result.BadRequest("用户已经是正常状态,操作无效。"))
|
||||
}
|
||||
|
||||
user.Status = USER_STATUS_OK
|
||||
|
||||
user = this.userDao.Save(user)
|
||||
|
||||
return this.Success(user)
|
||||
|
||||
}
|
||||
|
||||
//用户修改密码
|
||||
func (this *UserController) ChangePassword(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
oldPassword := request.FormValue("oldPassword")
|
||||
newPassword := request.FormValue("newPassword")
|
||||
if oldPassword == "" || newPassword == "" {
|
||||
panic(result.BadRequest("旧密码和新密码都不能为空"))
|
||||
}
|
||||
|
||||
user := this.checkUser(writer, request)
|
||||
|
||||
//如果是demo账号,不提供修改密码的功能。
|
||||
if user.Username == "demo" {
|
||||
return this.Success(user)
|
||||
}
|
||||
|
||||
if !util.MatchBcrypt(oldPassword, user.Password) {
|
||||
panic(result.BadRequest("旧密码不正确!"))
|
||||
}
|
||||
|
||||
user.Password = util.GetBcrypt(newPassword)
|
||||
|
||||
user = this.userDao.Save(user)
|
||||
|
||||
return this.Success(user)
|
||||
}
|
||||
|
||||
//管理员重置用户密码
|
||||
func (this *UserController) ResetPassword(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||
|
||||
userUuid := request.FormValue("userUuid")
|
||||
password := request.FormValue("password")
|
||||
if userUuid == "" {
|
||||
panic(result.BadRequest("用户不能为空"))
|
||||
}
|
||||
if password == "" {
|
||||
panic(result.BadRequest("密码不能为空"))
|
||||
}
|
||||
|
||||
currentUser := this.checkUser(writer, request)
|
||||
|
||||
if currentUser.Role != USER_ROLE_ADMINISTRATOR {
|
||||
panic(result.Unauthorized("没有权限"))
|
||||
}
|
||||
|
||||
user := this.userDao.CheckByUuid(userUuid)
|
||||
|
||||
user.Password = util.GetBcrypt(password)
|
||||
|
||||
user = this.userDao.Save(user)
|
||||
|
||||
return this.Success(currentUser)
|
||||
}
|
158
code/rest/user_dao.go
Normal file
158
code/rest/user_dao.go
Normal file
@ -0,0 +1,158 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"github.com/nu7hatch/gouuid"
|
||||
"tank/code/tool/builder"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UserDao struct {
|
||||
BaseDao
|
||||
}
|
||||
|
||||
//创建用户
|
||||
func (this *UserDao) Create(user *User) *User {
|
||||
|
||||
if user == nil {
|
||||
panic("参数不能为nil")
|
||||
}
|
||||
|
||||
timeUUID, _ := uuid.NewV4()
|
||||
user.Uuid = string(timeUUID.String())
|
||||
user.CreateTime = time.Now()
|
||||
user.UpdateTime = time.Now()
|
||||
user.LastTime = time.Now()
|
||||
user.Sort = time.Now().UnixNano() / 1e6
|
||||
|
||||
db := CONTEXT.DB.Create(user)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
//按照Id查询用户,找不到返回nil
|
||||
func (this *UserDao) FindByUuid(uuid string) *User {
|
||||
|
||||
// Read
|
||||
var user *User = &User{}
|
||||
db := CONTEXT.DB.Where(&User{Base: Base{Uuid: uuid}}).First(user)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
//按照Id查询用户,找不到抛panic
|
||||
func (this *UserDao) CheckByUuid(uuid string) *User {
|
||||
|
||||
if uuid == "" {
|
||||
panic("uuid必须指定")
|
||||
}
|
||||
|
||||
// Read
|
||||
var user = &User{}
|
||||
db := CONTEXT.DB.Where(&User{Base: Base{Uuid: uuid}}).First(user)
|
||||
this.PanicError(db.Error)
|
||||
return user
|
||||
}
|
||||
|
||||
//按照邮箱查询用户。
|
||||
func (this *UserDao) FindByUsername(username string) *User {
|
||||
|
||||
var user = &User{}
|
||||
db := CONTEXT.DB.Where(&User{Username: username}).First(user)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
//按照邮箱查询用户。
|
||||
func (this *UserDao) FindByEmail(email string) *User {
|
||||
|
||||
var user *User = &User{}
|
||||
db := CONTEXT.DB.Where(&User{Email: email}).First(user)
|
||||
if db.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
//显示用户列表。
|
||||
func (this *UserDao) Page(page int, pageSize int, username string, email string, phone string, status string, sortArray []builder.OrderPair) *Pager {
|
||||
|
||||
var wp = &builder.WherePair{}
|
||||
|
||||
if username != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "username LIKE ?", Args: []interface{}{"%" + username + "%"}})
|
||||
}
|
||||
|
||||
if email != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "email LIKE ?", Args: []interface{}{"%" + email + "%"}})
|
||||
}
|
||||
|
||||
if phone != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "phone = ?", Args: []interface{}{phone}})
|
||||
}
|
||||
|
||||
if status != "" {
|
||||
wp = wp.And(&builder.WherePair{Query: "status = ?", Args: []interface{}{status}})
|
||||
}
|
||||
|
||||
count := 0
|
||||
db := CONTEXT.DB.Model(&User{}).Where(wp.Query, wp.Args...).Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
|
||||
var users []*User
|
||||
orderStr := this.GetSortString(sortArray)
|
||||
if orderStr == "" {
|
||||
db = CONTEXT.DB.Where(wp.Query, wp.Args...).Offset(page * pageSize).Limit(pageSize).Find(&users)
|
||||
} else {
|
||||
db = CONTEXT.DB.Where(wp.Query, wp.Args...).Order(orderStr).Offset(page * pageSize).Limit(pageSize).Find(&users)
|
||||
}
|
||||
|
||||
this.PanicError(db.Error)
|
||||
|
||||
pager := NewPager(page, pageSize, count, users)
|
||||
|
||||
return pager
|
||||
}
|
||||
|
||||
//查询某个用户名是否已经有用户了
|
||||
func (this *UserDao) CountByUsername(username string) int {
|
||||
var count int
|
||||
db := CONTEXT.DB.
|
||||
Model(&User{}).
|
||||
Where("username = ?", username).
|
||||
Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
return count
|
||||
}
|
||||
|
||||
//查询某个邮箱是否已经有用户了
|
||||
func (this *UserDao) CountByEmail(email string) int {
|
||||
var count int
|
||||
db := CONTEXT.DB.
|
||||
Model(&User{}).
|
||||
Where("email = ?", email).
|
||||
Count(&count)
|
||||
this.PanicError(db.Error)
|
||||
return count
|
||||
}
|
||||
|
||||
//保存用户
|
||||
func (this *UserDao) Save(user *User) *User {
|
||||
|
||||
user.UpdateTime = time.Now()
|
||||
db := CONTEXT.DB.
|
||||
Save(user)
|
||||
this.PanicError(db.Error)
|
||||
return user
|
||||
}
|
||||
|
||||
//执行清理操作
|
||||
func (this *UserDao) Cleanup() {
|
||||
this.logger.Info("[UserDao]执行清理:清除数据库中所有User记录。")
|
||||
db := CONTEXT.DB.Where("uuid is not null and role != ?", USER_ROLE_ADMINISTRATOR).Delete(User{})
|
||||
this.PanicError(db.Error)
|
||||
}
|
67
code/rest/user_model.go
Normal file
67
code/rest/user_model.go
Normal file
@ -0,0 +1,67 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"tank/code/config"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
//游客身份
|
||||
USER_ROLE_GUEST = "GUEST"
|
||||
//普通注册用户
|
||||
USER_ROLE_USER = "USER"
|
||||
//管理员
|
||||
USER_ROLE_ADMINISTRATOR = "ADMINISTRATOR"
|
||||
)
|
||||
|
||||
const (
|
||||
USER_GENDER_MALE = "MALE"
|
||||
USER_GENDER_FEMALE = "FEMALE"
|
||||
USER_GENDER_UNKNOWN = "UNKNOWN"
|
||||
)
|
||||
|
||||
const (
|
||||
//正常状态
|
||||
USER_STATUS_OK = "OK"
|
||||
//被禁用
|
||||
USER_STATUS_DISABLED = "DISABLED"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Base
|
||||
Role string `json:"role" gorm:"type:varchar(45)"`
|
||||
Username string `json:"username" gorm:"type:varchar(45) not null;unique"`
|
||||
Password string `json:"-" gorm:"type:varchar(255)"`
|
||||
Email string `json:"email" gorm:"type:varchar(45) not null;unique"`
|
||||
Phone string `json:"phone" gorm:"type:varchar(45)"`
|
||||
Gender string `json:"gender" gorm:"type:varchar(45)"`
|
||||
City string `json:"city" gorm:"type:varchar(45)"`
|
||||
AvatarUrl string `json:"avatarUrl" gorm:"type:varchar(255)"`
|
||||
LastIp string `json:"lastIp" gorm:"type:varchar(128)"`
|
||||
LastTime time.Time `json:"lastTime" gorm:"type:timestamp not null;default:'2018-01-01 00:00:00'"`
|
||||
SizeLimit int64 `json:"sizeLimit" gorm:"type:bigint(20) not null;default:-1"`
|
||||
Status string `json:"status" gorm:"type:varchar(45)"`
|
||||
}
|
||||
|
||||
// set User's table name to be `profiles`
|
||||
func (this *User) TableName() string {
|
||||
return config.TABLE_PREFIX + "user"
|
||||
}
|
||||
|
||||
//通过一个字符串获取性别
|
||||
func GetGender(genderString string) string {
|
||||
if genderString == USER_GENDER_MALE || genderString == USER_GENDER_FEMALE || genderString == USER_GENDER_UNKNOWN {
|
||||
return genderString
|
||||
} else {
|
||||
return USER_GENDER_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
//通过一个字符串获取角色
|
||||
func GetRole(roleString string) string {
|
||||
if roleString == USER_ROLE_USER || roleString == USER_ROLE_ADMINISTRATOR {
|
||||
return roleString
|
||||
} else {
|
||||
return USER_ROLE_USER
|
||||
}
|
||||
}
|
114
code/rest/user_service.go
Normal file
114
code/rest/user_service.go
Normal file
@ -0,0 +1,114 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"tank/code/config"
|
||||
cache2 "tank/code/tool/cache"
|
||||
"tank/code/tool/result"
|
||||
"time"
|
||||
)
|
||||
|
||||
//@Service
|
||||
type UserService struct {
|
||||
Bean
|
||||
userDao *UserDao
|
||||
sessionDao *SessionDao
|
||||
|
||||
//操作文件的锁。
|
||||
locker *cache2.Table
|
||||
}
|
||||
|
||||
//初始化方法
|
||||
func (this *UserService) Init() {
|
||||
this.Bean.Init()
|
||||
|
||||
//手动装填本实例的Bean. 这里必须要用中间变量方可。
|
||||
b := CONTEXT.GetBean(this.userDao)
|
||||
if b, ok := b.(*UserDao); ok {
|
||||
this.userDao = b
|
||||
}
|
||||
|
||||
b = CONTEXT.GetBean(this.sessionDao)
|
||||
if b, ok := b.(*SessionDao); ok {
|
||||
this.sessionDao = b
|
||||
}
|
||||
|
||||
//创建一个用于存储用户文件锁的缓存。
|
||||
this.locker = cache2.NewTable()
|
||||
}
|
||||
|
||||
|
||||
//对某个用户进行加锁。加锁阶段用户是不允许操作文件的。
|
||||
func (this *UserService) MatterLock(userUuid string) {
|
||||
//如果已经是锁住的状态,直接报错
|
||||
|
||||
//去缓存中捞取
|
||||
cacheItem, err := this.locker.Value(userUuid)
|
||||
if err != nil {
|
||||
this.logger.Error("获取缓存时出错了" + err.Error())
|
||||
}
|
||||
|
||||
//当前被锁住了。
|
||||
if cacheItem != nil && cacheItem.Data() != nil {
|
||||
panic(result.BadRequest("当前正在进行文件操作,请稍后再试!"))
|
||||
}
|
||||
|
||||
//添加一把新锁,有效期为12小时
|
||||
duration := 12 * time.Hour
|
||||
this.locker.Add(userUuid, duration, true)
|
||||
}
|
||||
|
||||
|
||||
//对某个用户解锁,解锁后用户可以操作文件。
|
||||
func (this *UserService) MatterUnlock(userUuid string) {
|
||||
|
||||
exist := this.locker.Exists(userUuid)
|
||||
if exist {
|
||||
_, err := this.locker.Delete(userUuid)
|
||||
this.PanicError(err)
|
||||
} else {
|
||||
this.logger.Error("%s已经不存在matter锁了,解锁错误。", userUuid)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//装载session信息,如果session没有了根据cookie去装填用户信息。
|
||||
//在所有的路由最初会调用这个方法
|
||||
func (this *UserService) bootstrap(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
//登录身份有效期以数据库中记录的为准
|
||||
|
||||
//验证用户是否已经登录。
|
||||
sessionCookie, err := request.Cookie(config.COOKIE_AUTH_KEY)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sessionId := sessionCookie.Value
|
||||
|
||||
//去缓存中捞取
|
||||
cacheItem, err := CONTEXT.SessionCache.Value(sessionId)
|
||||
if err != nil {
|
||||
this.logger.Error("获取缓存时出错了" + err.Error())
|
||||
}
|
||||
|
||||
//缓存中没有,尝试去数据库捞取
|
||||
if cacheItem == nil || cacheItem.Data() == nil {
|
||||
session := this.sessionDao.FindByUuid(sessionCookie.Value)
|
||||
if session != nil {
|
||||
duration := session.ExpireTime.Sub(time.Now())
|
||||
if duration <= 0 {
|
||||
this.logger.Error("登录信息已过期")
|
||||
} else {
|
||||
user := this.userDao.FindByUuid(session.UserUuid)
|
||||
if user != nil {
|
||||
//将用户装填进缓存中
|
||||
CONTEXT.SessionCache.Add(sessionCookie.Value, duration, user)
|
||||
} else {
|
||||
this.logger.Error("没有找到对应的user " + session.UserUuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user