optimize code of custom error structure

This commit is contained in:
douxu 2026-01-20 16:42:54 +08:00
parent e1886bc347
commit 13433f93e5
2 changed files with 16 additions and 84 deletions

View File

@ -1,73 +0,0 @@
// Package errcode provides internal error definition and business error definition
package errcode
import (
"net/http"
)
// 此处为公共的错误码, 预留 10000000 ~ 10000099 间的 100 个错误码
var (
Success = newError(0, "success")
ErrServer = newError(10000000, "服务器内部错误")
ErrParams = newError(10000001, "参数错误, 请检查")
ErrNotFound = newError(10000002, "资源未找到")
ErrPanic = newError(10000003, "(*^__^*)系统开小差了,请稍后重试") // 无预期的panic错误
ErrToken = newError(10000004, "Token无效")
ErrForbidden = newError(10000005, "未授权") // 访问一些未授权的资源时的错误
ErrTooManyRequests = newError(10000006, "请求过多")
ErrCoverData = newError(10000007, "ConvertDataError") // 数据转换错误
)
// 各个业务模块自定义的错误码, 从 10000100 开始, 可以按照不同的业务模块划分不同的号段
// Example:
//var (
// ErrOrderClosed = NewError(10000100, "订单已关闭")
//)
// 用户模块相关错误码 10000100 ~ 1000199
var (
ErrUserInvalid = newError(10000101, "用户异常")
ErrUserNameOccupied = newError(10000102, "用户名已被占用")
ErrUserNotRight = newError(10000103, "用户名或密码不正确")
)
// 商品模块相关错误码 10000200 ~ 1000299
var (
ErrCommodityNotExists = newError(10000200, "商品不存在")
ErrCommodityStockOut = newError(10000201, "库存不足")
)
// 购物车模块相关错误码 10000300 1000399
var (
ErrCartItemParam = newError(10000300, "购物项参数异常")
ErrCartWrongUser = newError(10000301, "用户购物信息不匹配")
)
// 订单模块相关错误码 10000500 ~ 10000599
var (
ErrOrderParams = newError(10000500, "订单参数异常")
ErrOrderCanNotBeChanged = newError(10000501, "订单不可修改")
ErrOrderUnsupportedPayScene = newError(10000502, "支付场景暂不支持")
)
func (e *AppError) HttpStatusCode() int {
switch e.Code() {
case Success.Code():
return http.StatusOK
case ErrServer.Code(), ErrPanic.Code():
return http.StatusInternalServerError
case ErrParams.Code(), ErrUserInvalid.Code(), ErrUserNameOccupied.Code(), ErrUserNotRight.Code(),
ErrCommodityNotExists.Code(), ErrCommodityStockOut.Code(), ErrCartItemParam.Code(), ErrOrderParams.Code():
return http.StatusBadRequest
case ErrNotFound.Code():
return http.StatusNotFound
case ErrTooManyRequests.Code():
return http.StatusTooManyRequests
case ErrToken.Code():
return http.StatusUnauthorized
case ErrForbidden.Code(), ErrCartWrongUser.Code(), ErrOrderCanNotBeChanged.Code():
return http.StatusForbidden
default:
return http.StatusInternalServerError
}
}

View File

@ -10,12 +10,12 @@ import (
var codes = map[int]struct{}{}
// AppError define struct of internal error
// AppError define struct of internal error. occurred field records the location where the error is triggered
type AppError struct {
code int
msg string
cause error
occurred string // 保存由底层错误导致AppErr发生时的位置
occurred string
}
func (e *AppError) Error() string {
@ -49,10 +49,6 @@ func (e *AppError) Cause() error {
}
// WithCause define func return top level predefined errors,where the cause field contains the underlying base error
// 在逻辑执行中出现错误, 比如dao层返回的数据库查询错误
// 可以在领域层返回预定义的错误前附加上导致错误的基础错误。
// 如果业务模块预定义的错误码比较详细, 可以使用这个方法, 反之错误码定义的比较笼统建议使用Wrap方法包装底层错误生成项目自定义Error
// 并将其记录到日志后再使用预定义错误码返回接口响应
func (e *AppError) WithCause(err error) *AppError {
newErr := e.Clone()
newErr.cause = err
@ -61,8 +57,6 @@ func (e *AppError) WithCause(err error) *AppError {
}
// Wrap define func packaging information and errors returned by the underlying logic
// 用于逻辑中包装底层函数返回的error 和 WithCause 一样都是为了记录错误链条
// 该方法生成的error 用于日志记录, 返回响应请使用预定义好的error
func Wrap(msg string, err error) *AppError {
if err == nil {
return nil
@ -77,7 +71,7 @@ func (e *AppError) UnWrap() error {
return e.cause
}
// Is define func return result of whether any error in err's tree matches target. implemented to support errors.Is(err, target)
// Is define func return result of whether any error in err's tree matches target. implemented to support errors.Is(err, target)
func (e *AppError) Is(target error) bool {
targetErr, ok := target.(*AppError)
if !ok {
@ -86,6 +80,17 @@ func (e *AppError) Is(target error) bool {
return targetErr.Code() == e.Code()
}
// As define func return result of whether any error in err's tree matches target. implemented to support errors.As(err, target)
func (e *AppError) As(target any) bool {
t, ok := target.(**AppError)
if !ok {
return false
}
*t = e
return true
}
// Clone define func return a new AppError with source AppError's code, msg, cause, occurred
func (e *AppError) Clone() *AppError {
return &AppError{
@ -107,7 +112,7 @@ func newError(code int, msg string) *AppError {
return &AppError{code: code, msg: msg}
}
// getAppErrOccurredInfo 获取项目中调用Wrap或者WithCause方法时的程序位置, 方便排查问题
// getAppErrOccurredInfo define func return the location where the error is triggered
func getAppErrOccurredInfo() string {
pc, file, line, ok := runtime.Caller(2)
if !ok {
@ -140,7 +145,7 @@ type formattedErr struct {
Occurred string `json:"occurred"`
}
// toStructuredError 在JSON Encode 前把Error进行格式化
// toStructuredError define func convert AppError to structured error for better readability
func (e *AppError) toStructuredError() *formattedErr {
fe := new(formattedErr)
fe.Code = e.Code()