初始化

This commit is contained in:
2026-07-03 15:58:29 +08:00
parent 3a942c882e
commit dc90e889e1
77 changed files with 12912 additions and 0 deletions

View File

@@ -0,0 +1,356 @@
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", "0")
folderID, _ := strconv.ParseUint(folderIDStr, 10, 64)
// 获取存储策略ID(可选, 0=使用默认策略)
policyIDStr := ctx.DefaultQuery("storage_policy_id", "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
}
downloadURL, err := c.fileService.GetDownloadURL(id)
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
}
previewURL, err := c.fileService.GetPreviewURL(id)
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 uint64 `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, 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)
}