📋 目录结构说明
gozero/
├── api/
│ └── user.api # API 定义文件(手动编写)
├── etc/
│ └── test-api.yaml # 配置文件(数据库、Redis等配置)
├── internal/
│ ├── config/
│ │ └── config.go # 配置结构体定义(goctl自动生成)
│ ├── handler/
│ │ ├── routes.go # 路由注册文件(goctl自动生成)
│ │ ├── loginhandler.go # 登录处理器(goctl自动生成,可编辑)
│ │ └── registerhandler.go # 注册处理器(goctl自动生成,可编辑)
│ ├── logic/
│ │ ├── loginlogic.go # 登录业务逻辑(goctl自动生成,可编辑)
│ │ └── registerlogic.go # 注册业务逻辑(goctl自动生成,可编辑)
│ ├── model/
│ │ └── usermodel.go # 数据模型和数据库操作(手动编写)
│ ├── svc/
│ │ └── servicecontext.go # 服务上下文(goctl自动生成,可编辑)
│ ├── types/
│ │ └── types.go # 请求/响应类型定义(goctl自动生成)
│ └── utils/
│ └── jwt.go # JWT工具函数(手动编写)
├── test.go # 程序入口文件(goctl自动生成,可编辑)
└── go.mod # Go模块依赖
🔄 完整流程:从 .api 文件到接口返回
第一步:从 .api 文件生成代码
文件路径: api/user.api
执行命令:
goctl api go -api api/user.api -dir . -style gozero
生成的文件:
- ✅
internal/types/types.go- 根据 .api 中的 type 定义生成 Go 结构体 - ✅
internal/handler/routes.go- 根据 @handler 注解生成路由注册代码 - ✅
internal/handler/loginhandler.go- 登录接口的处理器(骨架代码) - ✅
internal/handler/registerhandler.go- 注册接口的处理器(骨架代码) - ✅
internal/logic/loginlogic.go- 登录业务逻辑(骨架代码) - ✅
internal/logic/registerlogic.go- 注册业务逻辑(骨架代码) - ✅
internal/svc/servicecontext.go- 服务上下文(骨架代码) - ✅
internal/config/config.go- 配置结构体(骨架代码) - ✅
test.go- 程序入口文件(骨架代码)
说明:
- 所有生成的文件都标注了
// Code generated by goctl或// Code scaffolded by goctl - 标注
DO NOT EDIT的文件不要手动修改,重新生成会被覆盖 - 标注
Safe to edit的文件可以手动编辑,不会被覆盖
第二步:程序启动流程
文件路径: test.go (程序入口)
执行流程:
1. main() 函数启动
↓
2. 加载配置文件 etc/test-api.yaml
↓
3. 创建 HTTP 服务器 (rest.MustNewServer)
↓
4. 创建服务上下文 (svc.NewServiceContext)
├── 初始化 MySQL 数据库连接
├── 初始化 Redis 连接(如果配置了)
└── 初始化 UserModel(用户模型)
↓
5. 注册路由 (handler.RegisterHandlers)
↓
6. 启动服务器 (server.Start())
关键代码位置:
// ... 启动流程代码 ...
第三步:路由注册
文件路径: internal/handler/routes.go
作用: 将 API 路径与处理器函数绑定
关键代码:
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodPost,
Path: "/api/user/login",
Handler: LoginHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/api/user/register",
Handler: RegisterHandler(serverCtx),
},
},
)
}
说明:
- 当请求
POST /api/user/login时,会调用LoginHandler函数 - 当请求
POST /api/user/register时,会调用RegisterHandler函数 - 路由信息来自
api/user.api文件中的定义
第四步:请求处理流程(以登录接口为例)
4.1 Handler 层 - 请求接收和参数解析
文件路径: internal/handler/loginhandler.go
作用:
- 接收 HTTP 请求
- 解析请求参数(JSON → Go 结构体)
- 调用 Logic 层处理业务逻辑
- 返回响应结果
执行流程:
1. 客户端发送 POST 请求到 /api/user/login
↓
2. routes.go 路由匹配,调用 LoginHandler
↓
3. LoginHandler 函数执行:
├── 解析请求体 JSON → types.LoginRequest
├── 创建 LoginLogic 实例
├── 调用 l.Login(&req) 执行业务逻辑
└── 将响应结果返回给客户端
关键代码:
func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LoginRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := logic.NewLoginLogic(r.Context(), svcCtx)
resp, err := l.Login(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
说明:
httpx.Parse()将 HTTP 请求体解析为types.LoginRequest结构体types.LoginRequest定义在internal/types/types.go,由user.api自动生成- Handler 层只负责参数解析和结果返回,不包含业务逻辑
4.2 Logic 层 - 业务逻辑处理
文件路径: internal/logic/loginlogic.go
作用:
- 参数验证
- 业务逻辑处理
- 调用 Model 层进行数据查询
- 调用工具函数(如 JWT 生成)
- 返回业务结果
执行流程:
1. 接收 LoginRequest 参数
↓
2. 参数验证(用户名、密码是否为空)
↓
3. 调用 Model 层查询用户
├── l.svcCtx.UserModel.FindOneByUsername(req.Username)
└── 通过 ServiceContext 访问 UserModel
↓
4. 验证用户状态(是否被禁用)
↓
5. 验证密码(bcrypt 密码比对)
↓
6. 生成 JWT Token(调用 utils.GenerateToken)
↓
7. 构造 LoginResponse 并返回
关键代码:
func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
// 1. 参数验证
if req.Username == "" {
return &types.LoginResponse{
Code: 2001,
Message: "用户名不能为空",
}, nil
}
// ... 更多验证 ...
// 2. 查找用户
user, err := l.svcCtx.UserModel.FindOneByUsername(req.Username)
// ... 错误处理 ...
// 3. 检查用户状态
if user.Status != 1 {
return &types.LoginResponse{
Code: 2005,
Message: "用户已被禁用",
}, nil
}
// 4. 验证密码
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
// ... 错误处理 ...
// 5. 生成 JWT Token
token, err := utils.GenerateToken(user.ID, user.Username)
// ... 错误处理 ...
// 6. 返回成功响应
resp = &types.LoginResponse{
Code: 0,
Message: "登录成功",
Data: types.LoginInfo{
Token: token,
UserID: user.ID,
Username: user.Username,
},
}
return resp, nil
}
说明:
- Logic 层通过
svcCtx(ServiceContext) 访问 Model 层 svcCtx.UserModel是在servicecontext.go中初始化的- Logic 层包含所有业务逻辑,不直接操作数据库
4.3 Model 层 - 数据库操作
文件路径: internal/model/usermodel.go
作用:
- 定义数据模型结构(User 结构体)
- 提供数据库操作方法(查询、插入、更新等)
- 封装 SQL 语句执行
执行流程(登录场景):
1. Logic 层调用 l.svcCtx.UserModel.FindOneByUsername(username)
↓
2. Model 层执行 SQL 查询
├── 构建 SQL 语句:SELECT ... FROM user WHERE username = ?
├── 执行查询:m.conn.QueryRow(&user, query, username)
└── 返回 User 结构体
↓
3. Logic 层接收查询结果
关键代码:
// FindOneByUsername 根据用户名查找用户
func (m *defaultUserModel) FindOneByUsername(username string) (*User, error) {
query := `SELECT id, username, password, email, phone, status, created_at, updated_at FROM user WHERE username = ? LIMIT 1`
var user User
err := m.conn.QueryRow(&user, query, username)
if err != nil {
return nil, err
}
return &user, nil
}
说明:
- Model 层直接操作数据库,使用 go-zero 的
sqlx包 m.conn是数据库连接,在servicecontext.go中初始化- Model 层只负责数据访问,不包含业务逻辑
4.4 ServiceContext - 服务上下文
文件路径: internal/svc/servicecontext.go
作用:
- 在整个请求生命周期中共享资源
- 包含:配置、数据库连接、Redis 连接、Model 实例等
- 所有 Handler 和 Logic 都可以通过
svcCtx访问这些资源
关键代码:
type ServiceContext struct {
Config config.Config
DB sqlx.SqlConn // MySQL 数据库连接
Redis *redis.Redis // Redis 客户端
UserModel model.UserModel // 用户模型
}
func NewServiceContext(c config.Config) *ServiceContext {
// 初始化 MySQL 连接
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
c.MySQL.User,
c.MySQL.Password,
c.MySQL.Host,
c.MySQL.Port,
c.MySQL.DBName,
c.MySQL.Charset,
)
db := sqlx.NewMysql(dsn)
// 初始化用户模型
userModel := model.NewUserModel(db)
return &ServiceContext{
Config: c,
DB: db,
Redis: redisClient,
UserModel: userModel,
}
}
说明:
- ServiceContext 在程序启动时创建一次,所有请求共享
- Handler 和 Logic 通过
svcCtx参数访问共享资源 - 如果需要添加新的数据库表或外部服务,在这里添加字段
第五步:响应返回
流程:
1. Logic 层返回 LoginResponse
↓
2. Handler 层接收响应
↓
3. Handler 层调用 httpx.OkJsonCtx() 将响应序列化为 JSON
↓
4. HTTP 响应返回给客户端
响应格式:
{
"code": 0,
"message": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user_id": 1,
"username": "testuser"
}
}
📊 完整流程图
┌─────────────────────────────────────────────────────────────┐
│ 第一步:代码生成 │
│ api/user.api → goctl api go → 生成所有骨架代码 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第二步:程序启动 │
│ test.go (main) │
│ ├── 加载配置 etc/test-api.yaml │
│ ├── 创建 HTTP 服务器 │
│ ├── 创建 ServiceContext(初始化 DB、Redis、Model) │
│ └── 注册路由 handler.RegisterHandlers() │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第三步:请求到达 │
│ POST /api/user/login │
│ ↓ │
│ routes.go 路由匹配 → LoginHandler │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第四步:Handler 层 │
│ internal/handler/loginhandler.go │
│ ├── 解析请求参数 (JSON → LoginRequest) │
│ ├── 创建 LoginLogic 实例 │
│ └── 调用 l.Login(&req) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第五步:Logic 层 │
│ internal/logic/loginlogic.go │
│ ├── 参数验证 │
│ ├── 调用 Model 查询用户 │
│ │ └── svcCtx.UserModel.FindOneByUsername() │
│ ├── 验证密码 │
│ ├── 生成 JWT Token (utils.GenerateToken) │
│ └── 返回 LoginResponse │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第六步:Model 层 │
│ internal/model/usermodel.go │
│ ├── 执行 SQL 查询 │
│ │ SELECT ... FROM user WHERE username = ? │
│ └── 返回 User 结构体 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第七步:响应返回 │
│ Logic → Handler → httpx.OkJsonCtx() → JSON 响应 → 客户端 │
└─────────────────────────────────────────────────────────────┘
🔑 关键文件说明
自动生成的文件(不要手动修改)
-
internal/types/types.go- 由
user.api中的type定义自动生成 - 包含所有请求和响应的结构体
- 由
-
internal/handler/routes.go- 由
user.api中的@handler注解自动生成 - 包含路由注册代码
- 由
可编辑的文件(业务逻辑在这里)
-
internal/handler/*handler.go- 处理器层,负责参数解析和结果返回
- 通常不需要修改,除非需要添加中间件
-
internal/logic/*logic.go- 业务逻辑层,核心业务代码在这里编写
- 包含参数验证、业务处理、调用 Model 层
-
internal/model/*model.go- 数据模型层,手动编写
- 定义数据结构和数据库操作方法
-
internal/svc/servicecontext.go- 服务上下文,包含共享资源
- 需要添加新的 Model 或外部服务时在这里添加
-
internal/utils/*.go- 工具函数,手动编写
- 如 JWT 生成、加密解密等
📝 总结
请求流程:
客户端请求
→ routes.go (路由匹配)
→ handler (参数解析)
→ logic (业务逻辑)
→ model (数据库操作)
→ logic (处理结果)
→ handler (返回响应)
→ 客户端接收
各层职责:
- Handler 层:HTTP 请求/响应处理,参数解析
- Logic 层:业务逻辑处理,参数验证,调用 Model 和工具函数
- Model 层:数据库操作,SQL 执行
- ServiceContext:共享资源管理(DB、Redis、Model 等)
数据流向:
JSON 请求 → types.Request → logic 处理 → model 查询 → 数据库
↓
JSON 响应 ← types.Response ← logic 构造 ← model 返回 ← 数据库结果