gin-layout

July 2, 2026 · View on GitHub

中文 | English

基于 Gin 的后台管理系统脚手架
内置 JWT 双 Token 认证、RBAC 权限、请求/登录日志、文件上传、任务中心、系统通知、readiness 探针、参数校验、请求语言国际化、声明式路由和 CLI 命令体系。

go codeql go report card license go version

项目定位

很多后台项目一开始都只是想“先把登录、权限、菜单、上传和日志跑起来”,但真正进入开发后,通常会很快碰到这些重复问题:

  • 认证、权限、日志和文件能力分散,初始化成本高
  • 路由、菜单、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支持部分配置热更新,失败时保留旧实例继续运行

相关资源

快速开始

1. 环境要求

  • Go >= 1.23
  • MySQL >= 5.7
  • Redis >= 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_ttljwt.refresh_cookie_*
  • 如果只启动 API、不启用异步任务,可以把 queue.enable 设为 false
  • 如果 queue.enable=true 但不想复用 redis.*,请把 queue.use_default_redis 设为 false,并补齐 queue.redis.*
  • 文件存储驱动和敏感存储参数运行时保存在 sys_configstorage.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.goAdminRouteTree()。Gin 路由注册和 API 元数据初始化都从这棵树生成,避免“代码路由”和“权限路由”长期分叉。

数据库关系是权限真相

当前权限模型采用“数据库关系为真相,Casbin 负责最终接口判定”的方式。角色、部门、菜单和 API 的业务关系以数据库为准,rebuild-user-permissions 命令会按这些关系重建用户最终 API 权限。

配置热更新是分级的

项目支持配置热更新,但不是所有配置都会在运行中立即生效。支持热更新的资源会尝试重建;如果重建失败,会继续保留旧实例运行,避免把服务直接打挂。

请求语言驱动文案

项目会在请求链路读取 Accept-Language(当前支持 zh-CN / en-US),并做归一化与降级处理:

  • 错误码文案:按请求语言返回;无法识别时降级到默认语言(zh-CN)。
  • 菜单列表/用户菜单树:仅返回 title,由后端按请求语言解析后返回。
  • 菜单详情:返回 title_i18n(用于前端编辑回填),不返回 title
  • 菜单新增/编辑:写接口使用 title_i18n,目前支持 zh-CNen-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-routerebuild-user-permissions 默认会二次确认;自动化场景可以显式加 -y
  • init-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 检查任务是否已在表中。同步只更新代码定义字段,会保留线上 statuscron_spec、是否允许手动触发、是否允许重试和记录策略等运营配置。

配置说明

配置查找顺序

配置文件查找顺序:

  1. 显式传入 -c / --config
  2. GO_ENV=development 时,使用当前工作目录的 config.yaml
  3. 若第 2 步缺失,则尝试从当前工作目录的 config/config.yaml.example 复制生成
  4. 非开发模式下,使用可执行文件所在目录的 config.yaml
  5. 若第 4 步缺失,则尝试从可执行文件同级的 config.yaml.example 复制生成

主要配置项

配置项说明
app.base_path日志、上传文件等本地路径的基础目录;未配置时默认按 GO_ENV 选择(development=当前工作目录,其他=可执行文件目录)
app.allow_degraded_startupservice 命令生效;依赖初始化失败时允许 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_redistrue 复用 redis.*false 时改用 queue.redis.* 独立连接
queueAsynq 异步任务开关、队列命名空间、并发度、优先级和审计日志重试策略
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 默认关闭,启用执行时会输出高风险告警。
  • 不要把同一个周期任务同时注册到 cronworker 体系里,否则会重复执行。

注意:reset-system-data 当前调用的是 system.ReinitializeSystemData(),会清空数据库对象并按当前迁移重建系统数据。生产环境必须保持该任务在 task_definitions.status=0,只有在明确需要重建系统数据时才临时启用并及时关闭。

热更新

启用方式:

app:
  watch_config: true

支持热更新:

  • logger.*
  • mysql.*
  • redis.*
  • app.base_url
  • app.cors_*
  • jwt.*jwt.secret_key 变更会被检测,但实际生效仍需重启)

仅检测并提示“需要重启”:

  • app.trusted_proxies
  • app.language
  • app.allow_degraded_startup
  • jwt.secret_key
  • 服务监听地址与端口
  • 路由结构

说明:

  • watch_config=true 只表示启用监听,不代表所有配置都能无损切换
  • MySQL、Redis、Casbin 会按新配置重建实例,失败时保留旧实例
  • JWT 密钥当前不支持热更新,修改后会记录告警并继续使用旧密钥,直到进程重启

开发指南

新增接口流程

  1. internal/controller/ 编写控制器
  2. internal/service/ 编写业务逻辑
  3. internal/validator/form/ 定义请求参数
  4. AdminRouteTree() 中声明路由
  5. 需要更新 API 路由表时执行 go run main.go -c ./config.yaml command api-route

参数校验约定

  • 枚举字段(如 statusis_authmethod)建议使用 oneof 显式约束。
  • ID 数组字段(如 role_idsmenu_listapi_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。

免责声明

本项目按 “现状”提供,不附带任何明示或默示担保。项目可能存在缺陷、安全漏洞或与特定业务场景不匹配的实现;上线前请自行完成代码审查、安全加固、配置审查、权限验收和数据备份。因使用、依赖、部署、改造或运维本项目导致的问题,由使用者自行承担。