442 lines
11 KiB
Go
442 lines
11 KiB
Go
package controller
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"seeyon-filesystem/dto"
|
|
"seeyon-filesystem/middleware"
|
|
"seeyon-filesystem/service"
|
|
"seeyon-filesystem/utils"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// FileController 文件控制器
|
|
type FileController struct {
|
|
fileService *service.FileService
|
|
}
|
|
|
|
// NewFileController 创建文件控制器实例
|
|
func NewFileController() *FileController {
|
|
return &FileController{
|
|
fileService: service.NewFileService(),
|
|
}
|
|
}
|
|
|
|
// Upload 上传文件
|
|
// POST /api/file/upload?folder_id=1&storage_policy_id=1
|
|
func (c *FileController) Upload(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
// 获取文件
|
|
fileHeader, err := ctx.FormFile("file")
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "请选择要上传的文件")
|
|
return
|
|
}
|
|
|
|
// 获取目标文件夹ID(优先查询参数, 其次表单字段)
|
|
folderIDStr := ctx.DefaultQuery("folder_id", "")
|
|
if folderIDStr == "" {
|
|
folderIDStr = ctx.PostForm("folder_id")
|
|
}
|
|
if folderIDStr == "" {
|
|
folderIDStr = "0"
|
|
}
|
|
folderID, _ := strconv.ParseUint(folderIDStr, 10, 64)
|
|
|
|
// 获取存储策略ID(可选, 0=使用默认策略)
|
|
policyIDStr := ctx.DefaultQuery("storage_policy_id", "")
|
|
if policyIDStr == "" {
|
|
policyIDStr = ctx.PostForm("storage_policy_id")
|
|
}
|
|
if policyIDStr == "" {
|
|
policyIDStr = "0"
|
|
}
|
|
storagePolicyID, _ := strconv.ParseUint(policyIDStr, 10, 64)
|
|
|
|
// 调用服务层
|
|
result, err := c.fileService.Upload(fileHeader, folderID, ownerID, storagePolicyID)
|
|
if err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.Success(ctx, result)
|
|
}
|
|
|
|
// GetByID 获取文件信息
|
|
// GET /api/file/:id
|
|
func (c *FileController) GetByID(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
result, err := c.fileService.GetByID(id, ownerID)
|
|
if err != nil {
|
|
utils.NotFound(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.Success(ctx, result)
|
|
}
|
|
|
|
// Download 下载文件
|
|
// GET /api/file/:id/download
|
|
func (c *FileController) Download(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
reader, file, err := c.fileService.Download(id, ownerID)
|
|
if err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
defer reader.Close()
|
|
|
|
// 设置下载响应头
|
|
ctx.Header("Content-Disposition", "attachment; filename=\""+file.Name+"\"")
|
|
ctx.Header("Content-Type", file.MimeType)
|
|
ctx.DataFromReader(http.StatusOK, file.Size, file.MimeType, reader, nil)
|
|
}
|
|
|
|
// GetDownloadURL 获取文件下载链接
|
|
// GET /api/file/:id/download-url
|
|
func (c *FileController) GetDownloadURL(ctx *gin.Context) {
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
token := ctx.GetHeader("token")
|
|
downloadURL, err := c.fileService.GetDownloadURL(id, token)
|
|
if err != nil {
|
|
utils.NotFound(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.Success(ctx, gin.H{"download_url": downloadURL})
|
|
}
|
|
|
|
// GetPreviewURL 获取文件预览链接
|
|
// GET /api/file/:id/preview-url
|
|
func (c *FileController) GetPreviewURL(ctx *gin.Context) {
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
token := ctx.GetHeader("token")
|
|
|
|
previewURL, err := c.fileService.GetPreviewURL(id, token)
|
|
if err != nil {
|
|
utils.NotFound(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.Success(ctx, gin.H{"preview_url": previewURL})
|
|
}
|
|
|
|
// Preview 文件预览(直接返回文件流, 浏览器内联展示)
|
|
// GET /api/file/:id/preview
|
|
func (c *FileController) Preview(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
reader, file, err := c.fileService.Download(id, ownerID)
|
|
if err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
defer reader.Close()
|
|
|
|
// 内联展示而非下载
|
|
ctx.Header("Content-Disposition", "inline; filename=\""+file.Name+"\"")
|
|
ctx.Header("Content-Type", file.MimeType)
|
|
ctx.DataFromReader(http.StatusOK, file.Size, file.MimeType, reader, nil)
|
|
}
|
|
|
|
// GetByKey 根据fileKey查询文件信息
|
|
// GET /api/file/info?key=xxx
|
|
func (c *FileController) GetByKey(ctx *gin.Context) {
|
|
key := ctx.Query("key")
|
|
if key == "" {
|
|
utils.BadRequest(ctx, "请提供fileKey参数")
|
|
return
|
|
}
|
|
|
|
result, err := c.fileService.GetByKey(key)
|
|
if err != nil {
|
|
utils.NotFound(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.Success(ctx, result)
|
|
}
|
|
|
|
// Rename 重命名文件
|
|
// PUT /api/file/:id/rename
|
|
func (c *FileController) Rename(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
NewName string `json:"new_name" binding:"required"`
|
|
Version int `json:"version"` // 乐观锁版本号
|
|
}
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
utils.BadRequest(ctx, "参数错误: "+err.Error())
|
|
return
|
|
}
|
|
|
|
if err := c.fileService.Rename(id, req.NewName, ownerID, req.Version); err != nil {
|
|
if strings.Contains(err.Error(), "已被其他人修改") {
|
|
utils.Error(ctx, 409, 409, err.Error())
|
|
return
|
|
}
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessWithMessage(ctx, "重命名成功", nil)
|
|
}
|
|
|
|
// Move 移动文件
|
|
// POST /api/file/:id/move
|
|
func (c *FileController) Move(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
TargetFolderID dto.StringID `json:"target_folder_id" binding:"required"`
|
|
}
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
utils.BadRequest(ctx, "参数错误: "+err.Error())
|
|
return
|
|
}
|
|
|
|
if err := c.fileService.Move(id, uint64(req.TargetFolderID), ownerID); err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessWithMessage(ctx, "移动成功", nil)
|
|
}
|
|
|
|
// SoftDelete 移入回收站
|
|
// DELETE /api/file/:id
|
|
func (c *FileController) SoftDelete(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
if err := c.fileService.SoftDelete(id, ownerID); err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessWithMessage(ctx, "已移入回收站", nil)
|
|
}
|
|
|
|
// Restore 从回收站恢复
|
|
// POST /api/file/:id/restore
|
|
func (c *FileController) Restore(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
if err := c.fileService.Restore(id, ownerID); err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessWithMessage(ctx, "已恢复", nil)
|
|
}
|
|
|
|
// HardDelete 永久删除
|
|
// DELETE /api/file/:id/permanent
|
|
func (c *FileController) HardDelete(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
if err := c.fileService.HardDelete(id, ownerID); err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessWithMessage(ctx, "已永久删除", nil)
|
|
}
|
|
|
|
// Search 搜索文件
|
|
// GET /api/file/search?keyword=xxx
|
|
func (c *FileController) Search(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
keyword := ctx.Query("keyword")
|
|
typeFilter := ctx.Query("type") // 可选: image/video/document/audio/archive/code
|
|
|
|
if keyword == "" && typeFilter == "" {
|
|
utils.BadRequest(ctx, "请输入搜索关键词或指定类型")
|
|
return
|
|
}
|
|
|
|
var results []dto.FileVO
|
|
var err error
|
|
|
|
if typeFilter != "" {
|
|
// 按类型搜索
|
|
exts := getExtensionsByType(typeFilter)
|
|
results, err = c.fileService.SearchByType(ownerID, exts)
|
|
} else {
|
|
// 按关键词搜索(支持|分隔多关键词)
|
|
results, err = c.fileService.Search(ownerID, keyword)
|
|
}
|
|
|
|
if err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.Success(ctx, results)
|
|
}
|
|
|
|
// getExtensionsByType 根据类型获取扩展名列表
|
|
func getExtensionsByType(fileType string) []string {
|
|
typeMap := map[string][]string{
|
|
"image": {"jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "ico"},
|
|
"video": {"mp4", "avi", "mov", "wmv", "flv", "mkv", "webm"},
|
|
"audio": {"mp3", "wav", "ogg", "aac", "flac", "wma"},
|
|
"document": {"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md", "csv"},
|
|
"archive": {"zip", "rar", "7z", "tar", "gz", "bz2"},
|
|
"code": {"js", "ts", "py", "go", "java", "c", "cpp", "h", "css", "html", "sql", "json", "xml", "yaml", "yml"},
|
|
}
|
|
if exts, ok := typeMap[fileType]; ok {
|
|
return exts
|
|
}
|
|
return []string{fileType}
|
|
}
|
|
|
|
// ListTrashed 查询回收站
|
|
// GET /api/file/trash
|
|
func (c *FileController) ListTrashed(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
results, err := c.fileService.ListTrashed(ownerID)
|
|
if err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.Success(ctx, results)
|
|
}
|
|
|
|
// GrantPermission 授权文件权限
|
|
// POST /api/file/:id/grant
|
|
func (c *FileController) GrantPermission(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
var req dto.GrantPermissionRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
utils.BadRequest(ctx, "参数错误: "+err.Error())
|
|
return
|
|
}
|
|
|
|
if err := c.fileService.GrantPermission(id, ownerID, &req); err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessWithMessage(ctx, "授权成功", nil)
|
|
}
|
|
|
|
// RevokePermission 撤销文件权限
|
|
// DELETE /api/file/:id/grant/:permId
|
|
func (c *FileController) RevokePermission(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
fileID, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
permID, err := strconv.ParseUint(ctx.Param("permId"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的权限ID")
|
|
return
|
|
}
|
|
|
|
if err := c.fileService.RevokePermission(fileID, permID, ownerID); err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.SuccessWithMessage(ctx, "已撤销", nil)
|
|
}
|
|
|
|
// ListPermissions 查看文件的授权列表
|
|
// GET /api/file/:id/grant
|
|
func (c *FileController) ListPermissions(ctx *gin.Context) {
|
|
ownerID := middleware.GetCurrentUserID(ctx)
|
|
|
|
fileID, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
|
|
if err != nil {
|
|
utils.BadRequest(ctx, "无效的文件ID")
|
|
return
|
|
}
|
|
|
|
results, err := c.fileService.ListPermissions(fileID, ownerID)
|
|
if err != nil {
|
|
utils.ServerError(ctx, err.Error())
|
|
return
|
|
}
|
|
|
|
utils.Success(ctx, results)
|
|
}
|