100 lines
3.7 KiB
Markdown
100 lines
3.7 KiB
Markdown
# 3.6 控制器开发
|
||
|
||
:::tip 概述
|
||
控制器是 HTTP 薄层,只做一件事:把解析好的请求转发给 service 接口。不写业务、不操作数据库、不组装复杂响应——所有逻辑在 logic 层。这个"薄"是有意为之:让权限、日志、参数解析统一收敛到 Bind 包装层。
|
||
:::
|
||
|
||
## 真实代码(摘自 internal/controller/example.go)
|
||
|
||
```go
|
||
package controller
|
||
|
||
import (
|
||
"context"
|
||
|
||
"easygoadmin/api/v1"
|
||
"easygoadmin/internal/service"
|
||
)
|
||
|
||
// ============================== 案例演示控制器 ==============================
|
||
|
||
// CExample 案例演示控制器
|
||
// 薄层控制器,仅负责参数透传,业务逻辑由 service.Example() 处理
|
||
type CExample struct{}
|
||
|
||
// Page 案例演示分页
|
||
// 按查询条件分页返回案例演示列表
|
||
func (c *CExample) Page(ctx context.Context, req *v1.ExamplePageReq) (res *v1.ExamplePageRes, err error) {
|
||
return service.Example().Page(ctx, req)
|
||
}
|
||
|
||
// List 案例演示平铺列表
|
||
// data 直接为数组,树形结构由前端 buildTree 构建
|
||
func (c *CExample) List(ctx context.Context, req *v1.ExampleListReq) (res v1.ExampleListRes, err error) {
|
||
return service.Example().List(ctx, req)
|
||
}
|
||
|
||
// Detail 案例演示详情
|
||
// 按主键ID查询详情
|
||
func (c *CExample) Detail(ctx context.Context, req *v1.ExampleDetailReq) (res *v1.ExampleDetailRes, err error) {
|
||
return service.Example().Detail(ctx, req)
|
||
}
|
||
|
||
// Add 新增案例演示
|
||
// 新增成功后返回新记录ID
|
||
func (c *CExample) Add(ctx context.Context, req *v1.ExampleAddReq) (res *v1.ExampleIdRes, err error) {
|
||
return service.Example().Add(ctx, req)
|
||
}
|
||
|
||
// Update 修改案例演示
|
||
// 按主键ID更新记录
|
||
func (c *CExample) Update(ctx context.Context, req *v1.ExampleUpdateReq) (res *v1.ExampleUpdateRes, err error) {
|
||
return service.Example().Update(ctx, req)
|
||
}
|
||
|
||
// Status 案例演示状态
|
||
// 设置记录的状态字段
|
||
func (c *CExample) Status(ctx context.Context, req *v1.ExampleStatusReq) (res *v1.ExampleStatusRes, err error) {
|
||
return service.Example().Status(ctx, req)
|
||
}
|
||
```
|
||
|
||
## 方法签名约定
|
||
|
||
```go
|
||
func (c *CXxx) 方法名(ctx context.Context, req *v1.XxxReq) (res *v1.XxxRes, err error)
|
||
```
|
||
|
||
这是 Bind 反射绑定的硬约束:**入参必须是指针请求结构体,出参必须是(结果指针, error)**。签名不匹配的方法不会被注册路由(静默跳过),排障时注意。
|
||
|
||
## 命名规则
|
||
|
||
| 项 | 规则 | 示例 |
|
||
|----|------|------|
|
||
| 控制器类型 | `C` + 模块 Pascal | `CExample`、`CConfigItem` |
|
||
| 文件名 | 表名去前缀 snake_case | `example.go`、`config_item.go` |
|
||
| 方法名 | 与 logic/接口方法名一致 | `Page`/`Add`/`BatchDelete` |
|
||
| 包 | 单包 `controller`,平铺放置 | 无子目录 |
|
||
|
||
## 控制器里禁止做的事
|
||
|
||
::: warning 薄层纪律
|
||
1. **禁止写业务逻辑**——if/for/计算全部下沉 logic。
|
||
2. **禁止直接 import dao**——数据访问只在 logic。
|
||
3. **禁止手写响应信封**——Bind 包装层统一 `response.Ok/Fail` 包装。
|
||
4. **禁止手写参数解析/权限判断**——`r.Parse` 与 `HasPerm` 在包装层完成。
|
||
5. 需要组合多个 service 调用时,逻辑放 logic 层的一个聚合方法,controller 仍只透传。
|
||
:::
|
||
|
||
## 特殊控制器参考
|
||
|
||
| 控制器 | 特点 |
|
||
|--------|------|
|
||
| `CAuth` | 登录/登出/验证码/令牌,gmeta 带 `public:"true"` |
|
||
| `CIndex` | 个人中心聚合(getUser/getMenus/updatePassword/updateProfile) |
|
||
| `COnlineUser` | 无表模块,数据源 Redis |
|
||
| `CUpload` | multipart 上传(`*ghttp.UploadFile` + `p:"file"` 标签) |
|
||
| `CGenerator` | 工具类,Preview/Generate/BatchGenerate |
|
||
|
||
控制器不区分"标准/特殊"写法——统一薄层透传,差异全部体现在请求结构体的 gmeta 注解上。
|