3.6 KiB
3.6 KiB
Go 代码规范
文件大小
| 层次 | 单文件行数上限 | 说明 |
|---|---|---|
| Controller | ≤ 120 行 | 只管参数绑定 + 调用服务 + 返回 |
| Service | ≤ 300 行 | 承载业务逻辑,超过则拆子服务 |
| Repository | ≤ 200 行 | 纯数据库操作 |
| Model | ≤ 80 行 | 纯结构体 |
| Middleware | ≤ 60 行 | 单一职责 |
| Router | ≤ 60 行 | 只做路由映射 |
瘦控制器铁律
Controller 每个方法不超过三步:
- 绑定请求参数
- 调用 Service
- 返回响应
// ✅ 正确
func (c *PostController) Create(ctx *gin.Context) {
var req dto.CreatePostRequest
if err := ctx.ShouldBind(&req); err != nil {
common.Error(ctx, common.ErrInvalidParam, "参数错误")
return
}
post, err := c.postService.Create(ctx, req, getCurrentUser(ctx))
if err != nil {
common.Error(ctx, common.ErrInternal, "创建失败")
return
}
common.Ok(ctx, post)
}
// ❌ 错误:Controller 里写 if-else 业务分支
func (c *PostController) Create(ctx *gin.Context) {
// ... 参数绑定 ...
if req.Type == "draft" {
// 草稿逻辑写在这里 ↓ 错误!
post.Status = 0
} else {
// 发布逻辑写在这里 ↓ 错误!
post.Status = 1
if post.Score > 100 { ... }
}
}
业务判断全部放在 Service 层。
Controller 禁止事项
- ❌ 直接调用 Repository
- ❌ 调用另一个 Controller
- ❌ 写 if-else 业务分支
- ❌ 在 handler 里直接操作数据库
- ❌ 代码回滚、重试等编排逻辑
命名规范
文件
- Go 文件:
snake_case.go - 模板文件:
snake_case.html - CSS/JS 文件:
kebab-case.css/camelCase.js
结构体与方法
- Controller:
type PostController struct{},方法func (c *PostController) Create(...) - Service:
type PostService struct{ repo *PostRepo },方法func (s *PostService) Create(...) - Repository:
type PostRepo struct{ db *gorm.DB },方法func (r *PostRepo) FindByID(...)
响应格式
统一使用 common 包的响应函数:
common.Ok(ctx, data) // 200 成功
common.Error(ctx, common.ErrInvalidParam, msg) // 400 参数错误
common.Error(ctx, common.ErrUnauthorized, msg) // 401 未认证
common.Error(ctx, common.ErrForbidden, msg) // 403 无权限
common.Error(ctx, common.ErrNotFound, msg) // 404 未找到
common.Error(ctx, common.ErrInternal, msg) // 500 服务器错误
模板变量
使用驼峰命名,与模板文件保持一致:
gin.H{
"Title": "页面标题",
"ExtraCSS": "/static/css/page.css",
"Guidelines": template.HTML(content), // HTML 内容需显式标记类型
}
依赖注入
使用构造器注入模式,Controller/Service/Middleware 的依赖通过构造函数传入:
// 构造器注入依赖
func NewAuthController(authService authUseCase, sm *session.Manager, limiter rateLimiter, cfg *config.Config, siteSettings *config.SiteSettings) *AuthController {
return &AuthController{authService: authService, sessionManager: sm, rateLimiter: limiter, cfg: cfg, siteSettings: siteSettings}
}
Controller 层通过 ISP 接口隔离 声明最小依赖 —— 只声明自己需要的方法子集,不依赖完整 Service 接口:
// controller/admin/interfaces.go
type siteSettingUseCase interface {
GetSettings() map[string]string
UpdateSetting(key, value string) error
UpdateBoolSetting(key string, value bool) error
GetAuditSettings(defaults *service.AuditDefaults) *service.AuditSettings
}