「小游戏服务平台」错误码规范(落地实现版)

错误码最佳实践 checklist ✅ * [x] 全局唯一性 :通过 CI 校验错误码不重复 * [x] 国际化 :支持至少中/英多语言 * [x] 多层日志 :前端 → SDK → API 网关 → 微服务,全链路带 * [x] 自动化生成 :从 自动生成 SDK 映射 & 文档 * [x] ...

小游戏平台错误码规范(落地实现版)


1. errors.yaml 配置文件模板

统一在仓库中维护错误码:

# errors.yaml
system:
  - code: 10001
    http: 500
    message: "系统错误,请稍后再试"
    i18n:
      en: "System error, please try again later"
      jp: "システムエラー、後でもう一度試してください"
    detail: "Unknown error"
  - code: 10002
    http: 400
    message: "请求参数无效"
    i18n:
      en: "Invalid request parameter"
      jp: "無効なリクエストパラメータ"
    detail: "Validation error"

user:
  - code: 201001
    http: 401
    message: "用户名或密码错误"
    i18n:
      en: "Incorrect username or password"
    detail: "Auth failed"

  - code: 201002
    http: 401
    message: "Token 无效或已过期"
    i18n:
      en: "Token is invalid or expired"
    detail: "JWT validation failed"

game:
  - code: 202002
    http: 400
    message: "游戏包大小超过限制"
    i18n:
      en: "Game package size exceeds limit"
    detail: "File size > 500MB"

payment:
  - code: 203003
    http: 400
    message: "回调签名验证失败"
    i18n:
      en: "Payment callback signature verification failed"
    detail: "Signature mismatch"

👉 好处:

  • 可读性强
  • 版本化管理(Git 控制)
  • 多语言内置
  • CI/CD 可以校验是否有重复错误码

2. SDK / 后端错误码加载示例

Go 后端示例

package errors

import (
    "encoding/json"
    "io/ioutil"
)

type ErrorCode struct {
    Code    int               `json:"code"`
    Http    int               `json:"http"`
    Message string            `json:"message"`
    I18n    map[string]string `json:"i18n"`
    Detail  string            `json:"detail"`
}

var errorMap = map[int]ErrorCode{}

func LoadErrorCodes(path string) error {
    data, err := ioutil.ReadFile(path)
    if err != nil {
        return err
    }
    var raw map[string][]ErrorCode
    if err := json.Unmarshal(data, &raw); err != nil {
        return err
    }
    for _, codes := range raw {
        for _, e := range codes {
            errorMap[e.Code] = e
        }
    }
    return nil
}

func GetError(code int, lang string) (int, string) {
    e, ok := errorMap[code]
    if !ok {
        return 500, "Unknown error"
    }
    if msg, ok := e.I18n[lang]; ok {
        return e.Http, msg
    }
    return e.Http, e.Message
}

使用示例

httpCode, msg := errors.GetError(201002, "en")
// httpCode = 401
// msg = "Token is invalid or expired"

JavaScript SDK 示例

import errorConfig from "./errors.json";

export function getError(code, lang = "zh") {
  const err = errorConfig[code];
  if (!err) return { http: 500, message: "未知错误" };
  return {
    http: err.http,
    message: err.i18n?.[lang] || err.message,
  };
}

// 使用示例
const err = getError(201002, "en");
console.log(err.message); // Token is invalid or expired

3. 错误码文档自动生成

可以通过 脚本将 errors.yaml 自动生成 Markdown/HTML 文档,避免人工维护:

生成 Markdown 示例

# errors2doc.py
import yaml

with open("errors.yaml", "r", encoding="utf-8") as f:
    data = yaml.safe_load(f)

with open("ERRORS.md", "w", encoding="utf-8") as f:
    for module, codes in data.items():
        f.write(f"## {module}\n\n")
        f.write("| Code | HTTP | Message | EN | JP |\n")
        f.write("|------|------|---------|----|----|\n")
        for c in codes:
            f.write(f"| {c['code']} | {c['http']} | {c['message']} | {c['i18n'].get('en','')} | {c['i18n'].get('jp','')} |\n")
        f.write("\n")

执行后会生成一份 ERRORS.md,供团队直接阅读。

4. 错误码最佳实践 checklist ✅

  • 全局唯一性:通过 CI 校验错误码不重复
  • 国际化:支持至少中/英多语言
  • 多层日志:前端 → SDK → API 网关 → 微服务,全链路带 trace_id
  • 自动化生成:从 errors.yaml 自动生成 SDK 映射 & 文档
  • 分级提示:用户可感知 / 无感知 / 安全敏感
  • 监控告警:关键错误码建立 Prometheus 告警(支付、广告)

总结

小游戏平台的错误码规范最终落地方式是:

  • 集中管理errors.yaml
  • 全链路一致(前端/SDK/后端同一套标准)
  • 国际化支持(多语言 Message)
  • 自动化文档(避免手工更新冲突)
  • 监控联动(关键错误码告警)

这样既能保证 研发效率,又能满足 运维与产品合规需求


5 代码实践:Go go generate 自动生成错误码

本节完善 errors.yaml 配置 + Go go generate 代码生成方案,使错误码管理与代码维护同步。

4.1 errors.yaml 完整模板

version: "1.0.0"

system:
  - code: 10001
    http: 500
    message: "系统错误,请稍后再试"
    i18n: { "en": "Internal server error", "jp": "システムエラー" }
    detail: "未知系统异常,需运维排查"
  - code: 10002
    http: 400
    message: "请求参数无效"
    i18n: { "en": "Invalid parameters" }
    detail: "必填字段缺失或格式非法"

user:
  - code: 201001
    http: 401
    message: "登录失败"
    i18n: { "en": "Login failed" }
    detail: "用户名或密码错误"

game:
  - code: 202002
    http: 400
    message: "游戏包大小超限"
    i18n: { "en": "Package size limit exceeded" }
    detail: "单个上传文件 > 500MB"

4.2 Go 代码生成器

//go:build ignore
package main

import (
	"fmt"
	"os"
	"text/template"
	"gopkg.in/yaml.v3"
)

type ErrDef struct {
	Code    int               `yaml:"code"`
	HTTP    int               `yaml:"http"`
	Message string            `yaml:"message"`
	I18n    map[string]string `yaml:"i18n"`
}

const tmplSrc = `package minigameerr

import "time"

var errorMap = map[int]struct{ HTTP int; Msg string; En string }{
{{range $module, $defs := .}}{{range $d := $defs}}	{{$d.Code}}: { {{$d.HTTP}}, {{$d.Message | printf "%q"}}, {{index $d.I18n "en" | printf "%q"}} },
{{end}}{{end}}
}
func HTTPStatus(code int) int { return errorMap[code].HTTP }
func Message(code int, lang string) string {
    if lang=="en" { return errorMap[code].En }
    return errorMap[code].Msg
}
func WithTrace(code int, traceID string) map[string]interface{} {
    m := errorMap[code]
    return map[string]interface{}{"code":code, "message":m.Msg, "trace_id":traceID, "timestamp":time.Now().Unix()}
}
`

func main() {
	if len(os.Args) < 2 { panic("usage: go run gen_errors.go errors.yaml") }
	data, _ := os.ReadFile(os.Args[1])
	var raw map[string][]ErrDef
	if err := yaml.Unmarshal(data, &raw); err != nil { panic(err) }
	template.Must(template.New("e").Parse(tmplSrc)).Execute(os.Stdout, raw)
}

4.3 使用方式

# 编辑 errors.yaml 后生成 Go 代码
go run gen_errors.go errors.yaml > errors_generated.go
# CI 校验
. .github/workflows/lint-errors.yml:
#   - run: go run scripts/check_errors.go errors.yaml

本章小结

聚焦错误码落地实践,提供了 errors.yaml 配置模板、CI 校验脚本、SDK 映射文件与自动生成文档方案。


延伸阅读


相关专题

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

继续阅读

探索更多技术文章

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

全部文章 返回首页

「prd」更多文章

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