2024-08-27 13:52:33 +00:00
|
|
|
|
package errs
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2024-08-27 13:53:58 +00:00
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
|
|
"code.30cm.net/digimon/library-go/errs/code"
|
2024-08-27 13:52:33 +00:00
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Scope 全域變數應由服務或模組設置
|
|
|
|
|
var Scope = code.Unset
|
|
|
|
|
|
|
|
|
|
// LibError 7 碼,服務 2 碼,詳細錯誤 2 碼 ,Cat 3 碼 不參與,獨立的 code 組成為( 000 category + 00 detail)
|
|
|
|
|
type LibError struct {
|
|
|
|
|
scope uint32 // 系統代號,*100000 來操作,顯示時不夠會補足 7 位數, Library 定義 -> 輸入時都只要輸入兩位數
|
|
|
|
|
category uint32 // 類別代碼 Library 定義 -> 不客製化業務訊息時,用這個大類別來給錯誤 3 碼
|
|
|
|
|
code uint32 // 細項 ,每個 repo 裡自行定義 -> 一萬以下都可以 2 碼
|
|
|
|
|
msg string // 顯示用的,給前端看的 Msg
|
|
|
|
|
internalErr error // 紀錄且包含真正的錯誤,通常用這個
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Error 是錯誤的介面
|
|
|
|
|
// 私有屬性 "displayMsg" 的 getter 函數,這邊只顯示業務邏輯錯誤,因為可能會帶出去給客戶端
|
|
|
|
|
// 要如何定位系統真的發生什麼錯?請使用 internalErr 來做定位,通常是會印 Log 的所以用這個
|
|
|
|
|
func (e *LibError) Error() string {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.msg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Category 私有屬性 "category" 的 getter 函數
|
|
|
|
|
func (e *LibError) Category() uint32 {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.category
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scope 私有屬性 "scope" 的 getter 函數
|
|
|
|
|
func (e *LibError) Scope() uint32 {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return code.Unset
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.scope
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Code 私有屬性 "code" 的 getter 函數
|
|
|
|
|
func (e *LibError) Code() uint32 {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return code.OK
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.code
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e *LibError) FullCode() uint32 {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.Scope()*100000 + e.Code()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DisplayErrorCode 要顯示的 Error Code
|
|
|
|
|
func (e *LibError) DisplayErrorCode() string {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return "000000"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fmt.Sprintf("%06d", e.FullCode())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// InternalError 帶入真正的 error
|
|
|
|
|
func (e *LibError) InternalError() error {
|
|
|
|
|
var err error = fmt.Errorf("failed to get internal error")
|
|
|
|
|
if e == nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if e.internalErr != nil {
|
|
|
|
|
err = e.internalErr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GeneralError 轉換 category 級別錯誤訊息,模糊化
|
|
|
|
|
func (e *LibError) GeneralError() string {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errStr, ok := code.CatToStr[e.Category()]
|
|
|
|
|
if !ok {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return errStr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Is 在執行 errors.Is() 時調用。
|
|
|
|
|
// 除非你非常確定你在做什麼,否則不要直接使用這個函數。
|
|
|
|
|
// 請使用 errors.Is 代替。
|
|
|
|
|
// 此函數比較兩個錯誤變量是否都是 *Err,並且具有相同的 code(不檢查包裹的內部錯誤)
|
|
|
|
|
func (e *LibError) Is(f error) bool {
|
|
|
|
|
var err *LibError
|
|
|
|
|
ok := errors.As(f, &err)
|
|
|
|
|
if !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.Code() == err.Code()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unwrap 返回底層錯誤
|
|
|
|
|
// 解除包裹錯誤的結果本身可能具有 Unwrap 方法;
|
|
|
|
|
// 我們稱通過反覆解除包裹產生的錯誤序列為錯誤鏈。
|
|
|
|
|
func (e *LibError) Unwrap() error {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e.internalErr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wrap 將內部錯誤設置到 Err 結構
|
|
|
|
|
func (e *LibError) Wrap(internalErr error) *LibError {
|
|
|
|
|
if e != nil {
|
|
|
|
|
e.internalErr = internalErr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e *LibError) GRPCStatus() *status.Status {
|
|
|
|
|
if e == nil {
|
|
|
|
|
return status.New(codes.OK, "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status.New(codes.Code(e.FullCode()), e.Error())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HTTPStatus 返回對應的 HTTP 狀態碼
|
|
|
|
|
func (e *LibError) HTTPStatus() int {
|
|
|
|
|
// 如果錯誤為空或錯誤碼為 OK,則返回 200 狀態碼
|
|
|
|
|
if e == nil || e.Code() == code.OK {
|
|
|
|
|
return http.StatusOK
|
|
|
|
|
}
|
|
|
|
|
// 根據錯誤碼判斷對應的 HTTP 狀態碼
|
|
|
|
|
switch e.Code() % 100 {
|
|
|
|
|
case code.ResourceInsufficient:
|
|
|
|
|
// 如果資源不足,返回 400 狀態碼
|
|
|
|
|
return http.StatusBadRequest
|
|
|
|
|
case code.Unauthorized, code.InsufficientPermission:
|
|
|
|
|
// 如果未授權或權限不足,返回 401 狀態碼
|
|
|
|
|
return http.StatusUnauthorized
|
|
|
|
|
case code.InsufficientQuota:
|
|
|
|
|
// 如果配額不足,返回 402 狀態碼
|
|
|
|
|
return http.StatusPaymentRequired
|
|
|
|
|
case code.InvalidPosixTime, code.Forbidden:
|
|
|
|
|
// 如果時間無效或禁止訪問,返回 403 狀態碼
|
|
|
|
|
return http.StatusForbidden
|
|
|
|
|
case code.ResourceNotFound:
|
|
|
|
|
// 如果資源未找到,返回 404 狀態碼
|
|
|
|
|
return http.StatusNotFound
|
|
|
|
|
case code.ResourceAlreadyExist, code.InvalidResourceState:
|
|
|
|
|
// 如果資源已存在或狀態無效,返回 409 狀態碼
|
|
|
|
|
return http.StatusConflict
|
|
|
|
|
case code.NotValidImplementation:
|
|
|
|
|
// 如果實現無效,返回 501 狀態碼
|
|
|
|
|
return http.StatusNotImplemented
|
|
|
|
|
default:
|
|
|
|
|
// 如果沒有匹配的錯誤碼,則繼續下一步
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根據錯誤的類別判斷對應的 HTTP 狀態碼
|
|
|
|
|
switch e.Category() {
|
|
|
|
|
case code.CatInput:
|
|
|
|
|
// 如果錯誤屬於輸入錯誤類別,返回 400 狀態碼
|
|
|
|
|
return http.StatusBadRequest
|
|
|
|
|
default:
|
|
|
|
|
// 如果沒有符合的條件,返回 500 狀態碼
|
|
|
|
|
return http.StatusInternalServerError
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewError 創建新的 Error
|
|
|
|
|
// 確保 category 在 0 到 999 之間,將超出的 category 設為最大值 999
|
|
|
|
|
// 確保 detail 在 0 到 99 之間,將超出的 detail 設為最大值 99
|
|
|
|
|
func NewError(scope, category, detail uint32, displayMsg string) *LibError {
|
|
|
|
|
// 確保 category 在 0 到 999 之間
|
|
|
|
|
if category > 999 {
|
|
|
|
|
category = 999 // 將超出的 category 設為最大值 999
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 確保 detail 在 0 到 99 之間
|
|
|
|
|
if detail > 99 {
|
|
|
|
|
detail = 99 // 將超出的 detail 設為最大值 99
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &LibError{
|
|
|
|
|
category: category,
|
|
|
|
|
code: category*100 + detail,
|
|
|
|
|
scope: scope,
|
|
|
|
|
msg: displayMsg,
|
|
|
|
|
}
|
|
|
|
|
}
|