- 全局替换 ApiKey → APIKey(类型、字段、方法、变量) - 修复所有 initialism 命名(API, SMTP, HTML, URL 等) - 添加所有缺失的包注释 - 修复导出符号的注释格式 主要修改: - ApiKey → APIKey(所有出现的地方) - ApiKeyID → APIKeyID - ApiKeyIDs → APIKeyIDs - TestSmtpConnection → TestSMTPConnection - HtmlURL → HTMLURL - 添加 20+ 个包注释 - 修复 10+ 个导出符号注释格式 验证结果: - ✓ golangci-lint: 0 issues - ✓ 单元测试: 通过 - ✓ 集成测试: 通过
187 lines
4.3 KiB
Go
187 lines
4.3 KiB
Go
// 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 <= 100 {
|
||
pageSize = val
|
||
}
|
||
} else if l := c.Query("limit"); l != "" {
|
||
if val, err := parseInt(l); err == nil && val > 0 && val <= 100 {
|
||
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
|
||
}
|