Files
backend/internal/pkg/response/response.go
lafay b640c9a249
All checks were successful
Build Backend / build (push) Successful in 3m51s
Build Backend / build-docker (push) Successful in 1m0s
refactor: upgrade to Go 1.26 and modernize code idioms
- Replace `interface{}` with `any` type alias across all packages
- Use built-in `min()`/`max()` for parameter clamping
- Use `slices.SortFunc`, `slices.Min`, `slices.Max` for cleaner code
- Use `strings.Cut()` for simpler string parsing in auth middleware
- Use `errors.Is()` for proper error comparison in handlers
- Update dependencies: golang.org/x/image 0.37.0 -> 0.38.0
- Add Wire code generation guidelines to ARCHITECTURE.md
- Disable Go cache in CI build workflow
2026-03-30 04:49:35 +08:00

193 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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, ResponseSnakeCase{
Code: statusCode,
Message: message,
Data: nil,
})
}