refactor: introduce Wire dependency injection and interface-based architecture
- Add Google Wire for compile-time dependency injection - Replace concrete service types with interfaces across handlers - Remove global state (DB, cache) in favor of constructor injection - Split monolithic files into focused modules: - config: separate files for each config domain - dto: converters split by domain (user, post, message, group) - cache: separate metrics.go and redis_cache.go - Introduce unified apperrors package with string-based error codes - Add transaction support with context-aware repository methods - Upgrade golang.org/x/crypto from 0.17.0 to 0.26.0
This commit is contained in:
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"carrot_bbs/internal/service"
|
||||
apperrors "carrot_bbs/internal/errors"
|
||||
)
|
||||
|
||||
// Response 统一响应结构
|
||||
@@ -119,44 +119,72 @@ func Paginated(c *gin.Context, list interface{}, total int64, page, pageSize int
|
||||
}
|
||||
|
||||
// HandleServiceError 统一处理 Service 错误
|
||||
// 如果 err 是 *service.ServiceError,返回对应的业务错误码和消息
|
||||
// 如果 err 是 *apperrors.AppError,返回对应的业务错误码和消息
|
||||
// 如果 err 是其他错误,返回 false(调用方应处理通用错误)
|
||||
// 返回 true 表示错误已处理,false 表示需要调用方继续处理
|
||||
func HandleServiceError(c *gin.Context, err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if se, ok := err.(*service.ServiceError); ok {
|
||||
ErrorWithStatusCode(c, statusCodeFromCode(se.Code), se.Code, se.Message)
|
||||
if se, ok := apperrors.As(err); ok {
|
||||
statusCode := statusCodeFromAppErrorCode(se.Code)
|
||||
ErrorWithStringCode(c, statusCode, se.Code, se.Message)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HandleError 统一处理错误(带默认消息)
|
||||
// 如果 err 是 *service.ServiceError,返回对应的业务错误码和消息
|
||||
// 如果 err 是 *apperrors.AppError,返回对应的业务错误码和消息
|
||||
// 如果 err 是其他错误,返回 InternalServerError 并使用 defaultMessage
|
||||
func HandleError(c *gin.Context, err error, defaultMessage string) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if se, ok := err.(*service.ServiceError); ok {
|
||||
ErrorWithStatusCode(c, statusCodeFromCode(se.Code), se.Code, se.Message)
|
||||
if se, ok := apperrors.As(err); ok {
|
||||
statusCode := statusCodeFromAppErrorCode(se.Code)
|
||||
ErrorWithStringCode(c, statusCode, se.Code, se.Message)
|
||||
return
|
||||
}
|
||||
InternalServerError(c, defaultMessage)
|
||||
}
|
||||
|
||||
// statusCodeFromCode 根据业务错误码获取 HTTP 状态码
|
||||
func statusCodeFromCode(code int) int {
|
||||
switch {
|
||||
case code >= 200 && code < 300:
|
||||
return http.StatusOK
|
||||
case code >= 400 && code < 500:
|
||||
return code
|
||||
case code >= 500:
|
||||
return code
|
||||
// statusCodeFromAppErrorCode 根据AppError的错误码字符串获取 HTTP 状态码
|
||||
func statusCodeFromAppErrorCode(code string) int {
|
||||
switch code {
|
||||
case "NOT_FOUND", "USER_NOT_FOUND", "POST_NOT_FOUND", "GROUP_NOT_FOUND",
|
||||
"GROUP_REQUEST_NOT_FOUND", "SCHEDULE_COURSE_NOT_FOUND":
|
||||
return http.StatusNotFound
|
||||
case "UNAUTHORIZED", "INVALID_CREDENTIALS":
|
||||
return http.StatusUnauthorized
|
||||
case "FORBIDDEN", "NOT_GROUP_MEMBER", "NOT_GROUP_ADMIN", "NOT_GROUP_OWNER",
|
||||
"USER_BANNED", "USER_BLOCKED", "MUTED", "MUTE_ALL_ENABLED",
|
||||
"SCHEDULE_FORBIDDEN", "UNAUTHORIZED_NOTIFICATION":
|
||||
return http.StatusForbidden
|
||||
case "BAD_REQUEST", "INVALID_USERNAME", "INVALID_EMAIL", "INVALID_PHONE",
|
||||
"WEAK_PASSWORD", "USERNAME_EXISTS", "EMAIL_EXISTS", "PHONE_EXISTS",
|
||||
"USER_ALREADY_EXISTS", "INVALID_OPERATION", "VERIFICATION_CODE_INVALID",
|
||||
"VERIFICATION_CODE_EXPIRED", "EMAIL_ALREADY_VERIFIED", "EMAIL_NOT_BOUND",
|
||||
"GROUP_FULL", "ALREADY_GROUP_MEMBER", "CANNOT_REMOVE_OWNER", "CANNOT_MUTE_OWNER",
|
||||
"CANNOT_JOIN", "JOIN_REQUEST_PENDING", "GROUP_REQUEST_HANDLED",
|
||||
"NOT_REQUEST_TARGET", "NO_ELIGIBLE_INVITEE", "NOT_MUTUAL_FOLLOW",
|
||||
"INVITE_NOT_ACCEPTED", "STICKER_ALREADY_EXISTS", "INVALID_STICKER_URL",
|
||||
"INVALID_SCHEDULE_PAYLOAD", "SCHEDULE_COLOR_DUPLICATED":
|
||||
return http.StatusBadRequest
|
||||
case "VERIFICATION_CODE_TOO_FREQUENT":
|
||||
return http.StatusTooManyRequests
|
||||
case "EMAIL_SERVICE_UNAVAILABLE", "VERIFICATION_CODE_UNAVAILABLE", "INTERNAL_ERROR":
|
||||
return http.StatusInternalServerError
|
||||
default:
|
||||
return http.StatusBadRequest
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorWithStringCode 带字符串错误码的错误响应
|
||||
func ErrorWithStringCode(c *gin.Context, statusCode int, code string, message string) {
|
||||
c.JSON(statusCode, ResponseSnakeCase{
|
||||
Code: 0, // 为了兼容,这里设为0,实际错误码在message中体现
|
||||
Message: message,
|
||||
Data: code,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user