Files
sub2api/backend/internal/pkg/response/response.go
shaw 2028cc29b7 fix: 修复多个管理后台问题
- 分页接口 page_size 最大限制从 100 改为 1000
- 通过 Redis Pub/Sub 实现跨实例认证缓存失效
- 允许订阅类型分组编辑计费倍率
- 账号计费倍率支持 3 位小数
2026-01-18 22:13:47 +08:00

187 lines
4.3 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 provides standardized HTTP response helpers.
package response
import (
"math"
"net/http"
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
"github.com/gin-gonic/gin"
)
// Response 标准API响应格式
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Reason string `json:"reason,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
Data any `json:"data,omitempty"`
}
// PaginatedData 分页数据格式(匹配前端期望)
type PaginatedData struct {
Items any `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Pages int `json:"pages"`
}
// Success 返回成功响应
func Success(c *gin.Context, data any) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: "success",
Data: data,
})
}
// Created 返回创建成功响应
func Created(c *gin.Context, data any) {
c.JSON(http.StatusCreated, Response{
Code: 0,
Message: "success",
Data: data,
})
}
// Error 返回错误响应
func Error(c *gin.Context, statusCode int, message string) {
c.JSON(statusCode, Response{
Code: statusCode,
Message: message,
Reason: "",
Metadata: nil,
})
}
// ErrorWithDetails returns an error response compatible with the existing envelope while
// optionally providing structured error fields (reason/metadata).
func ErrorWithDetails(c *gin.Context, statusCode int, message, reason string, metadata map[string]string) {
c.JSON(statusCode, Response{
Code: statusCode,
Message: message,
Reason: reason,
Metadata: metadata,
})
}
// ErrorFrom converts an ApplicationError (or any error) into the envelope-compatible error response.
// It returns true if an error was written.
func ErrorFrom(c *gin.Context, err error) bool {
if err == nil {
return false
}
statusCode, status := infraerrors.ToHTTP(err)
ErrorWithDetails(c, statusCode, status.Message, status.Reason, status.Metadata)
return true
}
// BadRequest 返回400错误
func BadRequest(c *gin.Context, message string) {
Error(c, http.StatusBadRequest, message)
}
// Unauthorized 返回401错误
func Unauthorized(c *gin.Context, message string) {
Error(c, http.StatusUnauthorized, message)
}
// Forbidden 返回403错误
func Forbidden(c *gin.Context, message string) {
Error(c, http.StatusForbidden, message)
}
// NotFound 返回404错误
func NotFound(c *gin.Context, message string) {
Error(c, http.StatusNotFound, message)
}
// InternalError 返回500错误
func InternalError(c *gin.Context, message string) {
Error(c, http.StatusInternalServerError, message)
}
// Paginated 返回分页数据
func Paginated(c *gin.Context, items any, total int64, page, pageSize int) {
pages := int(math.Ceil(float64(total) / float64(pageSize)))
if pages < 1 {
pages = 1
}
Success(c, PaginatedData{
Items: items,
Total: total,
Page: page,
PageSize: pageSize,
Pages: pages,
})
}
// PaginationResult 分页结果与pagination.PaginationResult兼容
type PaginationResult struct {
Total int64
Page int
PageSize int
Pages int
}
// PaginatedWithResult 使用PaginationResult返回分页数据
func PaginatedWithResult(c *gin.Context, items any, pagination *PaginationResult) {
if pagination == nil {
Success(c, PaginatedData{
Items: items,
Total: 0,
Page: 1,
PageSize: 20,
Pages: 1,
})
return
}
Success(c, PaginatedData{
Items: items,
Total: pagination.Total,
Page: pagination.Page,
PageSize: pagination.PageSize,
Pages: pagination.Pages,
})
}
// ParsePagination 解析分页参数
func ParsePagination(c *gin.Context) (page, pageSize int) {
page = 1
pageSize = 20
if p := c.Query("page"); p != "" {
if val, err := parseInt(p); err == nil && val > 0 {
page = val
}
}
// 支持 page_size 和 limit 两种参数名
if ps := c.Query("page_size"); ps != "" {
if val, err := parseInt(ps); err == nil && val > 0 && val <= 1000 {
pageSize = val
}
} else if l := c.Query("limit"); l != "" {
if val, err := parseInt(l); err == nil && val > 0 && val <= 1000 {
pageSize = val
}
}
return page, pageSize
}
func parseInt(s string) (int, error) {
var result int
for _, c := range s {
if c < '0' || c > '9' {
return 0, nil
}
result = result*10 + int(c-'0')
}
return result, nil
}