SuperAPI AI Directory

gin

General & other

View repository

Updated 10-16 04:56

About this skill

Gin Backend Development Specification

This specification is based on the current youlai-gin code (module name youlai-gin) and incorporates the Uber Go Style Guide and Go CodeReviewComments community conventions.

Trigger Conditions

  • Develop Gin projects
  • Implement REST APIs
  • Use GORM for data access
  • Implement JWT authentication
  • Implement permission control

Part 1: Technology Stack

Layer Selection Description
Runtime Environment Go 1.25+ Robust standard library, native concurrency
Web Framework Gin High-performance HTTP router
ORM GORM 2.0 Chained queries
Database MySQL 8.x InnoDB engine
Cache Redis 7.x (go-redis) Distributed cache / Token storage
Authentication golang-jwt v5 JWT signing/verification
Logging zap + lumberjack rotation Structured logging
API Documentation swaggo/swag (swag init -o api) OpenAPI auto-generation

Part 2: Directory Structure

Basic directory reference Standard Go Project Layout;internal/ Divided by business module, each module contains handler/model/service/router.

├── main.go                         # 程序入口(配置/日志/数据库/存储初始化 + 优雅关闭)
├── api/                            # Swagger 产物(swag init -o api 生成:docs.go/swagger.json/swagger.yaml)
├── internal/                       # 私有代码(Go 编译器强制不可被外部导入)
│   ├── auth/                       # 认证模块(账密/短信/扫码/小程序登录)
│   │   ├── handler/ model/ service/ router.go
│   ├── system/                     # 系统管理(按实体分子模块)
│   │   ├── user/ role/ menu/ dept/ dict/ config/ notice/ log/
│   │   │   └── handler/ model/ repository/ service/
│   │   └── router.go               # 模块装配(构造注入或函数注册)
│   ├── codegen/                    # 代码生成器
│   ├── file/                       # 文件模块
│   ├── message/                    # 消息模块(SSE 推送)
│   ├── common/                     # 公共基础设施(禁止依赖业务模块)
│   │   ├── auth/                   # JWT / Redis TokenManager + 认证/权限中间件
│   │   ├── config/                 # 配置加载(viper)
│   │   ├── context/                # gin 上下文取用户信息、操作人注入
│   │   ├── database/               # GORM 初始化 + 分页
│   │   ├── excel/ json/ logger/    # 导出、序列化、结构化日志
│   │   ├── permission/             # 权限服务 + 数据权限(datascope)
│   │   ├── redis/                  # Redis 客户端 + key 规范
│   │   ├── storage/                # 文件存储(local/s3/aliyun 工厂)
│   │   ├── utils/                  # 文件/密码/树/验证码工具(历史包,新代码不再往里堆)
│   │   ├── validator/              # 参数绑定与校验(BindJSON/BindQuery)
│   │   └── response.go             # 统一响应(包名 common,导入时别名 response)
│   ├── middleware/                 # 全局中间件(error_handler/operation_log/rate_limiter/requestid)
│   └── router/register.go          # 路由注册入口
├── pkg/                            # 公共库(可被外部导入,禁止依赖 internal/)
│   ├── constant/                   # 业务常量 + 错误码(阿里规范 "00000"/"A0400"…)
│   ├── enums/                      # 操作类型 / 日志模块枚举
│   ├── errs/                       # AppError 统一错误(Code/Msg/HTTPStatus/Err/Stack)
│   ├── gormx/                      # GORM 扩展(审计钩子、PatchMap)
│   ├── model/                      # BaseEntity/BaseQuery/PagedData/Option/KeyValue
│   └── types/                      # BigInt/FlexInt/LocalTime 序列化类型
├── configs/                        # dev/prod/test.yaml
├── docs/                           # 项目文档与图片(非 Swagger 产物)
├── docker/docker-compose.yml
├── sql/mysql/                      # 建库脚本
└── go.mod / go.sum

Design Principles:

  • internal/ cannot be imported externally (compiler-enforced);pkg/ reusable,must not import internal/
  • internal/common/ is infrastructure,must not import business modules(internal/system etc.)
  • Business module layering Handler → Service → Repository, model holds entity/form/query/vo
  • Target structure (new modules must follow): struct + constructor injection, e.g. dept:
deptHandler.NewHandler(deptService.NewService(deptRepo.NewRepository(db))).RegisterRoutes(r)

The package-level function style of user/role/config/notice/log etc. is legacy; new code should no longer imitate it.

Layer Directory Responsibility Forbidden Zone
Handler handler/ Parameter binding, calling service, writing response Do not write business logic, do not touch GORM
Model model/ entity/form/query/vo No logic
Repository repository/ GORM queries Do not write business judgments
Service service/ Business logic Do not accept *gin.Context, the first parameter is context.Context

Part 3: Naming Conventions

3.1 File Naming

Type Convention Example
Handler {name}_handler.go user_handler.go
Service {name}_service.go user_service.go
Repository {name}_repo.go user_repo.go
Entity entity.go under model/ model/entity.go
Form/Query/View form.go / query.go / vo.go Same as above

3.2 Naming Principles

  • Concise without redundancy: the package name already provides context, type names do not repeat the package name
  • Exported uppercase GetByID, unexported lowercase parseToken
  • context.Context always as a functionfirst parameter
  • Prohibitedaliasing pkg/model as common(misleading and confused with internal/common )
  • Avoid package names colliding with the standard library (legacy internal/common/context, alias on import appContext)
// ❌ 冗余:调用时变成 user.UserService
package user
type UserService struct{}

// ✅ 包名已提供语义
package user
type Service struct{}

3.3 Method Naming (new code must follow)

Action Prefix Example
Query single Get Get(ctx, id)
Query list List List(ctx, query)
Paginated query Page Page(ctx, query)
Create Create Create(ctx, form)
Update Update Update(ctx, form)
Delete Delete Delete(ctx, id)
Dropdown options Options Options(ctx)
Existence check Exists ExistsByCode(ctx, code)
  • Method nameWithout entity name: the receiver is already dept.Repository, write List instead of GetDeptList
  • Legacy Save*(create-or-update combined),GetXxxList no longer mimics

3.4 Variable Naming

Type Convention Example
Variable camelCase userList
Constant PascalCase MaxSize
Private variable lowercase first letter internalState
Boolean Is/Has/Can prefix isDeleted, hasPermission

3.5 import ordering (goimports -local youlai-gin enforced)

import (
    // 1. 标准库
    "context"
    "time"

    // 2. 第三方库
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"

    // 3. 本项目内部
    "youlai-gin/internal/common/response"
    "youlai-gin/pkg/errs"
)

Part 4: RESTful API Specification

4.1 Standard CRUD paths (using user as an example)

Operation Method Path
Paginated list GET /api/v1/users(pageNum/pageSize/keyword go in query)
Form echo GET /api/v1/users/:userId/form
Create POST /api/v1/users
Update PUT /api/v1/users/:userId
Delete (batch supported) DELETE /api/v1/users/:ids(comma-separated)
Status change PATCH /api/v1/users/:userId/status
Dropdown options GET /api/v1/users/options
Import/Export POST/GET /api/v1/users/import / /api/v1/users/export

4.2 Handler Template

// Page 用户分页列表
// @Summary 用户分页列表
// @Tags 02.用户接口
// @Param query query model.UserQuery false "查询参数"
// @Success 200 {object} common.Result
// @Router /api/v1/users [get]
func (h *Handler) Page(c *gin.Context) {
    var query model.UserQuery
    if err := validator.BindQuery(c, &query); err != nil {
        c.Error(err)
        return
    }
    result, err := h.svc.Page(c.Request.Context(), &query)
    if err != nil {
        c.Error(err)
        return
    }
    response.OkPaged(c, result)
}

Key points: for binding use validator.BindJSON/BindQuery(returns the already wrapped *errs.AppError); errors always c.Error(err) + return; responses always response.Ok/OkPaged/OkMsg。


Part 5: Response Format and Exception Handling

5.1 Unified response (internal/common/response.go, package name common)

type Result struct {
    Code string      `json:"code"` // 阿里错误码:00000 成功 / A0400 参数错 / B0001 系统错
    Msg  string      `json:"msg"`
    Data interface{} `json:"data"`
}
Function Purpose
Ok(c, data) Success + data
OkPage(c, list, pageNum, pageSize, total) / OkPaged(c, *model.PagedData) Pagination
OkMsg(c, msg) Success + message only
FromAppError(c, ae) By AppError output (real HTTP status code), for error middleware only

5.2 Error flow:c.Error + global middleware

// pkg/errs
type AppError struct {
    Code       string `json:"code"`
    Msg        string `json:"msg"`
    HTTPStatus int    `json:"-"`
    Err        error  `json:"-"`  // 仅日志,不返回客户端
    Stack      string `json:"-"`
}
  • Handler only writes c.Error(errs.BadRequest("具体信息")) / passes through the error returned by service
  • middleware.ErrorHandler() unified FromAppError output; for type checking use errors.As, not bare assertions
  • Forbidden handler directly calls response.Fail/BadRequest(legacy function, to be deleted)
  • Errors are logged only once at the top level; intermediate layers fmt.Errorf("...: %w", err) wrap

Part 6: Entity Specification

// pkg/model.BaseEntity:公共审计字段 + 手动软删除
type BaseEntity struct {
    CreateBy   *types.BigInt   `gorm:"column:create_by" json:"createBy,omitempty"`
    CreateTime types.LocalTime `gorm:"column:create_time;autoCreateTime" json:"createTime,omitempty"`
    UpdateBy   *types.BigInt   `gorm:"column:update_by" json:"updateBy,omitempty"`
    UpdateTime types.LocalTime `gorm:"column:update_time;autoUpdateTime" json:"updateTime,omitempty"`
    IsDeleted  int             `gorm:"column:is_deleted;default:0" json:"-"`
}

// 业务实体:主键用 types.BigInt(解决 JS 大数精度),显式 column 标签
type User struct {
    ID       types.BigInt `gorm:"primaryKey;autoIncrement" json:"id"`
    Username string       `gorm:"column:username;not null" json:"username"`
    Password string       `gorm:"column:password;not null" json:"-"`
    DeptID   types.BigInt `gorm:"column:dept_id" json:"deptId"`
    common.BaseEntity
}

func (User) TableName() string { return "sys_user" }
  • Soft delete uses the is_deleted flag (not gorm.DeletedAt), queries must include the condition
  • create_by/update_by is populated by the gormx audit hook from ctx,only one entity definition is allowed per table

Part 7: Service Specification

// Repository 接口定义在消费方(service 包),便于替换与测试
type Repository interface {
    Page(ctx context.Context, query *model.UserQuery) ([]model.User, int64, error)
    Create(ctx context.Context, user *model.User) error
}

type Service struct {
    repo Repository
}

func NewService(repo Repository) *Service { return &Service{repo: repo} }

func (s *Service) Create(ctx context.Context, form *model.UserForm) error {
    exists, err := s.repo.ExistsByUsername(ctx, form.Username)
    if err != nil {
        return err
    }
    if exists {
        return errs.BadRequest("用户名已存在")
    }
    user := &model.User{Username: form.Username, Password: utils.HashPassword(form.Password)}
    return s.repo.Create(ctx, user)
}
  • Forbidden service/repo methods receive *gin.Context; when an operator is needed, the handler passes in appContext.OperatorCtx(c)(operator already injected, for use by the audit hook)
  • repo methods uniformly use ctx as the first parameter + r.db.WithContext(ctx)
  • Business failures return errs.*,do not use panic

Part 8: Authentication Specification

8.1 TokenManager and Middleware

// internal/common/auth:TokenManager 接口(JWT/Redis 双实现),由 config.Security 决定
tokenManager, _ := auth.CreateTokenManager(&config.Cfg.Security)
authorized.Use(pkgAuth.Middleware(tokenManager))     // 验签 + 写入用户上下文
r.GET("...", auth.RequirePermission("sys:user:list"), handler) // 权限点

8.2 Permission Identifier Format

模块:实体:操作
sys:user:create / sys:user:update / sys:user:delete / sys:role:list

8.3 Getting the Current User

  • appContext.GetCurrentUserID(c) / GetCurrentUser(c): returns (v, error), an error means errs.Unauthorized, directly c.Error
  • When an operator is needed:appContext.OperatorCtx(c) convert to context.Context and pass to service

Part 9: Comment Specification

  • Exported symbols must have godoc comments(Chinese is fine; keep signal words in English)
  • Swagger annotations:@Summary / @Tags / @Param / @Success / @Router; use Tags with 01.认证接口 numbered grouping like this
  • Comments should state Why not What; do not write perishable context like "used for XXX flow"
  • Generated artifacts:swag init -o api(output to api/, do not edit by hand)
// Page 分页查询用户列表。
//
// 根据关键词模糊搜索用户名/昵称/手机号,支持状态筛选。
//
// @Summary 用户分页列表
// @Tags 02.用户接口
// @Param query query model.UserQuery false "查询参数"
// @Success 200 {object} common.Result
// @Router /api/v1/users [get]
func (h *Handler) Page(c *gin.Context) { ... }

Part 10: Code Quality Checklist

  • Follow RESTful path conventions (Part 4.1)
  • Errors go through c.Error + ErrorHandler; handlers must not directly response.Fail
  • service/repo must not contain *gin.Context,ctx as the first parameter
  • strconv.ParseInt/Atoi Errors must be checked
  • File uploads must validate size and type
  • Entities embed pkg/model.BaseEntity, soft delete uses is_deleted
  • New APIs have Swaggo annotations,swag init -o api regenerate
  • goimports -w -local youlai-gin + go vet passes, CI adds golangci-lint
  • imports in three ordered groups (standard library → third-party → project-internal)
  • Exported symbols have godoc comments
  • Error checks use errors.Is/As, no bare type assertions

Common Anti-Patterns

Anti-Pattern Correct Practice
fmt.Printf("debug: %v", data) slog/zap Structured logging
panic(err) Handle business errors Return errs.*,handler c.Error
strconv.ParseInt(s, 10, 64) ignore err Errors must be checked
Context stuffed into a struct field Context as the function's first parameter
service receives *gin.Context handler passes OperatorCtx(c) / c.Request.Context()
layer by layer log.Error + return err log only once at the top level; in between %w wrap
HTTP calls without timeout context.WithTimeout(ctx, 30*time.Second)
Stack traces returned to the caller Error messages sanitized; stack traces only written to logs
Create a new utils/common junk package Name by domain (e.g. excel、storage), put utility functions in the package that uses them
Alias pkg/model as common Use the package name directly model
Multiple entity definitions for the same table Single entity definition, reused across layers
Dangling DI graph / exported functions nobody calls Delete or complete them; no dead code left overnight