| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // Package response provides standardized HTTP response helpers.
- package response
- import (
- "log"
- "math"
- "net/http"
- infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
- "github.com/Wei-Shaw/sub2api/internal/util/logredact"
- "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,
- })
- }
- // Accepted 返回异步接受响应 (HTTP 202)
- func Accepted(c *gin.Context, data any) {
- c.JSON(http.StatusAccepted, Response{
- Code: 0,
- Message: "accepted",
- 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)
- // Log internal errors with full details for debugging
- if statusCode >= 500 && c.Request != nil {
- log.Printf("[ERROR] %s %s\n Error: %s", c.Request.Method, c.Request.URL.Path, logredact.RedactText(err.Error()))
- }
- 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
- }
|