Files
backend/internal/pkg/response/response.go

173 lines
4.9 KiB
Go
Raw Normal View History

package response
import (
"net/http"
"github.com/gin-gonic/gin"
apperrors "with_you/internal/errors"
)
// Response 统一响应结构
type Response 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,
})
}
// 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",
"SETUP_ALREADY_COMPLETED":
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",
"INVALID_SETUP_SECRET", "SETUP_SECRET_NOT_CONFIGURED":
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,
})
}