Add verification status checks to protected routes including post creation, updates, deletion, likes, favorites, voting, and WebSocket connections. Also rename RequireVerification middleware to RequireVerified and update error response format with string error codes.
193 lines
5.5 KiB
Go
193 lines
5.5 KiB
Go
package response
|
||
|
||
import (
|
||
"net/http"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
|
||
apperrors "carrot_bbs/internal/errors"
|
||
)
|
||
|
||
// Response 统一响应结构
|
||
type Response struct {
|
||
Code int `json:"code"`
|
||
Message string `json:"message"`
|
||
Data any `json:"data,omitempty"`
|
||
}
|
||
|
||
// ResponseSnakeCase 统一响应结构(snake_case)
|
||
type ResponseSnakeCase struct {
|
||
Code int `json:"code"`
|
||
Message string `json:"message"`
|
||
Data any `json:"data,omitempty"`
|
||
}
|
||
|
||
// Success 成功响应
|
||
func Success(c *gin.Context, data any) {
|
||
c.JSON(http.StatusOK, Response{
|
||
Code: 0,
|
||
Message: "success",
|
||
Data: data,
|
||
})
|
||
}
|
||
|
||
// SuccessWithMessage 成功响应带消息
|
||
func SuccessWithMessage(c *gin.Context, message string, data any) {
|
||
c.JSON(http.StatusOK, Response{
|
||
Code: 0,
|
||
Message: message,
|
||
Data: data,
|
||
})
|
||
}
|
||
|
||
// Error 错误响应
|
||
func Error(c *gin.Context, code int, message string) {
|
||
c.JSON(http.StatusBadRequest, Response{
|
||
Code: code,
|
||
Message: message,
|
||
})
|
||
}
|
||
|
||
// ErrorWithStatusCode 带状态码的错误响应
|
||
func ErrorWithStatusCode(c *gin.Context, statusCode int, code int, message string) {
|
||
c.JSON(statusCode, Response{
|
||
Code: code,
|
||
Message: message,
|
||
})
|
||
}
|
||
|
||
// BadRequest 参数错误
|
||
func BadRequest(c *gin.Context, message string) {
|
||
ErrorWithStatusCode(c, http.StatusBadRequest, 400, message)
|
||
}
|
||
|
||
// Unauthorized 未授权
|
||
func Unauthorized(c *gin.Context, message string) {
|
||
if message == "" {
|
||
message = "unauthorized"
|
||
}
|
||
ErrorWithStatusCode(c, http.StatusUnauthorized, 401, message)
|
||
}
|
||
|
||
// Forbidden 禁止访问
|
||
func Forbidden(c *gin.Context, message string) {
|
||
if message == "" {
|
||
message = "forbidden"
|
||
}
|
||
ErrorWithStatusCode(c, http.StatusForbidden, 403, message)
|
||
}
|
||
|
||
// NotFound 资源不存在
|
||
func NotFound(c *gin.Context, message string) {
|
||
if message == "" {
|
||
message = "resource not found"
|
||
}
|
||
ErrorWithStatusCode(c, http.StatusNotFound, 404, message)
|
||
}
|
||
|
||
// InternalServerError 服务器内部错误
|
||
func InternalServerError(c *gin.Context, message string) {
|
||
if message == "" {
|
||
message = "internal server error"
|
||
}
|
||
ErrorWithStatusCode(c, http.StatusInternalServerError, 500, message)
|
||
}
|
||
|
||
// PaginatedResponse 分页响应
|
||
type PaginatedResponse struct {
|
||
List any `json:"list"`
|
||
Total int64 `json:"total"`
|
||
Page int `json:"page"`
|
||
PageSize int `json:"page_size"`
|
||
TotalPages int `json:"total_pages"`
|
||
}
|
||
|
||
// Paginated 分页成功响应
|
||
func Paginated(c *gin.Context, list any, total int64, page, pageSize int) {
|
||
totalPages := int(total) / pageSize
|
||
if int(total)%pageSize > 0 {
|
||
totalPages++
|
||
}
|
||
|
||
Success(c, PaginatedResponse{
|
||
List: list,
|
||
Total: total,
|
||
Page: page,
|
||
PageSize: pageSize,
|
||
TotalPages: totalPages,
|
||
})
|
||
}
|
||
|
||
// HandleServiceError 统一处理 Service 错误
|
||
// 如果 err 是 *apperrors.AppError,返回对应的业务错误码和消息
|
||
// 如果 err 是其他错误,返回 false(调用方应处理通用错误)
|
||
// 返回 true 表示错误已处理,false 表示需要调用方继续处理
|
||
func HandleServiceError(c *gin.Context, err error) bool {
|
||
if err == nil {
|
||
return false
|
||
}
|
||
if se, ok := apperrors.As(err); ok {
|
||
statusCode := statusCodeFromAppErrorCode(se.Code)
|
||
ErrorWithStringCode(c, statusCode, se.Code, se.Message)
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
// HandleError 统一处理错误(带默认消息)
|
||
// 如果 err 是 *apperrors.AppError,返回对应的业务错误码和消息
|
||
// 如果 err 是其他错误,返回 InternalServerError 并使用 defaultMessage
|
||
func HandleError(c *gin.Context, err error, defaultMessage string) {
|
||
if err == nil {
|
||
return
|
||
}
|
||
if se, ok := apperrors.As(err); ok {
|
||
statusCode := statusCodeFromAppErrorCode(se.Code)
|
||
ErrorWithStringCode(c, statusCode, se.Code, se.Message)
|
||
return
|
||
}
|
||
InternalServerError(c, defaultMessage)
|
||
}
|
||
|
||
// 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":
|
||
return http.StatusUnauthorized
|
||
case "INVALID_CREDENTIALS":
|
||
return http.StatusBadRequest
|
||
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, gin.H{
|
||
"code": statusCode,
|
||
"message": message,
|
||
"error_code": code,
|
||
})
|
||
}
|