2025-06-30 19:21:56 +08:00
|
|
|
|
package http
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"math"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"tyapi-server/internal/shared/interfaces"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ResponseBuilder 响应构建器实现
|
|
|
|
|
|
type ResponseBuilder struct{}
|
|
|
|
|
|
|
|
|
|
|
|
// NewResponseBuilder 创建响应构建器
|
|
|
|
|
|
func NewResponseBuilder() interfaces.ResponseBuilder {
|
|
|
|
|
|
return &ResponseBuilder{}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Success 成功响应
|
|
|
|
|
|
func (r *ResponseBuilder) Success(c *gin.Context, data interface{}, message ...string) {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
msg := "操作成功"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if len(message) > 0 && message[0] != "" {
|
|
|
|
|
|
msg = message[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: true,
|
|
|
|
|
|
Message: msg,
|
|
|
|
|
|
Data: data,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Created 创建成功响应
|
|
|
|
|
|
func (r *ResponseBuilder) Created(c *gin.Context, data interface{}, message ...string) {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
msg := "创建成功"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if len(message) > 0 && message[0] != "" {
|
|
|
|
|
|
msg = message[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: true,
|
|
|
|
|
|
Message: msg,
|
|
|
|
|
|
Data: data,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusCreated, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Error 错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) Error(c *gin.Context, err error) {
|
|
|
|
|
|
// 根据错误类型确定状态码
|
|
|
|
|
|
statusCode := http.StatusInternalServerError
|
2025-07-02 16:17:59 +08:00
|
|
|
|
message := "服务器内部错误"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
errorDetail := err.Error()
|
|
|
|
|
|
|
|
|
|
|
|
// 这里可以根据不同的错误类型设置不同的状态码
|
|
|
|
|
|
// 例如:ValidationError -> 400, NotFoundError -> 404, etc.
|
|
|
|
|
|
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
Message: message,
|
|
|
|
|
|
Errors: errorDetail,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(statusCode, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// BadRequest 400错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) BadRequest(c *gin.Context, message string, errors ...interface{}) {
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
Message: message,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(errors) > 0 {
|
|
|
|
|
|
response.Errors = errors[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusBadRequest, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Unauthorized 401错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) Unauthorized(c *gin.Context, message ...string) {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
msg := "用户未登录或认证已过期"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if len(message) > 0 && message[0] != "" {
|
|
|
|
|
|
msg = message[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
Message: msg,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusUnauthorized, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Forbidden 403错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) Forbidden(c *gin.Context, message ...string) {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
msg := "权限不足,无法访问此资源"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if len(message) > 0 && message[0] != "" {
|
|
|
|
|
|
msg = message[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
Message: msg,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusForbidden, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NotFound 404错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) NotFound(c *gin.Context, message ...string) {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
msg := "请求的资源不存在"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if len(message) > 0 && message[0] != "" {
|
|
|
|
|
|
msg = message[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
Message: msg,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusNotFound, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Conflict 409错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) Conflict(c *gin.Context, message string) {
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
Message: message,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusConflict, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// InternalError 500错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) InternalError(c *gin.Context, message ...string) {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
msg := "服务器内部错误"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if len(message) > 0 && message[0] != "" {
|
|
|
|
|
|
msg = message[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
Message: msg,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Paginated 分页响应
|
|
|
|
|
|
func (r *ResponseBuilder) Paginated(c *gin.Context, data interface{}, pagination interfaces.PaginationMeta) {
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: true,
|
2025-07-02 16:17:59 +08:00
|
|
|
|
Message: "查询成功",
|
2025-06-30 19:21:56 +08:00
|
|
|
|
Data: data,
|
|
|
|
|
|
Pagination: &pagination,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// getRequestID 从上下文获取请求ID
|
|
|
|
|
|
func (r *ResponseBuilder) getRequestID(c *gin.Context) string {
|
|
|
|
|
|
if requestID, exists := c.Get("request_id"); exists {
|
|
|
|
|
|
if id, ok := requestID.(string); ok {
|
|
|
|
|
|
return id
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// BuildPagination 构建分页元数据
|
|
|
|
|
|
func BuildPagination(page, pageSize int, total int64) interfaces.PaginationMeta {
|
|
|
|
|
|
totalPages := int(math.Ceil(float64(total) / float64(pageSize)))
|
|
|
|
|
|
|
|
|
|
|
|
if totalPages < 1 {
|
|
|
|
|
|
totalPages = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return interfaces.PaginationMeta{
|
|
|
|
|
|
Page: page,
|
|
|
|
|
|
PageSize: pageSize,
|
|
|
|
|
|
Total: total,
|
|
|
|
|
|
TotalPages: totalPages,
|
|
|
|
|
|
HasNext: page < totalPages,
|
|
|
|
|
|
HasPrev: page > 1,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CustomResponse 自定义响应
|
|
|
|
|
|
func (r *ResponseBuilder) CustomResponse(c *gin.Context, statusCode int, data interface{}) {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
var message string
|
|
|
|
|
|
switch statusCode {
|
|
|
|
|
|
case http.StatusOK:
|
|
|
|
|
|
message = "请求成功"
|
|
|
|
|
|
case http.StatusCreated:
|
|
|
|
|
|
message = "创建成功"
|
|
|
|
|
|
case http.StatusNoContent:
|
|
|
|
|
|
message = "无内容"
|
|
|
|
|
|
case http.StatusBadRequest:
|
|
|
|
|
|
message = "请求参数错误"
|
|
|
|
|
|
case http.StatusUnauthorized:
|
|
|
|
|
|
message = "认证失败"
|
|
|
|
|
|
case http.StatusForbidden:
|
|
|
|
|
|
message = "权限不足"
|
|
|
|
|
|
case http.StatusNotFound:
|
|
|
|
|
|
message = "资源不存在"
|
|
|
|
|
|
case http.StatusConflict:
|
|
|
|
|
|
message = "资源冲突"
|
|
|
|
|
|
case http.StatusTooManyRequests:
|
|
|
|
|
|
message = "请求过于频繁"
|
|
|
|
|
|
case http.StatusInternalServerError:
|
|
|
|
|
|
message = "服务器内部错误"
|
|
|
|
|
|
default:
|
|
|
|
|
|
message = "未知状态"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-30 19:21:56 +08:00
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: statusCode >= 200 && statusCode < 300,
|
2025-07-02 16:17:59 +08:00
|
|
|
|
Message: message,
|
2025-06-30 19:21:56 +08:00
|
|
|
|
Data: data,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(statusCode, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ValidationError 验证错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) ValidationError(c *gin.Context, errors interface{}) {
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
2025-07-02 16:17:59 +08:00
|
|
|
|
Message: "请求参数验证失败",
|
2025-06-30 19:21:56 +08:00
|
|
|
|
Errors: errors,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusUnprocessableEntity, response)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TooManyRequests 限流错误响应
|
|
|
|
|
|
func (r *ResponseBuilder) TooManyRequests(c *gin.Context, message ...string) {
|
2025-07-02 16:17:59 +08:00
|
|
|
|
msg := "请求过于频繁,请稍后再试"
|
2025-06-30 19:21:56 +08:00
|
|
|
|
if len(message) > 0 && message[0] != "" {
|
|
|
|
|
|
msg = message[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := interfaces.APIResponse{
|
|
|
|
|
|
Success: false,
|
|
|
|
|
|
Message: msg,
|
|
|
|
|
|
RequestID: r.getRequestID(c),
|
|
|
|
|
|
Timestamp: time.Now().Unix(),
|
|
|
|
|
|
Meta: map[string]interface{}{
|
|
|
|
|
|
"retry_after": "60s",
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusTooManyRequests, response)
|
|
|
|
|
|
}
|