gin-layout
July 2, 2026 · View on GitHub
项目定位
很多后台项目一开始都只是想“先把登录、权限、菜单、上传和日志跑起来”,但真正进入开发后,通常会很快碰到这些重复问题:
- 认证、权限、日志和文件能力分散,初始化成本高
- 路由、菜单、API 权限关系容易逐步失控
- 不同项目里同一套后台基础设施被反复重写
- 配置、命令、迁移和部署流程缺少统一约定
gin-layout 的目标很明确:把后台管理场景里高频、重复、工程化要求高的基础能力沉淀成一套可以直接落地的后端骨架。
核心特性
| 能力 | 说明 |
|---|---|
| Auth | 内置 JWT 双 Token 登录、Access Token 校验、Refresh Token Cookie 刷新、黑名单 |
| RBAC | 管理员、角色、部门、菜单、API 权限管理 |
| Route Metadata | 声明式路由树统一生成 Gin 路由和 API 元数据 |
| Logs | 内置登录日志、请求日志、统一响应结构 |
| File Access | 文件上传与公开 / 私有文件访问 |
| Task Center | 内置异步任务、定时任务定义、运行记录、重试与运营配置 |
| Realtime | 系统通知、WebSocket 临时票据与实时消息连接 |
| I18n | 基于 Accept-Language 返回错误文案与菜单标题(支持 zh-CN / en-US) |
| Health Probes | 提供 /ping 与 /health/readiness,便于存活检测与依赖就绪检查 |
| Tooling | 提供 CLI 初始化、路由同步、权限重建、迁移配套能力 |
| Hot Reload | 支持部分配置热更新,失败时保留旧实例继续运行 |
相关资源
- 前端项目:go-admin-ui
- 在线文档:Apifox
- 演示地址:在线演示
- 命令与任务文档:docs/COMMANDS_AND_TASKS.md
- 迁移命令详解:docs/MIGRATE_COMMANDS.md
快速开始
1. 环境要求
Go >= 1.23MySQL >= 5.7Redis >= 5.0(可选)
2. 安装项目
git clone https://github.com/wannanbigpig/gin-layout.git
cd gin-layout
go mod download
3. 执行迁移
推荐使用项目命令:
go run main.go -c ./config.yaml command migrate # 默认等价于 migrate up
go run main.go -c ./config.yaml command migrate check
迁移执行完成后会写入一套默认基础数据,其中包含超级管理员账号 super_admin / 123456。仅建议用于本地初始化,首次登录后请立即修改密码。
迁移文件创建、时间戳命名规范、down/goto/force/version 等详细说明见:docs/MIGRATE_COMMANDS.md。
4. 配置项目
源码运行时建议带上 GO_ENV=development。未显式传入 -c 时:
- 开发模式会把当前工作目录下的
config/config.yaml.example自动复制为项目根目录config.yaml - 非开发模式会在可执行文件同级查找
config.yaml,若不存在则尝试从同目录config.yaml.example复制
也可以手动复制配置文件后再修改。
最小配置示例:
app:
app_env: local
debug: true
language: zh_CN
trusted_proxies:
- 127.0.0.1
watch_config: false
# allow_degraded_startup: false
jwt:
ttl: 1800
refresh_ttl: 0
refresh_token_ttl: 604800
refresh_cookie_name: refresh_token
refresh_cookie_path: /admin/v1/auth
refresh_cookie_same_site: Lax
refresh_cookie_secure: false
secret_key: change-me-to-a-random-secret
mysql:
enable: true
host: 127.0.0.1
port: 3306
database: go_layout
username: root
password: your_password
redis:
enable: true
host: 127.0.0.1
port: 6379
password: ""
database: 0
queue:
enable: true
use_default_redis: true
namespace: go_layout
concurrency: 8
strict_priority: false
queues:
critical: 4
default: 2
audit: 2
export: 2
low: 1
audit_max_retry: 3
audit_timeout_seconds: 10
注意:
jwt.secret_key为必填项,不能为空jwt.refresh_ttl已废弃,当前双 Token 方案实际使用jwt.refresh_token_ttl和jwt.refresh_cookie_*- 如果只启动 API、不启用异步任务,可以把
queue.enable设为false - 如果
queue.enable=true但不想复用redis.*,请把queue.use_default_redis设为false,并补齐queue.redis.* - 文件存储驱动和敏感存储参数运行时保存在
sys_config(storage.active_driver/storage.config),后台通过/admin/v1/system/storage/*管理;config.yaml主要负责基础依赖与进程级配置
5. 启动服务
GO_ENV=development go run main.go -c ./config.yaml service
需要显式指定监听地址或端口时:
GO_ENV=development go run main.go -c ./config.yaml service -H 127.0.0.1 -P 9001
如果启用了 queue.enable=true,还需要单独启动 worker:
GO_ENV=development go run main.go -c ./config.yaml worker
本地开发如需热更新,项目已提供 Air 配置:
go install github.com/air-verse/air@latest
make dev-service
如果启用了 queue.enable=true,worker 也可以单独使用热更新:
make dev-worker
注意:
make dev-service使用根目录的.air.toml启动 API 服务热更新。make dev-service/make dev-worker都会显式传入-c ./config.yaml,请先确保项目根目录已存在config.yaml。make dev-worker使用.air.worker.toml启动 worker 热更新,二进制和构建日志与 service 隔离,避免互相覆盖。- 不要在运行
make dev-worker的同时,再手动执行go run main.go worker或启动其他 worker 进程,否则会出现多个 worker 并发消费同一队列。 - worker 热更新会重启进程,执行中的任务可能被队列机制重试一次;这是开发环境的预期行为。
6. 验证服务
curl http://127.0.0.1:9001/ping
curl http://127.0.0.1:9001/health/readiness
/ping返回pong,说明 HTTP 进程已正常启动/health/readiness返回ready=true,说明当前配置下需要的依赖已经就绪
设计思路
声明式路由优先
后台路由维护在一棵声明式路由树中,目前入口位于 internal/routers/admin_router.go 的 AdminRouteTree()。Gin 路由注册和 API 元数据初始化都从这棵树生成,避免“代码路由”和“权限路由”长期分叉。
数据库关系是权限真相
当前权限模型采用“数据库关系为真相,Casbin 负责最终接口判定”的方式。角色、部门、菜单和 API 的业务关系以数据库为准,rebuild-user-permissions 命令会按这些关系重建用户最终 API 权限。
配置热更新是分级的
项目支持配置热更新,但不是所有配置都会在运行中立即生效。支持热更新的资源会尝试重建;如果重建失败,会继续保留旧实例运行,避免把服务直接打挂。
请求语言驱动文案
项目会在请求链路读取 Accept-Language(当前支持 zh-CN / en-US),并做归一化与降级处理:
- 错误码文案:按请求语言返回;无法识别时降级到默认语言(
zh-CN)。 - 菜单列表/用户菜单树:仅返回
title,由后端按请求语言解析后返回。 - 菜单详情:返回
title_i18n(用于前端编辑回填),不返回title。 - 菜单新增/编辑:写接口使用
title_i18n,目前支持zh-CN与en-US,两者至少一种非空。
常用命令
查看帮助:
go run main.go -h
go run main.go command -h
go run main.go service --help
常用命令:
| 命令 | 说明 |
|---|---|
go run main.go version | 输出当前版本号 |
go run main.go -c ./config.yaml service | 启动 API 服务 |
go run main.go -c ./config.yaml service -H 0.0.0.0 -P 9001 | 显式指定监听地址与端口 |
go run main.go -c ./config.yaml worker | 启动 Asynq 异步任务消费进程 |
go run main.go -c ./config.yaml cron | 启动定时任务 |
go run main.go command demo | 运行示例命令 |
go run main.go -c ./config.yaml command gen --from=gen/specs/product.yaml | 从表结构或 YAML 生成 CRUD 代码 |
go run main.go -c ./config.yaml command api-route -y | 扫描声明式路由树并重建 api 路由表 |
go run main.go -c ./config.yaml command rebuild-user-permissions -y | 按数据库关系重建用户最终 API 权限 |
go run main.go -c ./config.yaml command init-system | 清空当前配置数据库对象并重建系统数据 |
go run main.go -c ./config.yaml command task sync-definitions | 非破坏性同步内置任务定义到 task_definitions |
go run main.go -c ./config.yaml command task scan-async | 扫描异步任务注册与任务定义镜像 |
go run main.go -c ./config.yaml command task scan-cron | 扫描定时任务定义与任务定义镜像 |
go run main.go -c ./config.yaml command migrate check | 校验迁移文件命名与 up/down 配对 |
go run main.go -c ./config.yaml command migrate up | 执行全部未应用迁移 |
go run main.go -c ./config.yaml command migrate down 1 | 回滚 1 个迁移版本 |
如果配置文件不在默认位置,可以显式指定:
go run main.go -c /path/to/config.yaml service
go run main.go -c /path/to/config.yaml command init-system
迁移命令完整参数见:docs/MIGRATE_COMMANDS.md。
补充说明:
api-route、rebuild-user-permissions默认会二次确认;自动化场景可以显式加-yinit-system是高危重置命令,会清空当前配置数据库对象并重建系统数据;执行前会用红色提示当前环境、MySQL host、port、database、username、table_prefix,并要求确认已备份和输入当前库名init-system -y只跳过普通确认,不能单独跳过高危确认;自动化场景必须同时传入--backup-confirmed --confirm-database <mysql.database>,例如:
go run main.go -c ./config.yaml command init-system -y --backup-confirmed --confirm-database go_layout_test
- 新增内置异步任务或定时任务上线后,不要执行
init-system;使用command task sync-definitions同步任务定义,再用scan-async/scan-cron检查任务是否已在表中。同步只更新代码定义字段,会保留线上status、cron_spec、是否允许手动触发、是否允许重试和记录策略等运营配置。
配置说明
配置查找顺序
配置文件查找顺序:
- 显式传入
-c/--config GO_ENV=development时,使用当前工作目录的config.yaml- 若第 2 步缺失,则尝试从当前工作目录的
config/config.yaml.example复制生成 - 非开发模式下,使用可执行文件所在目录的
config.yaml - 若第 4 步缺失,则尝试从可执行文件同级的
config.yaml.example复制生成
主要配置项
| 配置项 | 说明 |
|---|---|
app.base_path | 日志、上传文件等本地路径的基础目录;未配置时默认按 GO_ENV 选择(development=当前工作目录,其他=可执行文件目录) |
app.allow_degraded_startup | 仅 service 命令生效;依赖初始化失败时允许 HTTP 服务先启动,并通过 readiness / 路由守卫暴露未就绪状态 |
app.base_url | 文件访问 URL 前缀,用于生成公开文件地址 |
app.trusted_proxies | 受信任代理地址或网段,影响 ClientIP() 与日志 IP |
jwt.secret_key | 必填;生产环境不能使用弱占位值 |
jwt.ttl / jwt.refresh_token_ttl / jwt.refresh_cookie_* | Access Token 生命周期、Refresh Token Cookie 生命周期与浏览器策略;jwt.refresh_ttl 已废弃 |
mysql | 数据库开关与连接信息 |
redis | 缓存、黑名单和分布式锁配置 |
queue.use_default_redis | true 复用 redis.*;false 时改用 queue.redis.* 独立连接 |
queue | Asynq 异步任务开关、队列命名空间、并发度、优先级和审计日志重试策略 |
storage.active_driver / storage.config | 运行时文件存储驱动与敏感配置,保存在 sys_config,由 /admin/v1/system/storage/* 管理 |
logger | 日志输出、切割和保留策略 |
如果通过 Nginx、Ingress 或负载均衡转发请求,需要同步配置 app.trusted_proxies,否则客户端 IP 可能记录不准确。
Worker 与 Cron
service负责提供 HTTP API。worker负责消费 Asynq 异步任务,当前内置了请求审计日志落库、请求日志导出、文件资源导出 3 类任务。queue.enable=false时,不需要启动worker,请求审计日志会在当前请求链路同步落库,但导出类异步能力也会随之不可用。cron负责定时任务调度;代码内置任务会同步到task_definitions,后台运营配置中的status/cron_spec/allow_manual/allow_retry会作为运行时真相,并通过进程缓存和cron_task_states维护最近同步/执行状态。- 演示定时任务初始化默认关闭;
reset-system-data默认关闭,启用执行时会输出高风险告警。 - 不要把同一个周期任务同时注册到
cron和worker体系里,否则会重复执行。
注意:reset-system-data 当前调用的是 system.ReinitializeSystemData(),会清空数据库对象并按当前迁移重建系统数据。生产环境必须保持该任务在 task_definitions.status=0,只有在明确需要重建系统数据时才临时启用并及时关闭。
热更新
启用方式:
app:
watch_config: true
支持热更新:
logger.*mysql.*redis.*app.base_urlapp.cors_*jwt.*(jwt.secret_key变更会被检测,但实际生效仍需重启)
仅检测并提示“需要重启”:
app.trusted_proxiesapp.languageapp.allow_degraded_startupjwt.secret_key- 服务监听地址与端口
- 路由结构
说明:
watch_config=true只表示启用监听,不代表所有配置都能无损切换- MySQL、Redis、Casbin 会按新配置重建实例,失败时保留旧实例
- JWT 密钥当前不支持热更新,修改后会记录告警并继续使用旧密钥,直到进程重启
开发指南
新增接口流程
- 在
internal/controller/编写控制器 - 在
internal/service/编写业务逻辑 - 在
internal/validator/form/定义请求参数 - 在
AdminRouteTree()中声明路由 - 需要更新 API 路由表时执行
go run main.go -c ./config.yaml command api-route
参数校验约定
- 枚举字段(如
status、is_auth、method)建议使用oneof显式约束。 - ID 数组字段(如
role_ids、menu_list、api_list)建议统一使用dive,gt=0,避免无效 ID(如0)进入业务层。 - 新增/修改校验规则时,建议同时补充正反例单测,避免后续回归。
测试
go test ./...
go test ./tests/admin_test
测试会优先使用项目根目录 config.yaml。如果当前环境的 MySQL 或 Redis 不可用,会自动回退到示例配置运行可脱离外部依赖的测试。
部署说明
构建
go build -o go-layout main.go
./go-layout service
如果没有显式传 -c,请在开发环境使用 GO_ENV=development,并确保当前工作目录存在 config/config.yaml.example(或已生成 config.yaml);部署二进制时请保证可执行文件同级存在配置文件。
Supervisor
[program:go-layout]
command=/path/to/go-layout -c /path/to/config.yaml service
directory=/path/to/go-layout
autostart=true
autorestart=true
startsecs=5
user=www-data
redirect_stderr=true
stdout_logfile=/path/to/go-layout/supervisord.log
Nginx
server {
listen 80;
server_name api.example.com;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:9001;
}
}
如果前面有反向代理,请把代理地址或网段加入 app.trusted_proxies。
目录结构
gin-layout/
├── cmd/ # 命令行入口
├── config/ # 配置结构与示例配置
├── data/ # MySQL / Redis 与迁移
├── docs/ # 补充文档与资源
├── internal/
│ ├── access/ # 权限基础设施
│ ├── controller/ # 控制器
│ ├── middleware/ # 中间件
│ ├── model/ # 数据模型
│ ├── resources/ # 资源转换
│ ├── routers/ # 声明式路由
│ ├── service/ # 业务逻辑
│ └── validator/ # 参数验证
├── pkg/ # 通用工具
├── storage/ # 文件存储
├── tests/ # 路由与集成测试
└── README.md
💝 赞助项目
感谢你使用 gin-layout。
如果这个项目对你有帮助,欢迎支持项目的持续开发与维护。
许可证
本项目采用 MIT 许可证,详见 LICENSE。
贡献
欢迎提交 Issue 和 Pull Request。
免责声明
本项目按 “现状”提供,不附带任何明示或默示担保。项目可能存在缺陷、安全漏洞或与特定业务场景不匹配的实现;上线前请自行完成代码审查、安全加固、配置审查、权限验收和数据备份。因使用、依赖、部署、改造或运维本项目导致的问题,由使用者自行承担。