生产级、极简、高可维护的 Go Web 单体式后端项目模版(Scaffold)。类似 Spring Boot Initializr —— clone 即可开始写业务代码。
基于 Gin + Bun + Redis + Zap + JWT 技术栈,内置主从分离、优雅关机、结构化日志、JWT 认证中间件。
| 组件 | 选型 | 版本 |
|---|---|---|
| 语言 | Go | 1.23+ |
| Web 框架 | Gin | v1.10 |
| ORM | Bun + pgdriver | v1.2 |
| 数据库 | PostgreSQL(仅此一种,严禁 MySQL) | 14+ |
| 缓存 | go-redis v9 | v9 |
| 日志 | Uber Zap | v1.27 |
| 认证 | golang-jwt v5 | v5 |
| 配置 | godotenv | v1.5 |
┌─────────────────────────────────────────────────────┐
│ cmd/main.go │
│ 入口:初始化 → 组装 → 启动 → 优雅关机 │
└────────────────────────┬────────────────────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ router/ │ │middleware/│ │ config/ │
│mainRouter │ │ auth.go │ │ config.go │
└─────┬─────┘ └─────┬─────┘ └───────────┘
│ │
┌─────┴──────────────┴─────┐
│ controller/ │ ← 控制层(按业务模块创建)
└─────────────┬─────────────┘
│
┌─────────────┴─────────────┐
│ service/ │ ← 业务层(纯 Go,不引用 gin.Context)
└─────────────┬─────────────┘
│
┌─────────────┴──────────────┐
│ │
┌───┴───┐ ┌────┴────┐ ┌───────┴──────┐
│ model/│ │database/ │ │ auth/ │ ← 基础设施层
│ │ │ postgres │ │ jwt.go │
│ │ │ redis │ └──────────────┘
└───────┘ └──────────┘
router → middleware → controller → service → model
→ database
→ apperrors
- middleware:不做业务逻辑,只做请求前置处理(认证/日志/限流),不访问数据库
- controller:不做业务逻辑,只绑定参数、调用 service、返回 HTTP 状态码
- service:不引用
gin.Context,只用context.Context,所有依赖通过构造函数注入 - database:只管理连接,不写业务查询
- model:Entity(Bun 标签)与 DTO(json 标签)严格分离
// 写操作 → Master
s.db.Master.NewInsert().Model(user).Exec(ctx)
// 读操作 → 随机从库(适合大多数场景)
s.db.RandomSlave().NewSelect().Model(&users).Scan(ctx)
// 读操作 → 按 key 粘性路由(适合"读己之写"场景)
s.db.SlaveByKey(userID).NewSelect().Model(&user).Scan(ctx)- 0 个从库(
POSTGRES_SLAVE_DSNS为空)→ 自动退回 Master - 1 个从库 → 直接使用
- 2+ 个从库 → 按上述策略分发
- Go 1.23+
- PostgreSQL 14+
- Redis 7+(可选,未连接时仅打印 warning)
# Mac / Linux
bash setup.sh
# Windows PowerShell
.\setup.ps1脚本会交互式询问:
- 项目存放目录(默认当前目录,不存在则自动创建)
- 项目名称(必填)
- Go 模块路径(默认
github.com/your-org/<项目名>) - 是否初始化 Git 仓库(默认 y)
自动完成:git clone → 替换模块路径 → 解除 Git 绑定 → go mod tidy。
git clone https://github.com/Go2Jv/goWebTemplate.git my-project
cd my-project
bash setup.sh # 执行交互式初始化cp .env.example .env
# 编辑 .env 填入数据库、Redis、JWT 密钥
go run ./cmd/main.go{"level":"info","timestamp":"...","msg":"application starting","app_name":"my-project","env":"production"}
{"level":"info","timestamp":"...","msg":"http server listening","addr":":8080"}
├── cmd/
│ └── main.go # 入口:配置 → DB → Redis → JWT → 路由 → 优雅关机
├── internal/
│ ├── config/
│ │ └── config.go # 强类型 Config(env 解析 + JWT)
│ ├── logger/
│ │ └── logger.go # Zap Logger + Gin 中间件
│ ├── apperrors/
│ │ └── errors.go # BizError + 错误码常量
│ ├── auth/
│ │ └── jwt.go # JWT 生成与验证
│ ├── middleware/
│ │ └── auth.go # AuthRequired + GetUserID
│ ├── database/
│ │ ├── postgres.go # 主从封装 + RandomSlave / SlaveByKey
│ │ └── redis.go # Redis 连接封装
├── internal/dto/ # 前后端数据传输对象
│ │ ├── req/ # Request DTO(json + binding 标签)
│ │ └── resp/ # Response DTO(json 标签)
│ ├── model/ # ← 业务 Entity(Bun 标签,按模块创建)
│ ├── service/ # ← 业务逻辑(按模块创建)
│ ├── controller/ # ← HTTP Handler(按模块创建)
│ └── router/
│ └── mainRouter.go # Controllers struct + 中间件 + 模块挂载
├── setup.sh # Mac / Linux 一键初始化脚本
├── setup.ps1 # Windows 一键初始化脚本
├── .env.example # 环境变量模版
├── README_AI_BACKEND.md # AI Agent 后端开发约束
└── README_AI_FRONTEND.md # 前端/客户端 API 对接规范
注意:
model/、service/、controller/目录为空 —— 这是干净的脚手架。按下方「新增业务模块」步骤添加你的第一个模块。
| 变量 | 默认值 | 说明 |
|---|---|---|
APP_NAME |
my-go-template |
应用名称 |
APP_ENV |
production |
运行环境(生产优先原则) |
APP_PORT |
8080 |
HTTP 监听端口 |
POSTGRES_MASTER_HOST |
localhost |
主库地址 |
POSTGRES_MASTER_PORT |
5432 |
主库端口 |
POSTGRES_MASTER_USER |
postgres |
主库用户 |
POSTGRES_MASTER_PASSWORD |
postgres |
主库密码 |
POSTGRES_MASTER_DATABASE |
myapp |
主库数据库名 |
POSTGRES_MASTER_SSLMODE |
disable |
SSL 模式 |
POSTGRES_SLAVE_DSNS |
(空) | 从库 DSN 列表(逗号分隔,0~N 个)。 格式: postgres://u:p@host:port/db?sslmode=disable |
REDIS_HOST |
localhost |
Redis 地址(Standalone 模式) |
REDIS_PORT |
6379 |
Redis 端口 |
REDIS_PASSWORD |
(空) | Redis 密码 |
REDIS_DB |
0 |
Redis DB 编号 |
JWT_SECRET_KEY |
change-me |
JWT 签名密钥。测试环境用默认值即可, 生产环境推荐 openssl rand -base64 32 生成 |
LOG_LEVEL |
debug |
日志级别:debug / info / warn / error |
这是一个干净的脚手架,不含任何业务代码。 按以下步骤添加你的第一个模块(详见 README_AI.md):
| 步骤 | 文件 | 做什么 |
|---|---|---|
| 1 | internal/model/{module}.go |
定义 Entity + Request/Response DTO |
| 2 | internal/service/{module}_service.go |
定义 Service,注入 *Postgres + *Redis |
| 3 | internal/controller/{module}_controller.go |
定义 Controller,方法名 {Action}Handler |
| 4 | internal/router/{module}Router.go |
定义 Register{Module}Router(rg, ctrl) |
| 5 | internal/router/mainRouter.go |
在 Controllers struct 追加字段 + 挂载路由 |
| 6 | cmd/main.go |
在 DI 块组装 Service → Controller → Controllers |
token, err := jwtMgr.GenerateToken(userID)
// 返回给客户端: {"token": token}// 在 mainRouter.go 中挂载到认证路由组
auth := v1.Group("")
auth.Use(middleware.AuthRequired(jwtMgr))
{
RegisterOrderRouter(auth, ctrls.Order)
}func (c *OrderController) ListHandler(ctx *gin.Context) {
userID, ok := middleware.GetUserID(ctx)
// ...
}优先使用请求头(
c.Request.Header.Set("X-Key", val)),仅异步 goroutine 场景使用c.Copy()。
// ✅ 同步:请求头
c.Request.Header.Set("X-User-ID", strconv.FormatInt(userID, 10))
// ✅ 异步:c.Copy() 防止 GC
copyCtx := c.Copy()
go func(ctx *gin.Context) { ... }(copyCtx)- 无全局变量 — 所有依赖通过构造函数显式注入,
init()禁止使用 - 层级单向依赖 — router → middleware → controller → service → model/database
- Entity 与 DTO 分离 — 数据库映射与 HTTP 序列化用不同的 struct
- 错误不吞没 — 每个 error 必须被处理或向上传递,使用
errors.As()判断 BizError - 只支持 PostgreSQL — 禁止引入 MySQL 驱动
- 读写分离 — 写用
db.Master,读用db.RandomSlave()或db.SlaveByKey(key) - 优雅关机 — 收到 SIGINT/SIGTERM 后 10 秒内完成在途请求
- 生产优先 — 默认
APP_ENV=production,日志默认 JSON 格式 - JWT 内置 — 开箱即用的认证中间件,请求头传递
X-User-ID
如果你是一个 AI Agent(Cursor、Claude Code、Copilot 等),在为此项目生成代码前 必须先阅读对应角色的文档:
| 文档 | 目标读者 | 内容 |
|---|---|---|
| README_AI_BACKEND.md | 后端 AI Agent | 强制检查清单、文件权限分级、防幻觉规则、新增模块步骤 |
| README_AI_FRONTEND.md | 前端开发者 / 前端 AI Agent | API 响应格式、错误码速查表、认证方式、拦截器示例 |
核心规则:生成任何代码前,先用 find / grep 确认当前项目状态。
MIT