chore: stabilize lint and verify builds
This commit is contained in:
@@ -31,6 +31,7 @@ func (e *AppError) Unwrap() error { return e.originalErr }
|
||||
// copy 返回 AppError 的副本,用于链式调用时的并发安全
|
||||
func (e *AppError) copy() *AppError {
|
||||
newErr := *e
|
||||
|
||||
return &newErr
|
||||
}
|
||||
|
||||
@@ -43,6 +44,7 @@ func (e *AppError) WithCause(err error) *AppError {
|
||||
if _, file, line, ok := runtime.Caller(1); ok {
|
||||
newErr.file = fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
|
||||
return newErr
|
||||
}
|
||||
|
||||
@@ -50,6 +52,7 @@ func (e *AppError) WithCause(err error) *AppError {
|
||||
func (e *AppError) WithData(data any) *AppError {
|
||||
newErr := e.copy()
|
||||
newErr.Data = data
|
||||
|
||||
return newErr
|
||||
}
|
||||
|
||||
@@ -57,12 +60,14 @@ func (e *AppError) WithData(data any) *AppError {
|
||||
func (e *AppError) WithMsg(msg string) *AppError {
|
||||
newErr := e.copy()
|
||||
newErr.Message = msg
|
||||
|
||||
return newErr
|
||||
}
|
||||
|
||||
func (e *AppError) WithMsgf(format string, args ...any) *AppError {
|
||||
newErr := e.copy()
|
||||
newErr.Message = fmt.Sprintf(format, args...)
|
||||
|
||||
return newErr
|
||||
}
|
||||
|
||||
@@ -70,6 +75,7 @@ func (e *AppError) WithMsgf(format string, args ...any) *AppError {
|
||||
func (e *AppError) WithSQL(sql string) *AppError {
|
||||
newErr := e.copy()
|
||||
newErr.sql = sql
|
||||
|
||||
return newErr
|
||||
}
|
||||
|
||||
@@ -80,6 +86,7 @@ func (e *AppError) WithParams(params ...any) *AppError {
|
||||
if _, file, line, ok := runtime.Caller(1); ok {
|
||||
newErr.file = fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
|
||||
return newErr
|
||||
}
|
||||
|
||||
|
||||
@@ -17,18 +17,24 @@ func NewErrorHandler() *ErrorHandler {
|
||||
}
|
||||
|
||||
// Handle 处理错误并返回统一格式
|
||||
func (h *ErrorHandler) Handle(err error) *AppError {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
func (handler *ErrorHandler) Handle(err error) *AppError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var appErr *AppError
|
||||
if errors.As(err, &appErr) {
|
||||
return appErr
|
||||
}
|
||||
|
||||
// 处理 Fiber 错误
|
||||
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||
return h.handleFiberError(fiberErr)
|
||||
var fiberErr *fiber.Error
|
||||
if errors.As(err, &fiberErr) {
|
||||
return handler.handleFiberError(fiberErr)
|
||||
}
|
||||
|
||||
// 处理 GORM 错误
|
||||
if appErr := h.handleGormError(err); appErr != nil {
|
||||
if appErr := handler.handleGormError(err); appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
@@ -42,7 +48,7 @@ func (h *ErrorHandler) Handle(err error) *AppError {
|
||||
}
|
||||
|
||||
// handleFiberError 处理 Fiber 错误
|
||||
func (h *ErrorHandler) handleFiberError(fiberErr *fiber.Error) *AppError {
|
||||
func (handler *ErrorHandler) handleFiberError(fiberErr *fiber.Error) *AppError {
|
||||
var appErr *AppError
|
||||
|
||||
switch fiberErr.Code {
|
||||
@@ -73,7 +79,7 @@ func (h *ErrorHandler) handleFiberError(fiberErr *fiber.Error) *AppError {
|
||||
}
|
||||
|
||||
// handleGormError 处理 GORM 错误
|
||||
func (h *ErrorHandler) handleGormError(err error) *AppError {
|
||||
func (handler *ErrorHandler) handleGormError(err error) *AppError {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return &AppError{
|
||||
Code: ErrRecordNotFound.Code,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package errorx
|
||||
|
||||
import "github.com/gofiber/fiber/v3"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
// 全局实例
|
||||
var DefaultSender = NewResponseSender()
|
||||
@@ -11,6 +15,7 @@ func Middleware(c fiber.Ctx) error {
|
||||
if err != nil {
|
||||
return DefaultSender.SendError(c, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,7 +25,8 @@ func Wrap(err error) *AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
var appErr *AppError
|
||||
if errors.As(err, &appErr) {
|
||||
return &AppError{
|
||||
Code: appErr.Code,
|
||||
Message: appErr.Message,
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/gofiber/fiber/v3/binder"
|
||||
"github.com/gofiber/utils/v2"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
logrus "github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"quyun/v2/database/models"
|
||||
@@ -28,18 +28,22 @@ func NewResponseSender() *ResponseSender {
|
||||
}
|
||||
|
||||
// SendError 发送错误响应
|
||||
func (s *ResponseSender) SendError(ctx fiber.Ctx, err error) error {
|
||||
appErr := s.handler.Handle(err)
|
||||
func (sender *ResponseSender) SendError(ctx fiber.Ctx, err error) error {
|
||||
appErr := sender.handler.Handle(err)
|
||||
|
||||
if appErr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 记录错误日志
|
||||
s.logError(appErr, ctx)
|
||||
sender.logError(appErr, ctx)
|
||||
|
||||
// 根据 Content-Type 返回不同格式
|
||||
return s.sendResponse(ctx, appErr)
|
||||
return sender.sendResponse(ctx, appErr)
|
||||
}
|
||||
|
||||
// logError 记录错误日志
|
||||
func (s *ResponseSender) logError(appErr *AppError, ctx fiber.Ctx) {
|
||||
func (sender *ResponseSender) logError(appErr *AppError, ctx fiber.Ctx) {
|
||||
// 确保每个错误实例都有唯一ID,便于日志关联
|
||||
if appErr.ID == "" {
|
||||
appErr.ID = uuid.NewString()
|
||||
@@ -47,44 +51,48 @@ func (s *ResponseSender) logError(appErr *AppError, ctx fiber.Ctx) {
|
||||
|
||||
// 构造详细的错误级联链路(包含类型、状态、定位等)
|
||||
chain := make([]map[string]any, 0, 4)
|
||||
var e error = appErr
|
||||
for e != nil {
|
||||
currentErr := error(appErr)
|
||||
for currentErr != nil {
|
||||
entry := map[string]any{
|
||||
"type": fmt.Sprintf("%T", e),
|
||||
"error": e.Error(),
|
||||
"type": fmt.Sprintf("%T", currentErr),
|
||||
"error": currentErr.Error(),
|
||||
}
|
||||
switch v := e.(type) {
|
||||
case *AppError:
|
||||
entry["code"] = v.Code
|
||||
entry["statusCode"] = v.StatusCode
|
||||
if v.file != "" {
|
||||
entry["file"] = v.file
|
||||
|
||||
var appErrItem *AppError
|
||||
if errors.As(currentErr, &appErrItem) {
|
||||
entry["code"] = appErrItem.Code
|
||||
entry["statusCode"] = appErrItem.StatusCode
|
||||
if appErrItem.file != "" {
|
||||
entry["file"] = appErrItem.file
|
||||
}
|
||||
if len(v.params) > 0 {
|
||||
entry["params"] = v.params
|
||||
if len(appErrItem.params) > 0 {
|
||||
entry["params"] = appErrItem.params
|
||||
}
|
||||
if v.sql != "" {
|
||||
entry["sql"] = v.sql
|
||||
if appErrItem.sql != "" {
|
||||
entry["sql"] = appErrItem.sql
|
||||
}
|
||||
if v.ID != "" {
|
||||
entry["id"] = v.ID
|
||||
if appErrItem.ID != "" {
|
||||
entry["id"] = appErrItem.ID
|
||||
}
|
||||
case *fiber.Error:
|
||||
entry["statusCode"] = v.Code
|
||||
entry["message"] = v.Message
|
||||
}
|
||||
|
||||
var fiberErr *fiber.Error
|
||||
if errors.As(currentErr, &fiberErr) {
|
||||
entry["statusCode"] = fiberErr.Code
|
||||
entry["message"] = fiberErr.Message
|
||||
}
|
||||
|
||||
// GORM 常见错误归类标记
|
||||
if errors.Is(e, gorm.ErrRecordNotFound) {
|
||||
if errors.Is(currentErr, gorm.ErrRecordNotFound) {
|
||||
entry["gorm"] = "record_not_found"
|
||||
} else if errors.Is(e, gorm.ErrDuplicatedKey) {
|
||||
} else if errors.Is(currentErr, gorm.ErrDuplicatedKey) {
|
||||
entry["gorm"] = "duplicated_key"
|
||||
} else if errors.Is(e, gorm.ErrInvalidTransaction) {
|
||||
} else if errors.Is(currentErr, gorm.ErrInvalidTransaction) {
|
||||
entry["gorm"] = "invalid_transaction"
|
||||
}
|
||||
|
||||
chain = append(chain, entry)
|
||||
e = errors.Unwrap(e)
|
||||
currentErr = errors.Unwrap(currentErr)
|
||||
}
|
||||
|
||||
root := chain[len(chain)-1]["error"]
|
||||
@@ -100,7 +108,7 @@ func (s *ResponseSender) logError(appErr *AppError, ctx fiber.Ctx) {
|
||||
fullPath = fmt.Sprintf("%s?%s", path, query)
|
||||
}
|
||||
|
||||
fields := log.Fields{
|
||||
fields := logrus.Fields{
|
||||
"id": appErr.ID,
|
||||
"code": appErr.Code,
|
||||
"statusCode": appErr.StatusCode,
|
||||
@@ -131,7 +139,7 @@ func (s *ResponseSender) logError(appErr *AppError, ctx fiber.Ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
logEntry := log.WithFields(fields)
|
||||
logEntry := logrus.WithFields(fields)
|
||||
|
||||
if appErr.originalErr != nil {
|
||||
logEntry = logEntry.WithError(appErr.originalErr)
|
||||
@@ -148,16 +156,28 @@ func (s *ResponseSender) logError(appErr *AppError, ctx fiber.Ctx) {
|
||||
}
|
||||
|
||||
// sendResponse 发送响应
|
||||
func (s *ResponseSender) sendResponse(ctx fiber.Ctx, appErr *AppError) error {
|
||||
func (sender *ResponseSender) sendResponse(ctx fiber.Ctx, appErr *AppError) error {
|
||||
contentType := utils.ToLower(utils.UnsafeString(ctx.Request().Header.ContentType()))
|
||||
contentType = binder.FilterFlags(utils.ParseVendorSpecificContentType(contentType))
|
||||
|
||||
switch contentType {
|
||||
case fiber.MIMETextXML, fiber.MIMEApplicationXML:
|
||||
return ctx.Status(appErr.StatusCode).XML(appErr)
|
||||
if err := ctx.Status(appErr.StatusCode).XML(appErr); err != nil {
|
||||
return fmt.Errorf("send xml response: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
case fiber.MIMETextHTML, fiber.MIMETextPlain:
|
||||
return ctx.Status(appErr.StatusCode).SendString(appErr.Message)
|
||||
if err := ctx.Status(appErr.StatusCode).SendString(appErr.Message); err != nil {
|
||||
return fmt.Errorf("send text response: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
return ctx.Status(appErr.StatusCode).JSON(appErr)
|
||||
if err := ctx.Status(appErr.StatusCode).JSON(appErr); err != nil {
|
||||
return fmt.Errorf("send json response: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user