「小游戏服务平台」(扩展工程版)

错误码分层设计 为了便于定位和统一管理,可以采用 分层 + 分模块 的设计: 1.1 分层 * 前端层错误(1xx) :网络错误、版本不兼容、SDK 调用失败 * 服务端业务错误(2xx) :用户、游戏、广告、支付、风控等业务逻辑错误 * 系统级错误(5xx) :服务器异常、数据库/缓存不可用 * ...


小游戏平台错误码规范(扩展工程版)


1. 错误码分层设计

为了便于定位和统一管理,可以采用 分层 + 分模块 的设计:

1.1 分层

  • 前端层错误(1xx):网络错误、版本不兼容、SDK 调用失败
  • 服务端业务错误(2xx):用户、游戏、广告、支付、风控等业务逻辑错误
  • 系统级错误(5xx):服务器异常、数据库/缓存不可用
  • 第三方依赖错误(3xx):支付通道、CDN、云存储、广告平台

1.2 分模块(业务域划分)

同前面约定:用户(01)、游戏(02)、支付(03)、广告(04)、开发者(05)、数据(06)、风控(07)、系统(99)

2. SDK 与前端错误处理规范

2.1 错误码统一封装

SDK 内部调用 API 后,必须返回统一格式:

{
  "code": 201002,
  "message": "Token 无效或已过期",
  "detail": "jwt signature invalid",
  "trace_id": "xyz-123-456",
  "timestamp": "2025-09-27T10:20:30Z",
  "i18n": {
    "en": "Token is invalid or expired",
    "jp": "トークンが無効または期限切れです"
  }
}
  • 前端:展示 message(或根据 i18n 选择语言)
  • SDK:上报 trace_id 到监控系统,便于后端追踪

2.2 错误分级提示

  • 用户可感知错误(如密码错误、余额不足) → 明确提示
  • 用户无感知错误(如埋点上报失败) → 静默处理或延迟重试
  • 安全敏感错误(如风控封禁、支付校验失败) → 模糊提示(避免暴露系统逻辑)

3. 日志与监控联动

3.1 Trace ID 贯通

  • 每个请求分配一个 trace_id(UUID/雪花算法)
  • 前端/SDK → API 网关 → 微服务 → 数据存储,全链路携带
  • 出错时,错误码与 trace_id 必须写入日志,方便 ELK/监控系统查询

3.2 指标监控

  • 每个错误码必须在监控系统(Prometheus + Grafana)建立监控指标
  • 示例:
rate(error_count{code="203003"}[5m])

👉 可实时监控「支付回调签名验证失败」的频率,快速发现第三方攻击或接入问题。

3.3 告警规则

  • 系统级错误(5xx)出现 ≥ 100 次/分钟 → 触发告警
  • 业务错误(2xx)出现异常峰值(超过日常均值 3 倍) → 触发告警
  • 支付/广告相关错误必须进入 SLA 监控(直接影响收入)

4. 错误码维护流程

4.1 错误码申请

  • 新功能开发 → 开发人员在「错误码文档」中申请错误码
  • 架构组/后端组长 审核,确保不冲突

4.2 错误码文档

  • 错误码需统一维护在 Markdown 文档 / Swagger API 文档 / 内部 Wiki

  • 每个错误码必须包含:

    • 编码
    • Message(中文/英文/日文多语言)
    • 场景说明
    • HTTP 状态码
    • 解决方案(运维手册中体现)

4.3 错误码版本管理

  • 使用 Git 仓库维护 errors.yamlerrors.json 文件
  • CI/CD 流程校验错误码是否重复
  • SDK 自动拉取最新错误码(生成本地映射文件)

5. 错误码分类示例(细化)

5.1 用户模块

错误码Message说明处理方式
201001用户名或密码错误登录失败弹窗提示,允许重试
201002Token 无效或已过期JWT 校验失败跳转登录页
201005用户被风控限制异常操作弹窗提示,联系客服

5.2 游戏模块

错误码Message说明处理方式
202001上传失败,格式错误zip 包损坏提示重新上传
202002文件大小超限(500MB)超过限制提示压缩或拆包
202004游戏已下架违规/开发者删除提示「游戏已下架」

5.3 支付模块

错误码Message说明处理方式
203001订单不存在无效订单号提示「订单无效」
203003回调签名验证失败签名不匹配安全告警 + 异常上报
203004重复支付请求幂等保护返回原订单状态

5.4 广告模块

错误码Message说明处理方式
204001无广告库存没有可展示广告返回默认广告/占位图
204002点击无效作弊/重复点击静默拦截,不提示用户

6. 最佳实践建议

  • 错误码分配范围(例如用户模块 201000–201999)
  • SDK 内置错误映射表(开发者不需要背数字)
  • 自动化测试覆盖率(确保每个错误码有测试用例)
  • SLA 指标:对支付/广告错误码单独统计成功率,保证收入稳定

总结

小游戏平台的错误码规范需要做到:

  • 模块化(不同业务域独立编号)
  • 分层次(系统/业务/第三方)
  • 可国际化(支持多语言提示)
  • 可追踪(trace_id 全链路追踪)
  • 可运维(日志+监控+告警联动)
  • 可管理(文档化+版本化+自动校验)

这样既能满足 用户体验,也能保证 研发和运维效率


7 代码实践:统一错误中间件与 trace_id 全链路

本节将错误码规范落地为 微服务统一错误中间件,覆盖 trace_id 贯通、分层计算、安全脱敏、自动 HTTP 状态码映射。

5.1 统一错误响应结构

package errmw

import (
	"net/http"
	"time"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type ErrorResponse struct {
	Code      int    `json:"code"`
	Message   string `json:"message"`
	TraceID   string `json:"trace_id"`
	Timestamp int64  `json:"timestamp"`
}

type ErrorCode struct {
	HTTP   int
	Msg    string
	I18nEn string
	Level  string
	Module string
}

5.2 中间件:trace_id + 错误拦截

func TraceMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		traceID := c.GetHeader("X-Trace-ID")
		if traceID == "" { traceID = uuid.New().String() }
		c.Set("trace_id", traceID)
		c.Writer.Header().Set("X-Trace-ID", traceID)
		c.Next()
	}
}

func ErrorMiddleware(registry map[int]ErrorCode) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()
		if len(c.Errors) == 0 { return }
		err := c.Errors.Last()
		bizErr, ok := err.Err.(minigameerr.MiniGameError)
		if !ok {
			writeErrorJSON(c, 10001, registry, true)
			return
		}
		writeErrorJSON(c, bizErr.Code, registry, false)
	}
}

func writeErrorJSON(c *gin.Context, code int, reg map[int]ErrorCode, safeMode bool) {
	def, ok := reg[code]
	if !ok { code, def = 10001, reg[10001] }
	msg := def.Msg
	if c.GetHeader("Accept-Language") == "en" && def.I18nEn != "" { msg = def.I18nEn }
	if safeMode { msg = "系统错误,请稍后再试"; code = 10001 }
	c.JSON(def.HTTP, ErrorResponse{Code: code, Message: msg, TraceID: c.GetString("trace_id"), Timestamp: time.Now().Unix()})
}

5.3 生物层使用示例

func Login(c *gin.Context) {
	var req dto.LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.AbortWithError(400, minigameerr.MiniGameError{Code: 10002}); return
	}
	if _, err := service.Login(c.Request.Context(), req); err != nil {
		c.AbortWithError(401, minigameerr.MiniGameError{Code: 201001}); return
	}
	response.Success(c, user)
}

5.4 注册 Gin 引擎

func NewEngine() *gin.Engine {
	r := gin.New()
	r.Use(TraceMiddleware())
	r.Use(gin.Recovery())
	r.Use(gin.Logger())
	r.Use(ErrorMiddleware(minigameerr.Registry))
	routes.Register(r)
	return r
}

本章小结

从工程化角度扩展错误码设计,实现分层分模块管理 + 国际化 + 自动化生成 + 微服务统一错误中间件。


延伸阅读


相关专题

📂 本文属于 「小游戏服务平台」完整知识体系
如果你正在构建一个 SaaS 小游戏平台,可参考 后端架构白皮书 了解多租户、缓存、高可用等通用架构模式。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「prd」更多文章

  1. Kite 关闭案例:AI 编程助手为什么早起飞却没飞远
  2. Powa Technologies 失败案例:宏大支付愿景为什么没有落地
  3. BlueJeans 受挫案例:视频会议早入场,为什么没赢到最后