# 文件存储模块设计文档 ## 概述 为项目新增通用文件存储服务模块,支持三种可配置的存储后端(本地文件系统、阿里云 OSS、MinIO),前端提供文件 CRUD 和预览功能。 **定位**:通用存储服务,可用于个人文件管理、公共资源管理、新闻图片视频等多种业务场景。 ## 设计决定 | 项目 | 决定 | 原因 | |------|------|------| | 文件归属 | 混合模式(公开/私有标记) | 灵活适配多种业务场景 | | 目录结构 | 平铺列表 + 分类标签 | 简单高效,适合通用存储 | | 预览类型 | 图片 + 视频 + PDF | 覆盖主流文件类型 | | 存储切换 | 配置文件单选 | 简单清晰,重启生效 | | 架构模式 | Strategy Pattern 存储抽象层 | 干净、可扩展、易测试 | ## 后端架构 ### 1. Storage 接口(Strategy Pattern) ```go // backend/internal/storage/storage.go type Storage interface { Upload(ctx context.Context, key string, reader io.Reader, size int64, contentType string) error Delete(ctx context.Context, key string) error GetURL(ctx context.Context, key string) (string, error) Exists(ctx context.Context, key string) (bool, error) } ``` 三种实现: | 实现 | 文件 | GetURL 返回 | |------|------|------------| | LocalStorage | `storage/local.go` | `/api/v1/file/:id/download` 由后端代理 | | OSSStorage | `storage/oss.go` | 阿里云签名 URL(带过期时间) | | MinIOStorage | `storage/minio.go` | Presigned URL | ### 2. 配置结构 ```yaml # etc/base-api.yaml Storage: Type: "local" # local / oss / minio MaxSize: 104857600 # 100MB 单文件限制 Local: RootDir: "./uploads" OSS: Endpoint: "oss-cn-hangzhou.aliyuncs.com" AccessKeyId: "" AccessKeySecret: "" Bucket: "" MinIO: Endpoint: "localhost:9000" AccessKeyId: "" AccessKeySecret: "" Bucket: "" UseSSL: false ``` ```go // backend/internal/config/config.go 新增 type StorageConfig struct { Type string `json:",default=local"` MaxSize int64 `json:",default=104857600"` // 100MB Local struct { RootDir string `json:",default=./uploads"` } OSS struct { Endpoint string AccessKeyId string AccessKeySecret string Bucket string } MinIO struct { Endpoint string AccessKeyId string AccessKeySecret string Bucket string UseSSL bool } } ``` ### 3. 数据模型 ```go // backend/model/file_entity.go type File struct { Id int64 `gorm:"primaryKey;autoIncrement" json:"id"` Name string `gorm:"type:varchar(255);not null" json:"name"` Key string `gorm:"type:varchar(500);uniqueIndex" json:"key"` Size int64 `gorm:"not null" json:"size"` MimeType string `gorm:"type:varchar(100)" json:"mimeType"` Category string `gorm:"type:varchar(50);index;default:'default'" json:"category"` IsPublic bool `gorm:"default:false" json:"isPublic"` UserId int64 `gorm:"index" json:"userId"` StorageType string `gorm:"type:varchar(20)" json:"storageType"` Status int `gorm:"default:1" json:"status"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } ``` Model 方法:Insert, FindOne, FindList (分页+筛选), Update, Delete ### 4. API 设计 ``` // backend/api/file.api type ( FileUploadResponse { Code int `json:"code"` Message string `json:"message"` Success bool `json:"success"` Data FileInfo `json:"data,omitempty"` } FileInfo { Id int64 `json:"id"` Name string `json:"name"` Key string `json:"key"` Size int64 `json:"size"` MimeType string `json:"mimeType"` Category string `json:"category"` IsPublic bool `json:"isPublic"` UserId int64 `json:"userId"` StorageType string `json:"storageType"` Url string `json:"url"` CreatedAt string `json:"createdAt"` UpdatedAt string `json:"updatedAt"` } FileListRequest { Page int `form:"page,default=1"` PageSize int `form:"pageSize,default=20"` Keyword string `form:"keyword,optional"` Category string `form:"category,optional"` MimeType string `form:"mimeType,optional"` } FileListResponse { Code int `json:"code"` Message string `json:"message"` Success bool `json:"success"` Data []FileInfo `json:"data"` Total int64 `json:"total"` } FileUpdateRequest { Name string `json:"name,optional"` Category string `json:"category,optional"` IsPublic *bool `json:"isPublic,optional"` } FileUrlResponse { Code int `json:"code"` Message string `json:"message"` Success bool `json:"success"` Url string `json:"url"` } ) @server ( prefix: /api/v1 middleware: Cors, Log, Auth, Authz group: file ) service base-api { @handler UploadFile post /file/upload (returns FileUploadResponse) @handler GetFileList get /files (FileListRequest) returns (FileListResponse) @handler GetFile get /file/:id returns (FileUploadResponse) @handler GetFileUrl get /file/:id/url returns (FileUrlResponse) @handler UpdateFile put /file/:id (FileUpdateRequest) returns (FileUploadResponse) @handler DeleteFile delete /file/:id returns (FileUploadResponse) } ``` ### 5. 权限策略(Casbin) ```go // 新增到 seedCasbinPolicies // user: 文件上传和查看 {"user", "/api/v1/file/upload", "POST"}, {"user", "/api/v1/files", "GET"}, {"user", "/api/v1/file/:id", "GET"}, {"user", "/api/v1/file/:id/url", "GET"}, {"user", "/api/v1/file/:id", "PUT"}, // super_admin: 文件删除 {"super_admin", "/api/v1/file/:id", "DELETE"}, ``` 业务层权限: - user 只能查看自己的 + isPublic=true 的文件 - user 只能编辑自己的文件 - admin 可查看和编辑所有文件 - super_admin 可删除任何文件 ### 6. Key 生成策略 ``` {category}/{YYYY-MM}/{uuid}{ext} ``` 例如:`default/2026-02/a1b2c3d4-e5f6.jpg` ## 前端设计 ### 路由 `/files` — 文件管理页面,侧边栏新增「文件管理」导航项 ### 文件管理页面 **表格列**: - 文件名(带 MIME 图标) - 分类(标签) - 大小(格式化为 KB/MB) - 类型(图片/视频/PDF/其他) - 上传者 - 公开状态(开关) - 上传时间 - 操作(预览、编辑、删除) **功能**: - 顶部:上传按钮 + 搜索 + 分类筛选 - 拖拽上传区域 + 点击上传 - 上传进度条 ### 预览弹窗 | 类型 | 实现 | |------|------| | 图片 (jpg/png/gif/webp) | `` 直接显示 | | 视频 (mp4/webm) | `