diff --git a/build/doc/db.sql b/build/doc/db.sql new file mode 100644 index 0000000..c5a1231 --- /dev/null +++ b/build/doc/db.sql @@ -0,0 +1,112 @@ +CREATE TABLE `tank20_download_token` ( + `uuid` char(36) NOT NULL, + `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', + `matter_uuid` char(36) DEFAULT NULL COMMENT '文件标识', + `expire_time` timestamp NULL DEFAULT NULL COMMENT '授权访问的次数', + `ip` varchar(45) DEFAULT NULL COMMENT '消费者的ip', + `sort` bigint(20) DEFAULT NULL, + `modify_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`uuid`), + UNIQUE KEY `id_UNIQUE` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='下载的token表'; + +CREATE TABLE `tank20_image_cache` ( + `uuid` char(36) NOT NULL, + `user_uuid` char(36) DEFAULT NULL COMMENT '上传的用户id', + `matter_uuid` char(36) DEFAULT NULL, + `uri` varchar(512) DEFAULT NULL COMMENT '请求的uri', + `md5` varchar(45) DEFAULT NULL COMMENT '文件的md5值', + `size` bigint(20) DEFAULT '0' COMMENT '文件大小', + `path` varchar(255) DEFAULT NULL, + `sort` bigint(20) DEFAULT NULL, + `modify_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`uuid`), + UNIQUE KEY `id_UNIQUE` (`uuid`), + UNIQUE KEY `uri_UNIQUE` (`uri`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='图片缓存表'; + +CREATE TABLE `tank20_matter` ( + `uuid` char(36) NOT NULL, + `puuid` varchar(45) DEFAULT NULL COMMENT '上一级的uuid', + `user_uuid` char(36) DEFAULT NULL COMMENT '上传的用户id', + `dir` tinyint(1) DEFAULT '0' COMMENT '是否是文件夹', + `alien` tinyint(1) DEFAULT '0', + `name` varchar(255) DEFAULT NULL COMMENT '文件名称', + `md5` varchar(45) DEFAULT NULL COMMENT '文件的md5值', + `size` bigint(20) DEFAULT '0' COMMENT '文件大小', + `privacy` tinyint(1) DEFAULT '0' COMMENT '文件是否是公有的', + `path` varchar(255) DEFAULT NULL, + `sort` bigint(20) DEFAULT NULL, + `modify_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`uuid`), + UNIQUE KEY `id_UNIQUE` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='file表'; + +CREATE TABLE `tank20_preference` ( + `uuid` char(36) NOT NULL, + `name` varchar(45) DEFAULT NULL COMMENT '网站名称', + `logo_url` varchar(255) DEFAULT NULL, + `favicon_url` varchar(255) DEFAULT NULL, + `footer_line1` varchar(1024) DEFAULT NULL, + `footer_line2` varchar(1024) DEFAULT NULL, + `version` varchar(45) DEFAULT NULL, + `sort` bigint(20) DEFAULT NULL, + `modify_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`uuid`), + UNIQUE KEY `id_UNIQUE` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='网站偏好设置表'; + +CREATE TABLE `tank20_session` ( + `uuid` char(36) NOT NULL, + `authentication` char(36) DEFAULT NULL COMMENT '认证身份,存放在cookie中', + `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', + `ip` varchar(45) DEFAULT NULL COMMENT '用户的ip地址', + `expire_time` timestamp NULL DEFAULT NULL, + `sort` bigint(20) DEFAULT NULL, + `modify_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`uuid`), + UNIQUE KEY `id_UNIQUE` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='session表'; + +CREATE TABLE `tank20_upload_token` ( + `uuid` char(36) NOT NULL, + `user_uuid` char(36) DEFAULT NULL COMMENT '用户uuid', + `folder_uuid` char(36) DEFAULT NULL, + `matter_uuid` char(36) DEFAULT NULL, + `filename` varchar(255) DEFAULT NULL COMMENT '文件后缀名的过滤,可以只允许用户上传特定格式的文件。', + `privacy` tinyint(1) DEFAULT '1', + `size` bigint(20) DEFAULT '0', + `expire_time` timestamp NULL DEFAULT NULL, + `ip` varchar(45) DEFAULT NULL COMMENT '消费者的ip', + `sort` bigint(20) DEFAULT NULL, + `modify_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`uuid`), + UNIQUE KEY `id_UNIQUE` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='上传的token表'; + +CREATE TABLE `tank20_user` ( + `uuid` char(36) NOT NULL, + `role` varchar(45) DEFAULT 'USER', + `username` varchar(255) DEFAULT NULL COMMENT '昵称', + `password` varchar(255) DEFAULT NULL COMMENT '密码', + `email` varchar(45) DEFAULT NULL COMMENT '邮箱', + `phone` varchar(45) DEFAULT NULL COMMENT '电话', + `gender` varchar(45) DEFAULT 'UNKNOWN' COMMENT '性别,默认未知', + `city` varchar(45) DEFAULT NULL COMMENT '城市', + `avatar_url` varchar(255) DEFAULT NULL COMMENT '头像链接', + `last_time` datetime DEFAULT NULL COMMENT '上次登录使劲按', + `last_ip` varchar(45) DEFAULT NULL, + `size_limit` int(11) DEFAULT '-1' COMMENT '该账号上传文件的大小限制,单位byte。<0 表示不设限制', + `status` varchar(45) DEFAULT 'OK', + `sort` bigint(20) DEFAULT NULL, + `modify_time` timestamp NULL DEFAULT NULL, + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`uuid`), + UNIQUE KEY `id_UNIQUE` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表描述'; diff --git a/rest/alien_controller.go b/rest/alien_controller.go index ba85498..fbf3e88 100644 --- a/rest/alien_controller.go +++ b/rest/alien_controller.go @@ -424,6 +424,10 @@ func (this *AlienController) Download(writer http.ResponseWriter, request *http. } } + + //缓存 + + //对图片做缩放处理。 imageProcess := request.FormValue("imageProcess") if imageProcess == "resize" { diff --git a/rest/config.go b/rest/config.go index 3329fc3..4091f4e 100644 --- a/rest/config.go +++ b/rest/config.go @@ -16,8 +16,8 @@ const ( //用户身份的cookie字段名 COOKIE_AUTH_KEY = "_ak" - //数据库表前缀 tank100表示当前应用版本是tank:1.0.x版,数据库结构发生变化必然是中型升级 - TABLE_PREFIX = "tank10_" + //数据库表前缀 tank200表示当前应用版本是tank:2.0.x版,数据库结构发生变化必然是中型升级 + TABLE_PREFIX = "tank20_" //当前版本 VERSION = "1.0.5" diff --git a/rest/context.go b/rest/context.go index 26e871b..8697f4f 100644 --- a/rest/context.go +++ b/rest/context.go @@ -99,6 +99,11 @@ func (this *Context) registerBeans() { //downloadToken this.registerBean(new(DownloadTokenDao)) + //imageCache + this.registerBean(new(ImageCacheController)) + this.registerBean(new(ImageCacheDao)) + this.registerBean(new(ImageCacheService)) + //matter this.registerBean(new(MatterController)) this.registerBean(new(MatterDao)) diff --git a/rest/image_cache_controller.go b/rest/image_cache_controller.go new file mode 100644 index 0000000..c609802 --- /dev/null +++ b/rest/image_cache_controller.go @@ -0,0 +1,158 @@ +package rest + +import ( + "net/http" + "strconv" + "strings" +) + +type ImageCacheController struct { + BaseController + imageCacheDao *ImageCacheDao + imageCacheService *ImageCacheService +} + +//初始化方法 +func (this *ImageCacheController) Init(context *Context) { + this.BaseController.Init(context) + + //手动装填本实例的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) *WebResult { + + uuid := request.FormValue("uuid") + if uuid == "" { + return this.Error("文件的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) *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 := []OrderPair{ + { + key: "create_time", + value: orderCreateTime, + }, + { + key: "size", + value: orderSize, + }, + } + + pager := this.imageCacheDao.Page(page, pageSize, userUuid, sortArray) + + return this.Success(pager) +} + +//删除一个文件 +func (this *ImageCacheController) Delete(writer http.ResponseWriter, request *http.Request) *WebResult { + + uuid := request.FormValue("uuid") + if uuid == "" { + return this.Error("文件的uuid必填") + } + + imageCache := this.imageCacheDao.FindByUuid(uuid) + + //判断文件的所属人是否正确 + user := this.checkUser(writer, request) + if user.Role != USER_ROLE_ADMINISTRATOR && imageCache.UserUuid != user.Uuid { + return this.Error(RESULT_CODE_UNAUTHORIZED) + } + + this.imageCacheDao.Delete(imageCache) + + return this.Success("删除成功!") +} + +//删除一系列文件。 +func (this *ImageCacheController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *WebResult { + + uuids := request.FormValue("uuids") + if uuids == "" { + return this.Error("文件的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 { + return this.Error(RESULT_CODE_UNAUTHORIZED) + } + + this.imageCacheDao.Delete(imageCache) + + } + + return this.Success("删除成功!") +} diff --git a/rest/image_cache_dao.go b/rest/image_cache_dao.go new file mode 100644 index 0000000..939c8b4 --- /dev/null +++ b/rest/image_cache_dao.go @@ -0,0 +1,142 @@ +package rest + +import ( + "fmt" + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/mysql" + "github.com/nu7hatch/gouuid" + "os" + "time" +) + +type ImageCacheDao struct { + BaseDao +} + +//按照Id查询文件 +func (this *ImageCacheDao) FindByUuid(uuid string) *ImageCache { + + // Read + var imageCache ImageCache + db := this.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 := this.context.DB.Where(&ImageCache{Base: Base{Uuid: uuid}}).First(&imageCache) + this.PanicError(db.Error) + + return &imageCache + +} + +//按照名字查询文件夹 +func (this *ImageCacheDao) FindByUri(uri string) *ImageCache { + + var wp = &WherePair{} + + wp = wp.And(&WherePair{Query: "uri = ?", Args: []interface{}{uri}}) + + var imageCache = &ImageCache{} + db := this.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 := this.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 := this.context.DB. + Where(ImageCache{UserUuid: userUuid}). + Find(&imageCaches) + this.PanicError(db.Error) + + return imageCaches +} + +//获取某个文件夹下所有的文件和子文件 +func (this *ImageCacheDao) Page(page int, pageSize int, userUuid string, sortArray []OrderPair) *Pager { + + var wp = &WherePair{} + + if userUuid != "" { + wp = wp.And(&WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}}) + } + + var conditionDB *gorm.DB + conditionDB = this.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.ModifyTime = time.Now() + db := this.context.DB.Create(imageCache) + this.PanicError(db.Error) + + return imageCache +} + +//修改一个文件 +func (this *ImageCacheDao) Save(imageCache *ImageCache) *ImageCache { + + imageCache.ModifyTime = time.Now() + db := this.context.DB.Save(imageCache) + this.PanicError(db.Error) + + return imageCache +} + +//删除一个文件,数据库中删除,物理磁盘上删除。 +func (this *ImageCacheDao) Delete(imageCache *ImageCache) { + + db := this.context.DB.Delete(&imageCache) + this.PanicError(db.Error) + + //删除文件 + err := os.Remove(CONFIG.MatterPath + imageCache.Path) + + if err != nil { + LogError(fmt.Sprintf("删除磁盘上的文件出错,不做任何处理")) + //this.PanicError(err) + } +} diff --git a/rest/image_cache_model.go b/rest/image_cache_model.go new file mode 100644 index 0000000..3be6fda --- /dev/null +++ b/rest/image_cache_model.go @@ -0,0 +1,20 @@ +package rest + +/** + * 图片缓存,对于那些处理过的图片,统一管理在这里。 + */ +type ImageCache struct { + Base + UserUuid string `json:"userUuid"` + MatterUuid string `json:"matterUuid"` + Uri string `json:"name"` + Md5 string `json:"md5"` + Size int64 `json:"size"` + Path string `json:"path"` + Matter *Matter `gorm:"-" json:"matter"` +} + +// set File's table name to be `profiles` +func (ImageCache) TableName() string { + return TABLE_PREFIX + "image_cache" +} diff --git a/rest/image_cache_service.go b/rest/image_cache_service.go new file mode 100644 index 0000000..3740c38 --- /dev/null +++ b/rest/image_cache_service.go @@ -0,0 +1,26 @@ +package rest + +//@Service +type ImageCacheService struct { + Bean + imageCacheDao *ImageCacheDao +} + +//初始化方法 +func (this *ImageCacheService) Init(context *Context) { + + //手动装填本实例的Bean. 这里必须要用中间变量方可。 + b := context.GetBean(this.imageCacheDao) + if b, ok := b.(*ImageCacheDao); ok { + this.imageCacheDao = b + } + +} + +//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。 +func (this *ImageCacheService) Detail(uuid string) *ImageCache { + + imageCache := this.imageCacheDao.CheckByUuid(uuid) + + return imageCache +} diff --git a/rest/matter_dao.go b/rest/matter_dao.go index a951466..970ae27 100644 --- a/rest/matter_dao.go +++ b/rest/matter_dao.go @@ -230,9 +230,8 @@ func (this *MatterDao) Delete(matter *Matter) { if err != nil { LogError(fmt.Sprintf("删除磁盘上的文件出错,不做任何处理")) + //this.PanicError(err) } - this.PanicError(err) - } }