library-go/errs/errors.go

215 lines
5.5 KiB
Go
Raw Normal View History

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,
}
}