// Package errors 定义业务错误类型与哨兵错误。 // 业务错误携带可被前端消费的业务码(Code),并支持 errors.As 提取。 package errors import ( stderrors "errors" "fmt" ) // BizError 是带业务码的业务错误。 type BizError struct { Code int Msg string Err error } func (e *BizError) Error() string { if e.Err != nil { return fmt.Sprintf("%s: %v", e.Msg, e.Err) } return e.Msg } func (e *BizError) Unwrap() error { return e.Err } // New 构造一个不带原始 error 的业务错误。 func New(code int, msg string) *BizError { return &BizError{Code: code, Msg: msg} } // Wrap 在传播错误时附加上下文与业务码,保留错误链路。 func Wrap(code int, msg string, err error) *BizError { return &BizError{Code: code, Msg: msg, Err: err} } // Is 判断 err 链中是否包含 target,语义与标准库 errors.Is 一致。 // 它沿 Unwrap 链遍历,因此能正确识别被 Wrap 包装后的目标错误 // (如 gorm.ErrRecordNotFound 或本包的哨兵错误 ErrNotFound)。 func Is(err, target error) bool { return stderrors.Is(err, target) } // 常用哨兵错误,供各层直接引用。 var ( ErrBadRequest = New(40000, "bad request") ErrUnauthorized = New(40100, "unauthorized") ErrForbidden = New(40300, "forbidden") ErrNotFound = New(40400, "not found") ErrServer = New(50000, "internal server error") ) // CodeOf 从 err 中提取业务码,非业务错误返回 50000。 func CodeOf(err error) int { var be *BizError if err != nil && asBizError(err, &be) { return be.Code } return 50000 } // MsgOf 从 err 中提取可读消息,非业务错误返回内部错误提示。 func MsgOf(err error) string { var be *BizError if err != nil && asBizError(err, &be) { return be.Msg } return "internal server error" } // asBizError 兼容被 fmt.Errorf("%w") 包装后的 BizError。 func asBizError(err error, target **BizError) bool { for err != nil { if b, ok := err.(*BizError); ok { *target = b return true } u, ok := err.(interface{ Unwrap() error }) if !ok { return false } err = u.Unwrap() } return false }