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) }