219 lines
4.4 KiB
Go
219 lines
4.4 KiB
Go
package error
|
||
|
||
import (
|
||
code2 "code.30cm.net/wanderland/library-go/errors/code"
|
||
"errors"
|
||
"fmt"
|
||
"net/http"
|
||
|
||
"google.golang.org/grpc/codes"
|
||
"google.golang.org/grpc/status"
|
||
)
|
||
|
||
// Scope 全域變數應由服務或模組設置
|
||
var Scope = code2.Unset
|
||
|
||
// Err 6 碼,服務 2, 大類 2, 詳細錯誤 2
|
||
type Err struct {
|
||
category uint32
|
||
code uint32
|
||
scope uint32
|
||
msg string
|
||
internalErr error
|
||
}
|
||
|
||
// Error 是錯誤的介面
|
||
// 私有屬性 "msg" 的 getter 函數
|
||
func (e *Err) Error() string {
|
||
if e == nil {
|
||
return ""
|
||
}
|
||
|
||
// 如果 internal err 存在,連接錯誤字串
|
||
var internalErrStr string
|
||
if e.internalErr != nil {
|
||
internalErrStr = e.internalErr.Error()
|
||
}
|
||
|
||
if e.msg != "" {
|
||
if internalErrStr != "" {
|
||
return fmt.Sprintf("%s: %s", e.msg, internalErrStr)
|
||
}
|
||
return e.msg
|
||
}
|
||
|
||
generalErrStr := e.GeneralError()
|
||
if internalErrStr != "" {
|
||
return fmt.Sprintf("%s: %s", generalErrStr, internalErrStr)
|
||
}
|
||
return generalErrStr
|
||
}
|
||
|
||
// Category 私有屬性 "category" 的 getter 函數
|
||
func (e *Err) Category() uint32 {
|
||
if e == nil {
|
||
return 0
|
||
}
|
||
return e.category
|
||
}
|
||
|
||
// Scope 私有屬性 "scope" 的 getter 函數
|
||
func (e *Err) Scope() uint32 {
|
||
if e == nil {
|
||
return code2.Unset
|
||
}
|
||
|
||
return e.scope
|
||
}
|
||
|
||
// CodeStr 返回帶有零填充的錯誤代碼字串
|
||
func (e *Err) CodeStr() string {
|
||
if e == nil {
|
||
return "00000"
|
||
}
|
||
|
||
if e.Category() == code2.CatGRPC {
|
||
return fmt.Sprintf("%d%04d", e.Scope(), e.Category()+e.Code())
|
||
}
|
||
|
||
return fmt.Sprintf("%d%04d", e.Scope(), e.Code())
|
||
}
|
||
|
||
// Code 私有屬性 "code" 的 getter 函數
|
||
func (e *Err) Code() uint32 {
|
||
if e == nil {
|
||
return code2.OK
|
||
}
|
||
|
||
return e.code
|
||
}
|
||
|
||
func (e *Err) FullCode() uint32 {
|
||
if e == nil {
|
||
return 0
|
||
}
|
||
|
||
if e.Category() == code2.CatGRPC {
|
||
return e.Scope()*10000 + e.Category() + e.Code()
|
||
}
|
||
|
||
return e.Scope()*10000 + e.Code()
|
||
}
|
||
|
||
// HTTPStatus 返回對應的 HTTP 狀態碼
|
||
func (e *Err) HTTPStatus() int {
|
||
if e == nil || e.Code() == code2.OK {
|
||
return http.StatusOK
|
||
}
|
||
// 根據 code 判斷狀態碼
|
||
switch e.Code() {
|
||
case code2.ResourceInsufficient:
|
||
// 400
|
||
return http.StatusBadRequest
|
||
case code2.Unauthorized, code2.InsufficientPermission:
|
||
// 401
|
||
return http.StatusUnauthorized
|
||
case code2.InsufficientQuota:
|
||
// 402
|
||
return http.StatusPaymentRequired
|
||
case code2.InvalidPosixTime, code2.Forbidden:
|
||
// 403
|
||
return http.StatusForbidden
|
||
case code2.ResourceNotFound:
|
||
// 404
|
||
return http.StatusNotFound
|
||
case code2.ResourceAlreadyExist, code2.InvalidResourceState:
|
||
// 409
|
||
return http.StatusConflict
|
||
case code2.NotValidImplementation:
|
||
// 501
|
||
return http.StatusNotImplemented
|
||
default:
|
||
}
|
||
|
||
// 根據 category 判斷狀態碼
|
||
switch e.Category() {
|
||
case code2.CatInput:
|
||
return http.StatusBadRequest
|
||
default:
|
||
// 如果沒有符合的條件,返回狀態碼 500
|
||
return http.StatusInternalServerError
|
||
}
|
||
}
|
||
|
||
// GeneralError 轉換 category 級別錯誤訊息
|
||
// 這是給客戶或 API 調用者的一般錯誤訊息
|
||
func (e *Err) GeneralError() string {
|
||
if e == nil {
|
||
return ""
|
||
}
|
||
|
||
errStr, ok := code2.CatToStr[e.Category()]
|
||
if !ok {
|
||
return ""
|
||
}
|
||
|
||
return errStr
|
||
}
|
||
|
||
// Is 在執行 errors.Is() 時調用。
|
||
// 除非你非常確定你在做什麼,否則不要直接使用這個函數。
|
||
// 請使用 errors.Is 代替。
|
||
// 此函數比較兩個錯誤變量是否都是 *Err,並且具有相同的 code(不檢查包裹的內部錯誤)
|
||
func (e *Err) Is(f error) bool {
|
||
var err *Err
|
||
ok := errors.As(f, &err)
|
||
if !ok {
|
||
return false
|
||
}
|
||
return e.Code() == err.Code()
|
||
}
|
||
|
||
// Unwrap 返回底層錯誤
|
||
// 解除包裹錯誤的結果本身可能具有 Unwrap 方法;
|
||
// 我們稱通過反覆解除包裹產生的錯誤序列為錯誤鏈。
|
||
func (e *Err) Unwrap() error {
|
||
if e == nil {
|
||
return nil
|
||
}
|
||
return e.internalErr
|
||
}
|
||
|
||
// Wrap 將內部錯誤設置到 Err 結構
|
||
func (e *Err) Wrap(internalErr error) *Err {
|
||
if e != nil {
|
||
e.internalErr = internalErr
|
||
}
|
||
return e
|
||
}
|
||
|
||
func (e *Err) GRPCStatus() *status.Status {
|
||
if e == nil {
|
||
return status.New(codes.OK, "")
|
||
}
|
||
|
||
return status.New(codes.Code(e.FullCode()), e.Error())
|
||
}
|
||
|
||
// 工廠函數
|
||
|
||
// NewErr 創建新的 Err
|
||
func NewErr(scope, category, detail uint32, msg string) *Err {
|
||
return &Err{
|
||
category: category,
|
||
code: detail,
|
||
scope: scope,
|
||
msg: msg,
|
||
}
|
||
}
|
||
|
||
// NewGRPCErr 創建新的 gRPC Err
|
||
func NewGRPCErr(scope, detail uint32, msg string) *Err {
|
||
return &Err{
|
||
category: code2.CatGRPC,
|
||
code: detail,
|
||
scope: scope,
|
||
msg: msg,
|
||
}
|
||
}
|