2024-08-06 09:38:33 +00:00
|
|
|
|
package error
|
|
|
|
|
|
|
|
|
|
import (
|
2024-08-07 16:01:58 +00:00
|
|
|
|
code2 "code.30cm.net/wanderland/library-go/errors/code"
|
2024-08-06 09:38:33 +00:00
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Scope 全域變數應由服務或模組設置
|
2024-08-07 16:01:58 +00:00
|
|
|
|
var Scope = code2.Unset
|
2024-08-06 09:38:33 +00:00
|
|
|
|
|
2024-08-06 16:23:40 +00:00
|
|
|
|
// Err 6 碼,服務 2, 大類 2, 詳細錯誤 2
|
2024-08-06 09:38:33 +00:00
|
|
|
|
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 {
|
2024-08-07 16:01:58 +00:00
|
|
|
|
return code2.Unset
|
2024-08-06 09:38:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.scope
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CodeStr 返回帶有零填充的錯誤代碼字串
|
|
|
|
|
func (e *Err) CodeStr() string {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return "00000"
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:01:58 +00:00
|
|
|
|
if e.Category() == code2.CatGRPC {
|
2024-08-06 09:38:33 +00:00
|
|
|
|
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 {
|
2024-08-07 16:01:58 +00:00
|
|
|
|
return code2.OK
|
2024-08-06 09:38:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.code
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e *Err) FullCode() uint32 {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:01:58 +00:00
|
|
|
|
if e.Category() == code2.CatGRPC {
|
2024-08-06 09:38:33 +00:00
|
|
|
|
return e.Scope()*10000 + e.Category() + e.Code()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.Scope()*10000 + e.Code()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HTTPStatus 返回對應的 HTTP 狀態碼
|
|
|
|
|
func (e *Err) HTTPStatus() int {
|
2024-08-07 16:01:58 +00:00
|
|
|
|
if e == nil || e.Code() == code2.OK {
|
2024-08-06 09:38:33 +00:00
|
|
|
|
return http.StatusOK
|
|
|
|
|
}
|
|
|
|
|
// 根據 code 判斷狀態碼
|
|
|
|
|
switch e.Code() {
|
2024-08-07 16:01:58 +00:00
|
|
|
|
case code2.ResourceInsufficient:
|
2024-08-06 09:38:33 +00:00
|
|
|
|
// 400
|
|
|
|
|
return http.StatusBadRequest
|
2024-08-07 16:01:58 +00:00
|
|
|
|
case code2.Unauthorized, code2.InsufficientPermission:
|
2024-08-06 09:38:33 +00:00
|
|
|
|
// 401
|
|
|
|
|
return http.StatusUnauthorized
|
2024-08-07 16:01:58 +00:00
|
|
|
|
case code2.InsufficientQuota:
|
2024-08-06 09:38:33 +00:00
|
|
|
|
// 402
|
|
|
|
|
return http.StatusPaymentRequired
|
2024-08-07 16:01:58 +00:00
|
|
|
|
case code2.InvalidPosixTime, code2.Forbidden:
|
2024-08-06 09:38:33 +00:00
|
|
|
|
// 403
|
|
|
|
|
return http.StatusForbidden
|
2024-08-07 16:01:58 +00:00
|
|
|
|
case code2.ResourceNotFound:
|
2024-08-06 09:38:33 +00:00
|
|
|
|
// 404
|
|
|
|
|
return http.StatusNotFound
|
2024-08-07 16:01:58 +00:00
|
|
|
|
case code2.ResourceAlreadyExist, code2.InvalidResourceState:
|
2024-08-06 09:38:33 +00:00
|
|
|
|
// 409
|
|
|
|
|
return http.StatusConflict
|
2024-08-07 16:01:58 +00:00
|
|
|
|
case code2.NotValidImplementation:
|
2024-08-06 09:38:33 +00:00
|
|
|
|
// 501
|
|
|
|
|
return http.StatusNotImplemented
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根據 category 判斷狀態碼
|
|
|
|
|
switch e.Category() {
|
2024-08-07 16:01:58 +00:00
|
|
|
|
case code2.CatInput:
|
2024-08-06 09:38:33 +00:00
|
|
|
|
return http.StatusBadRequest
|
|
|
|
|
default:
|
|
|
|
|
// 如果沒有符合的條件,返回狀態碼 500
|
|
|
|
|
return http.StatusInternalServerError
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GeneralError 轉換 category 級別錯誤訊息
|
|
|
|
|
// 這是給客戶或 API 調用者的一般錯誤訊息
|
|
|
|
|
func (e *Err) GeneralError() string {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:01:58 +00:00
|
|
|
|
errStr, ok := code2.CatToStr[e.Category()]
|
2024-08-06 09:38:33 +00:00
|
|
|
|
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())
|
|
|
|
|
}
|
2024-08-07 08:27:15 +00:00
|
|
|
|
|
|
|
|
|
// 工廠函數
|
|
|
|
|
|
|
|
|
|
// 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{
|
2024-08-07 16:01:58 +00:00
|
|
|
|
category: code2.CatGRPC,
|
2024-08-07 08:27:15 +00:00
|
|
|
|
code: detail,
|
|
|
|
|
scope: scope,
|
|
|
|
|
msg: msg,
|
|
|
|
|
}
|
|
|
|
|
}
|