package main import ( "context" "fmt" "net/http" "github.com/yuninks/errorx" "github.com/yuninks/langx" ) // ---- 定义业务错误码 --------------------------------------------------- // 预定义错误码,集中声明,类型安全。 var ( ErrLoginFailed = errorx.NewCode("login_failed", 401, "登录失败") ErrTokenExpired = errorx.NewCode("token_expired", 401, "令牌已过期,请重新登录") ErrUserNotFound = errorx.NewCode("user_not_found", 404, "用户 #name# 不存在") ErrParamInvalid = errorx.NewCode("param_invalid", 422, "参数校验失败: #field#") ErrInternal = errorx.NewCode("internal", 500, "服务器内部错误") ErrRateLimit = errorx.NewCode("rate_limit", 429, "请求过于频繁,请 #seconds# 秒后重试") ) // ---- 补充语言包(可通过 embed/文件 批量导入) -------------------------- func init() { langx.RegisterTrans("en", map[string]string{ "login_failed": "Login failed", "token_expired": "Token expired, please re-login", "user_not_found": "User #name# not found", "param_invalid": "Parameter validation failed: #field#", "internal": "Internal server error", "rate_limit": "Too many requests, retry in #seconds# seconds", }) } // ====================================================================== // 示例 1:基础用法 — 创建错误并获取多语言消息 // ====================================================================== func exampleBasic() { fmt.Println("=== 示例1:基础用法 ===") ctx := context.Background() // 创建带上下文的运行时错误 err := ErrLoginFailed.New(ctx) fmt.Println("中文:", err.Error()) // 切换到英文 errEn := err.WithLang("en") fmt.Println("英文:", errEn.Error()) // 原实例不受影响 fmt.Println("原实例仍是中文:", err.Error()) // 直接从 ErrorCode 获取翻译消息(不创建 error) fmt.Println("直接翻译:", ErrLoginFailed.Msg(ctx)) fmt.Println() } // ====================================================================== // 示例 2:占位符替换 // ====================================================================== func examplePlaceholder() { fmt.Println("=== 示例2:占位符替换 ===") ctx := context.Background() // 使用 Newf 创建带占位符的错误 err := ErrUserNotFound.Newf(ctx, map[string]string{"name": "admin"}) fmt.Println("中文:", err.Error()) // 链式派生:追加更多占位符 err2 := err.WithKV("extra", "value") fmt.Println("追加占位符:", err2.Error()) // err 不受影响 fmt.Println("原实例不变:", err.Error()) // 不可变链式:中文 → 英文 errEn := ErrRateLimit. Newf(ctx, map[string]string{"seconds": "30"}). WithLang("en") fmt.Println("英文:", errEn.Error()) // 批量替换 WithMap err3 := ErrParamInvalid.New(ctx).WithMap(map[string]string{ "field": "email", }) fmt.Println("WithMap:", err3.Error()) fmt.Println() } // ====================================================================== // 示例 3:辅助函数 — 从 error 中提取信息 // ====================================================================== func exampleHelpers() { fmt.Println("=== 示例3:辅助函数 ===") ctx := context.Background() err := ErrParamInvalid.Newf(ctx, map[string]string{"field": "age"}) // 类型安全提取 e, ok := errorx.As(err) if ok { fmt.Println("Key:", e.Key()) fmt.Println("Code:", e.Code()) fmt.Println("Format:", e.Format()) } // 快捷函数 fmt.Println("CodeFrom:", errorx.CodeFrom(err)) fmt.Println("KeyFrom:", errorx.KeyFrom(err)) // 对非 langError 的容错处理 fmt.Println("CodeFrom(context.Canceled):", errorx.CodeFrom(context.Canceled)) // -1 fmt.Println() } // ====================================================================== // 示例 4:HTTP API 响应 — 模拟真实场景 // ====================================================================== func exampleHTTP() { fmt.Println("=== 示例4:HTTP API 响应 ===") // 模拟从请求中获取语言 zhCtx := langx.SetCtxLang(context.Background(), "zh") enCtx := langx.SetCtxLang(context.Background(), "en") // 处理请求 handleLogin := func(ctx context.Context) (int, string) { // 模拟登录失败 err := ErrLoginFailed.New(ctx) return err.Code(), err.Error() } codeZH, msgZH := handleLogin(zhCtx) codeEN, msgEN := handleLogin(enCtx) fmt.Printf("中文响应: code=%d, msg=%s\n", codeZH, msgZH) fmt.Printf("英文响应: code=%d, msg=%s\n", codeEN, msgEN) // 模拟参数校验失败 handleParam := func(ctx context.Context, field string) (int, string) { err := ErrParamInvalid.Newf(ctx, map[string]string{"field": field}) return errorx.CodeFrom(err), err.Error() } fmt.Println() code, msg := handleParam(zhCtx, "username") fmt.Printf("参数校验(中文): code=%d, msg=%s\n", code, msg) fmt.Println() } // ====================================================================== // 示例 5:中间件 — 统一错误处理 // ====================================================================== func exampleMiddleware() { fmt.Println("=== 示例5:中间件模式 ===") // writeJSON 模拟写入 HTTP JSON 响应 writeJSON := func(err error) { code := errorx.CodeFrom(err) msg := err.Error() if code == -1 { code = 500 msg = "未知错误" } fmt.Printf("HTTP 响应: {\"code\":%d, \"msg\":\"%s\"}\n", code, msg) } // 场景1:业务错误 ctx := context.Background() writeJSON(ErrTokenExpired.New(ctx)) // 场景2:带占位符的业务错误 writeJSON(ErrUserNotFound.Newf(ctx, map[string]string{"name": "test_user"})) // 场景3:非 errorx 错误也能兜底 writeJSON(http.ErrServerClosed) fmt.Println() } // ====================================================================== // 示例 6:error 链与 errors.Is / errors.As 兼容 // ====================================================================== func exampleErrorChain() { fmt.Println("=== 示例6:与标准 errors 包兼容 ===") ctx := context.Background() baseErr := ErrInternal.New(ctx) wrappedErr := fmt.Errorf("处理订单失败: %w", baseErr) // errors.Is / errors.As 仍然可用 e, ok := errorx.As(wrappedErr) if ok { fmt.Println("从包装后的 error 中提取成功:") fmt.Println(" Key:", e.Key()) fmt.Println(" Code:", e.Code()) fmt.Println(" 消息:", e.Error()) } fmt.Println() } // ====================================================================== // 示例 7:动态创建错误码(未预定义的场景) // ====================================================================== func exampleDynamic() { fmt.Println("=== 示例7:动态创建错误码 ===") // 运行时动态注册错误码 dbError := errorx.NewCode("db_connection_failed", 503, "数据库连接失败: #detail#") ctx := context.Background() err := dbError.Newf(ctx, map[string]string{"detail": "timeout after 30s"}) fmt.Println("Key:", errorx.KeyFrom(err)) fmt.Println("Code:", errorx.CodeFrom(err)) fmt.Println("消息:", err.Error()) fmt.Println() } // ---- main ------------------------------------------------------------ func main() { // 初始化 langx langx.InitLangx( langx.SetDefaultCode(0), langx.SetDefaultLanguage("zh"), ) exampleBasic() examplePlaceholder() exampleHelpers() exampleHTTP() exampleMiddleware() exampleErrorChain() exampleDynamic() }