Skip to content

Router

go
const xxxLogTitle = "参数管理"   // 操作日志模块名

xxx := protected.Group("/xxx")
xxx.GET("/list", satoken.CheckPermission("system:xxx:list"), handler.XxxApiApp.List)
xxx.GET("/configKey/:configKey", sagin.CheckLogin(), handler.XxxApiApp.GetByKey)
xxx.GET("/:xxxId", satoken.CheckPermission("system:xxx:query"), handler.XxxApiApp.GetInfo)
xxx.POST("/export", satoken.CheckPermission("system:xxx:export"),
	oplog.Log(xxxLogTitle, enum.BusinessTypeExport), handler.XxxApiApp.Export)
xxx.POST("", satoken.CheckPermission("system:xxx:add"),
	oplog.Log(xxxLogTitle, enum.BusinessTypeInsert),
	repeatsubmit.RepeatSubmit(0, ""), handler.XxxApiApp.Add)
xxx.PUT("", satoken.CheckPermission("system:xxx:edit"),
	oplog.Log(xxxLogTitle, enum.BusinessTypeUpdate),
	repeatsubmit.RepeatSubmit(0, ""), handler.XxxApiApp.Edit)
xxx.DELETE("/refreshCache", satoken.CheckPermission("system:xxx:remove"),
	oplog.Log(xxxLogTitle, enum.BusinessTypeClean), handler.XxxApiApp.RefreshCache)
xxx.DELETE("/:xxxIds", satoken.CheckPermission("system:xxx:remove"),
	oplog.Log(xxxLogTitle, enum.BusinessTypeDelete), handler.XxxApiApp.Remove)

中间件顺序:鉴权 → 日志 → 防重 → handler

顺序不是风格问题,改了行为就变

  • 鉴权最前:未授权请求不该白占一个防重锁。
  • 日志在防重之前:被防重挡掉的请求 handler 没执行,但仍记一条失败日志。
  • repeatsubmit 须在 encrypt.ApiEncrypt() 之后:指纹要用解密后的明文,否则密文每次随机密钥、同样入参算出不同指纹,防重直接失效。

详见 一次请求的流转

路径细节

  • 根路径用 "" 而非 "/",后者会注册成 /xxx/
  • 静态段(/export/refreshCache/updateByKey)与同层通配段(/:id可以共存,gin 静态段优先,无需刻意调整注册顺序。但 值得写测试钉住——这条规则一旦变化,DELETE /config/refreshCache 会被当成「删除主键为 refreshCache 的配置」而 静默走错分支

注解速查

JavaGo说明
@SaCheckPermission("system:xxx:list")satoken.CheckPermission("system:xxx:list")多个权限码是 OR,语义是「任一命中放行」
@SaCheckRole("admin")satoken.CheckRole("admin")同上
仅需登录(无权限注解)sagin.CheckLogin()没挂 @SaCheckPermission 的接口用这个
完全公开sagin.Ignore()仅在路由位于带 plugin.TokenInterceptor() 的组内时才需要;注册在 protected 组之外(如 /ping)的公开路由什么标记都不用加
@Log(title="x", businessType=INSERT)oplog.Log("x", enum.BusinessTypeInsert)
@RepeatSubmit()repeatsubmit.RepeatSubmit(0, "")0 = 默认 5s;< 1s 注册期 panic
@RepeatSubmit(interval=10000)repeatsubmit.RepeatSubmit(10*time.Second, "")
@RateLimiter(time=60,count=10,limitType=IP)ratelimiter.RateLimiter(time.Minute, 10, ratelimiter.LimitTypeIP, 0, "")
@ApiEncryptencrypt.ApiEncrypt()

BusinessType 取值即 Java ordinal()(库里存数字): Other=0, Insert=1, Update=2, Delete=3, Grant=4, Export=5, Import=6, Force=7, GenCode=8, Clean=9顺序不可调整、不可插入新值,只能末尾追加。

oplog.Log 可选项:WithoutRequestData()WithoutResponseData()WithExcludeParams("field")WithOperatorType(enum.OperatorTypeMobile)。密码类字段已由 constant.ExcludeProperties 全局排除,无需重复声明。

新增进程入口的必备初始化

顺序有依赖,照抄 cmd/standalone/main.go

go
config.Load(...)
jsonx.Init()        // 必须在首个 c.JSON / 参数绑定之前接管 gin codec
database.Init(); redis.Init(); satoken.Init(); encrypt.Init()
snowflake.Init()    // 主键发号器,插入前必须就绪
captcha.Init(); ratelimiter.Init(); repeatsubmit.Init()   // 三者都依赖 redis
oplog.Init(systemservice.OperLogSvcApp.RecordOper)        // 依赖 database + snowflake

全局中间件:Recover → CORS → TraceID → RepeatableBody → AccessLog → XSS → I18n

基于 MIT 协议开源