From fad998ea150a80184cad71dfc26af693661ca812 Mon Sep 17 00:00:00 2001 From: yumaojun03 <719118794@qq.com> Date: Sun, 11 May 2025 17:19:48 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=B8=9A=E5=8A=A1=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/v3/controllers/book.go | 5 ++ book/v3/controllers/comment.go | 4 ++ book/v3/exception/README.md | 88 +++++++++++++++++++++++++++++ book/v3/exception/common.go | 35 ++++++++++++ book/v3/exception/exception.go | 59 +++++++++++++++++++ book/v3/exception/exception_test.go | 24 ++++++++ 6 files changed, 215 insertions(+) create mode 100644 book/v3/exception/README.md create mode 100644 book/v3/exception/common.go create mode 100644 book/v3/exception/exception.go create mode 100644 book/v3/exception/exception_test.go diff --git a/book/v3/controllers/book.go b/book/v3/controllers/book.go index e3b4daf..485e8d8 100644 --- a/book/v3/controllers/book.go +++ b/book/v3/controllers/book.go @@ -4,7 +4,9 @@ import ( "context" "122.51.31.227/go-course/go18/book/v3/config" + "122.51.31.227/go-course/go18/book/v3/exception" "122.51.31.227/go-course/go18/book/v3/models" + "gorm.io/gorm" ) var Book = &BookController{} @@ -37,6 +39,9 @@ func (c *BookController) GetBook(ctx context.Context, in *GetBookRequest) (*mode bookInstance := &models.Book{} // 需要从数据库中获取一个对象 if err := config.DB().Where("id = ?", in.BookNumber).Take(bookInstance).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, exception.ErrNotFound("book number: %d not found", in.BookNumber) + } return nil, err } diff --git a/book/v3/controllers/comment.go b/book/v3/controllers/comment.go index 6b537d6..0406170 100644 --- a/book/v3/controllers/comment.go +++ b/book/v3/controllers/comment.go @@ -20,6 +20,10 @@ func (c *CommentController) AddComment(ctx context.Context, in *AddCommentReques // 业务处理的细节 // 多个业务模块 进行交互 book, err := Book.GetBook(ctx, NewGetBookRequest(in.BookNumber)) + + // if exception.IsApiException(err, exception.CODE_NOT_FOUND) { + + // } if err != nil { // 获取查询不到报错 return nil, err diff --git a/book/v3/exception/README.md b/book/v3/exception/README.md new file mode 100644 index 0000000..c66ccb0 --- /dev/null +++ b/book/v3/exception/README.md @@ -0,0 +1,88 @@ +# 业务异常 + +## 异常的定义 + +```go +// 用于描述业务异常 +// 实现自定义异常 +// return error +type ApiException struct { + // 业务异常的编码, 50001 表示Token过期 + Code int `json:"code"` + // 异常描述信息 + Message string `json:"message"` + // 不会出现在Boyd里面, 序列画成JSON, http response 进行set + HttpCode int `json:"-"` +} + +// The error built-in interface type is the conventional interface for +// representing an error condition, with the nil value representing no error. +// +// type error interface { +// Error() string +// } +func (e *ApiException) Error() string { + return e.Message +} +``` + +## 异常的比对, 对于 Error Code 更加准确 + +```go +// 通过 Code 来比较错误 +func IsApiException(err error, code int) bool { + var apiErr *ApiException + if errors.As(err, &apiErr) { + return apiErr.Code == code + } + return false +} +``` + +## 内置了一些全局异常,方便快速使用 + +```go +func ErrServerInternal(format string, a ...any) *ApiException { + return &ApiException{ + Code: CODE_SERVER_ERROR, + Message: fmt.Sprintf(format, a...), + HttpCode: 500, + } +} + +func ErrNotFound(format string, a ...any) *ApiException { + return &ApiException{ + Code: CODE_NOT_FOUND, + Message: fmt.Sprintf(format, a...), + HttpCode: 404, + } +} + +func ErrValidateFailed(format string, a ...any) *ApiException { + return &ApiException{ + Code: CODE_PARAM_INVALIDATE, + Message: fmt.Sprintf(format, a...), + HttpCode: 400, + } +} +``` + +## 返回自定义异常 + +```go + // 需要从数据库中获取一个对象 + if err := config.DB().Where("id = ?", in.BookNumber).Take(bookInstance).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, exception.ErrNotFound("book number: %d not found", in.BookNumber) + } + return nil, err + } +``` + +## 判断自定义异常 + +```go +if exception.IsApiException(err, exception.CODE_NOT_FOUND) { + // 异常处理逻辑 +} +``` diff --git a/book/v3/exception/common.go b/book/v3/exception/common.go new file mode 100644 index 0000000..e5664de --- /dev/null +++ b/book/v3/exception/common.go @@ -0,0 +1,35 @@ +package exception + +import ( + "fmt" +) + +const ( + CODE_SERVER_ERROR = 5000 + CODE_NOT_FOUND = 404 + CODE_PARAM_INVALIDATE = 400 +) + +func ErrServerInternal(format string, a ...any) *ApiException { + return &ApiException{ + Code: CODE_SERVER_ERROR, + Message: fmt.Sprintf(format, a...), + HttpCode: 500, + } +} + +func ErrNotFound(format string, a ...any) *ApiException { + return &ApiException{ + Code: CODE_NOT_FOUND, + Message: fmt.Sprintf(format, a...), + HttpCode: 404, + } +} + +func ErrValidateFailed(format string, a ...any) *ApiException { + return &ApiException{ + Code: CODE_PARAM_INVALIDATE, + Message: fmt.Sprintf(format, a...), + HttpCode: 400, + } +} diff --git a/book/v3/exception/exception.go b/book/v3/exception/exception.go new file mode 100644 index 0000000..1d285ff --- /dev/null +++ b/book/v3/exception/exception.go @@ -0,0 +1,59 @@ +package exception + +import ( + "errors" + + "github.com/infraboard/mcube/v2/tools/pretty" +) + +func NewApiException(code int, message string) *ApiException { + return &ApiException{ + Code: code, + Message: message, + } +} + +// 用于描述业务异常 +// 实现自定义异常 +// return error +type ApiException struct { + // 业务异常的编码, 50001 表示Token过期 + Code int `json:"code"` + // 异常描述信息 + Message string `json:"message"` + // 不会出现在Boyd里面, 序列画成JSON, http response 进行set + HttpCode int `json:"-"` +} + +// The error built-in interface type is the conventional interface for +// representing an error condition, with the nil value representing no error. +// +// type error interface { +// Error() string +// } +func (e *ApiException) Error() string { + return e.Message +} + +func (e *ApiException) String() string { + return pretty.ToJSON(e) +} + +func (e *ApiException) WithMessage(msg string) *ApiException { + e.Message = msg + return e +} + +func (e *ApiException) WithHttpCode(httpCode int) *ApiException { + e.HttpCode = httpCode + return e +} + +// 通过 Code 来比较错误 +func IsApiException(err error, code int) bool { + var apiErr *ApiException + if errors.As(err, &apiErr) { + return apiErr.Code == code + } + return false +} diff --git a/book/v3/exception/exception_test.go b/book/v3/exception/exception_test.go new file mode 100644 index 0000000..8b10cb8 --- /dev/null +++ b/book/v3/exception/exception_test.go @@ -0,0 +1,24 @@ +package exception_test + +import ( + "testing" + + "122.51.31.227/go-course/go18/book/v3/exception" +) + +func CheckIsError() error { + return exception.ErrNotFound("book %d not found", 1) +} + +func TestException(t *testing.T) { + err := CheckIsError() + t.Log(err) + + // 怎么获取ErrorCode, 断言这个接口的对象的具体类型 + if v, ok := err.(*exception.ApiException); ok { + t.Log(v.Code) + t.Log(v.String()) + } + + t.Log(exception.IsApiException(err, exception.CODE_NOT_FOUND)) +}