大家好,我是 JavaPub。
最近又抽时间折腾了一下自己的开源项目 ShiyuAdmin。
项目地址:
https://github.com/Rodert/ShiyuAdmin/

ShiyuAdmin 是我自己维护的一套通用后台管理系统,目前技术栈主要是:
- Go
- Gin
- Gorm
- React
- Ant Design Pro
- RBAC
- PostgreSQL
- Redis
- Docker
最开始做它的时候,我的想法其实很简单:
以后再做管理后台,别每次都从登录、用户、角色、菜单、权限这些东西重新写一遍。
所以慢慢把用户管理、角色管理、菜单权限、部门管理、动态菜单、接口权限、操作日志、Redis 管理、系统监控、数据管理这些东西都加了进去。
但随着自己真正拿它去做项目,我越来越发现:
一个后台框架想真正"拿来就用",光有 RBAC 还远远不够。
所以最近又集中更新了一波。
这次我重点解决了几个我自己在实际项目里经常遇到的问题。
一、终于加入了完整的文件管理
这次比较大的一个更新,就是给 ShiyuAdmin 加了一套真正的 文件管理模块。
这不是简单写一个:
text
POST /upload
然后把文件扔到某个目录里就结束了。
我希望从一开始就把文件当成一个独立的基础设施来设计。
所以这次新增了两个核心模型:
text
StorageConfig
MediaFile
也就是:
text
存储配置
↓
文件元数据
↓
实际存储对象
文件本身和存储后端是分开的。
目前默认提供本地存储,初始化时会自动创建:
text
本地存储
Driver: local
BasePath: ./data/uploads
但数据模型里已经预留了:
text
local
s3
oss
以及:
text
Endpoint
Bucket
PublicURL
AccessKey
SecretKey
这些字段。
这意味着后面要接:
text
AWS S3
Cloudflare R2
阿里云 OSS
腾讯云 COS
MinIO
实际上就比较顺了。
我不太喜欢那种一开始把文件上传路径硬编码死,后面要上对象存储的时候再推倒重来的做法。
既然是通用后台,就应该尽量把扩展能力提前留出来。

二、文件不只是上传,还要能真正"管理"
有了存储还不够。
后台里现在已经加入了完整的文件列表和管理能力。
包括:
text
文件列表
文件搜索
分页
上传
下载
删除
回收站
恢复
而且这里的删除并不是直接物理删除。
我用了软删除的方式,把删除后的文件放进"回收站"。
对应接口也拆开了:
text
GET /files
GET /files/recycle-bin
POST /files/upload
GET /files/:code/download
DELETE /files/:code
POST /files/:code/restore
这样用户误删文件以后,还有机会恢复。
文件记录里面也不只是保存一个路径,而是记录了:
text
FileCode
StorageID
OriginalName
ObjectKey
MimeType
Size
SHA256
AccessLevel
UploaderCode
比如 SHA256 后面可以继续做:
text
文件校验
重复文件判断
内容寻址
安全扫描
这些东西。
这也是我现在做 ShiyuAdmin 一个比较明确的思路:
不只是把页面做出来,而是尽量把以后真正做业务需要的扩展空间留出来。
三、文件权限也接进了 RBAC
既然 ShiyuAdmin 本身就是一个 RBAC 后台,那么文件系统肯定不能自己另搞一套权限。
所以这次直接把文件管理接进了原来的权限体系。
目前主要拆成:
text
system:file:list
system:file:upload
system:file:delete
也就是说可以做到:
text
A 角色只能查看文件
B 角色可以查看 + 上传
C 角色拥有查看 + 上传 + 删除权限
而不是只要登录后台,就能随便上传和删除。
很多后台系统一开始文件上传只是一个辅助功能。
但随着业务越来越复杂,合同、图片、Excel、PDF、附件、视频全部往里面塞以后,文件本身其实已经变成非常重要的一类业务数据。
这时候权限就不能再靠前端"隐藏一个按钮"来解决了。
接口权限必须一起做。
四、单文件上传直接放到了 2 GiB
这个更新其实还有一个挺有意思的小坑。
文件模块刚做完的时候,应用层虽然支持大文件,但部署以后发现:
Nginx 先把请求拦了。
所以后面又专门提交了一次修复:
text
fix: allow 2GiB multipart uploads
在 Nginx 层增加了:
nginx
client_max_body_size 2050m;
为什么不是刚好 2048m?
因为 multipart/form-data 本身还有 boundary 等额外数据,需要留一点空间。
后端本身也做了大小限制:
go
const maxUploadSize int64 = 2 << 30
并通过 MaxBytesReader 控制请求大小。
所以现在单文件最大可以做到:
text
2 GiB
图片、PDF、压缩包,甚至一些视频文件都基本够用了。
这个地方也算一个挺典型的工程问题:
一个功能"代码支持",不代表真正部署出去以后就支持。
中间可能还有:
text
浏览器
CDN
Cloudflare
Nginx
网关
后端框架
应用服务器
对象存储
任何一层限制都可能导致上传失败。
五、私有文件终于可以在线预览了
文件管理做好以后,我很快又遇到一个问题。
如果文件是私有的,直接把 URL 塞给浏览器并不合适。
因为它需要经过登录鉴权和 RBAC 权限校验。
于是又加了一次提交:
text
feat: preview private media files
后端新增:
text
GET /files/:code/preview
并且仍然要求:
text
system:file:list
权限。
前端不会直接暴露一个裸文件地址,而是带着 Bearer Token 请求文件,然后拿到 Blob,再创建临时 URL 用来展示。
目前支持的在线预览包括:
text
图片
PDF
文本文件
图片直接展示;
PDF 和文本可以通过 iframe 预览;
其他暂时不支持的格式则提示用户下载查看。
这个设计我个人还是比较喜欢的。
因为:
text
登录鉴权
↓
RBAC 权限验证
↓
读取文件
↓
返回 Blob
↓
浏览器临时预览
整个链路仍然在后台的权限控制范围内。
而不是为了图方便,把原本应该是私有的文件变成公开 URL。

六、另外一个小功能:主题偏好
这一波更新里还顺手完善了后台的主题偏好。
这个功能相比文件管理当然不算大,但对于一个每天都要打开的后台来说,体验还是挺重要的。
后台系统和普通网站不太一样。
很多运营、管理员、开发人员可能一天要盯着它几个小时。
所以类似:
text
主题
布局
界面偏好
这些东西我后面也会继续完善。
这类功能单独拿出来没什么惊艳的,但一个成熟后台往往就是由这些"小东西"一点点堆出来的。
七、这次我更在意的,其实是 Docker 部署
除了业务功能,这次我还重新整理了一遍整个项目的 Docker 部署方式。
这部分我觉得反而非常重要。
以前很多开源后台都是:
bash
git clone
然后:
bash
npm install
npm run build
go build
docker build
服务器自己从源码开始折腾。
这种方式开发阶段没问题。
但是部署多了以后真的很烦。
所以这次我直接做了一套 统一应用镜像。
八、前端 + 后端打成一个镜像
现在根目录增加了一个多阶段 Dockerfile。
整个构建过程大致是:
text
Node 20
↓
构建 React 前端
Go 1.23
↓
编译 Go Backend
Nginx Alpine
↓
放入前端静态文件
Supervisor
↓
同时管理 Nginx + Go API
也就是说:
text
React
+
Go API
+
Nginx
+
Supervisor
最终全部变成:
text
一个 Docker Image
而不是服务器上跑一堆分散的东西。
这样以后部署的时候简单很多。
九、GitHub Actions 自动构建 GHCR 镜像
这个是我这次比较喜欢的一个改动。
现在每次 Push 代码,GitHub Actions 都可以自动:
text
Checkout
↓
Docker Build
↓
生成镜像
↓
Push 到 GHCR
也就是:
text
GitHub Container Registry
对应镜像:
text
ghcr.io/<owner>/<repo>
Action 会自动生成多种标签,包括:
text
branch
tag
sha-xxx
YYYYMMDD-HHmmss
latest
其中默认分支会额外维护 latest。
这样服务器以后根本不用再编译项目。
只需要:
bash
docker compose pull
docker compose up -d --no-build
就行了。
整个发布流程变成:
text
我 Push GitHub
↓
GitHub Actions
↓
构建 ShiyuAdmin Image
↓
GHCR
↓
服务器 docker pull
↓
启动
这才是我比较喜欢的开源项目部署方式。

十、为什么我要把本地 Compose 和线上部署拆开?
另外我还专门做了一次:
text
chore: separate local compose and disable auto fly deploy
把:
text
docker-compose.local.yml
和正式部署配置区分开。
同时 Fly.io 不再跟着每次 main push 自动部署,而是改成手动触发。
原因也很简单。
以前一个仓库同时承担:
text
开发环境
测试环境
演示环境
线上环境
镜像发布
很容易越做越乱。
现在我的思路逐渐变成:
text
源码
↓
CI Build
↓
标准 Docker Image
↓
不同环境自己决定怎么运行
GHCR 负责"发布镜像"。
Fly.io、自己的服务器、云服务器等则只是不同的"运行环境"。
这样两件事情就解耦了。
我认为这比"GitHub 一 Push 就自动把某台服务器更新掉"更加适合作为一个公共开源项目的默认方案。
十一、现在的 ShiyuAdmin 到什么程度了?
如果把最近几个月加进去的东西放到一起,现在基本已经有:
text
用户管理
角色管理
菜单管理
部门管理
RBAC 权限
动态菜单
接口权限
数据权限
个人中心
登录日志
操作日志
Redis 缓存管理
数据管理
系统监控
Dashboard
访问态势
可视化图表
文件管理
文件上传
文件下载
文件预览
文件回收站
存储配置
Docker Compose
统一应用镜像
GitHub Actions
GHCR 自动发布
项目本身采用前后端分离架构,后端是 Go + Gin + Gorm,前端是 React + Umi Max + Ant Design Pro;README 目前也已经把它定位成适合快速搭建中后台、学习 RBAC 或作为新业务基础脚手架的通用后台系统。
所以现在我自己也不太愿意把它单纯定义成一个:
"Go 后台管理系统 Demo"。
我更希望它慢慢变成一个:
真正能拿来作为新项目基础工程的后台底座。
十二、接下来还会继续加什么?
文件系统现在只是第一版。
从目前的数据结构其实已经能看出来,我后面比较想继续做:
text
Cloudflare R2
AWS S3
MinIO
阿里云 OSS
腾讯云 COS
这类对象存储。
然后继续完善:
text
文件分类
目录
批量操作
图片缩略图
视频预览
文件引用关系
存储空间统计
上传进度
分片上传
断点续传
另外还有一个方向,是继续降低部署成本。
理想状态就是:
bash
docker compose up -d
甚至:
bash
docker run ...
就能得到一整套可以直接开发业务的后台。
最后
我一直觉得:
自己维护一个开源项目,最有意思的地方其实并不是一次性写出多少代码。
而是你真的拿它去做项目以后,会不断发现:
text
这里还不够方便
那里不够安全
这个地方不好部署
那个功能缺了一块
然后一点一点补上。
ShiyuAdmin 也是这样。
它不是某一天突然写出来的,而是在我自己的实际开发过程中,一点点长出来的。
如果你最近正好在学:
text
Go
Gin
Gorm
React
Ant Design Pro
RBAC
Docker
GitHub Actions
或者正准备做一个自己的后台管理系统,可以拿去参考或者直接改。
项目完全开源。
GitHub:
https://github.com/Rodert/ShiyuAdmin/
觉得项目还不错的话,也欢迎给个 Star。
有 Bug、功能建议,也欢迎直接提 Issue。
我还会继续更新。
一起成为勇猛精进的人类。