You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

8.7 KiB

文件存储模块设计文档

概述

为项目新增通用文件存储服务模块,支持三种可配置的存储后端(本地文件系统、阿里云 OSS、MinIO),前端提供文件 CRUD 和预览功能。

定位:通用存储服务,可用于个人文件管理、公共资源管理、新闻图片视频等多种业务场景。

设计决定

项目 决定 原因
文件归属 混合模式(公开/私有标记) 灵活适配多种业务场景
目录结构 平铺列表 + 分类标签 简单高效,适合通用存储
预览类型 图片 + 视频 + PDF 覆盖主流文件类型
存储切换 配置文件单选 简单清晰,重启生效
架构模式 Strategy Pattern 存储抽象层 干净、可扩展、易测试

后端架构

1. Storage 接口(Strategy Pattern)

// 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. 配置结构

# 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
// 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. 数据模型

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

// 新增到 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) <img> 直接显示
视频 (mp4/webm) <video> HTML5 播放器
PDF <iframe> 嵌入查看
其他 文件信息卡片 + 下载按钮

前端类型

interface FileInfo {
  id: number
  name: string
  key: string
  size: number
  mimeType: string
  category: string
  isPublic: boolean
  userId: number
  storageType: string
  url: string
  createdAt: string
  updatedAt: string
}

文件变更清单

新建文件

后端

  • backend/internal/storage/storage.go — 接口定义 + 工厂函数
  • backend/internal/storage/local.go — 本地存储实现
  • backend/internal/storage/oss.go — 阿里云 OSS 实现
  • backend/internal/storage/minio.go — MinIO 实现
  • backend/model/file_entity.go — File GORM 模型
  • backend/model/file_model.go — File 数据访问方法
  • backend/api/file.api — API 定义
  • backend/internal/handler/file/ — handlers (goctl 生成)
  • backend/internal/logic/file/ — 业务逻辑(6个)

前端

  • frontend/react-shadcn/pc/src/pages/FileManagementPage.tsx — 文件管理页面

修改文件

后端

  • backend/internal/config/config.go — 新增 StorageConfig
  • backend/etc/base-api.yaml — 新增 Storage 配置段
  • backend/internal/svc/servicecontext.go — 初始化 Storage + AutoMigrate File + 种子 Casbin
  • backend/base.api — import file.api
  • backend/internal/types/types.go — goctl 重新生成
  • backend/internal/handler/routes.go — goctl 重新生成
  • backend/go.mod / go.sum — 新增 OSS/MinIO SDK 依赖

前端

  • frontend/react-shadcn/pc/src/types/index.ts — 新增 File 类型
  • frontend/react-shadcn/pc/src/services/api.ts — 新增 file API 方法
  • frontend/react-shadcn/pc/src/App.tsx — 新增 /files 路由
  • frontend/react-shadcn/pc/src/components/layout/Sidebar.tsx — 新增导航项