topfans/backend/gateway/pkg/response/response.go
2026-04-27 12:59:19 +08:00

200 lines
5.6 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 (
"encoding/json"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
// Response 统一响应格式
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// writeJSON 统一输出 JSON并关闭 HTML 转义(避免将 & 转成 \u0026导致预签名 URL 失效)
func writeJSON(c *gin.Context, httpCode int, payload interface{}) {
c.Status(httpCode)
c.Header("Content-Type", "application/json; charset=utf-8")
enc := json.NewEncoder(c.Writer)
enc.SetEscapeHTML(false)
_ = enc.Encode(payload)
}
// Success 成功响应200
func Success(c *gin.Context, data interface{}) {
writeJSON(c, http.StatusOK, Response{
Code: 200,
Message: "ok",
Data: data,
})
}
// SuccessWithMessage 成功响应(自定义消息)
func SuccessWithMessage(c *gin.Context, message string, data interface{}) {
writeJSON(c, http.StatusOK, Response{
Code: 200,
Message: message,
Data: data,
})
}
// Error 错误响应
func Error(c *gin.Context, httpCode int, message string) {
writeJSON(c, httpCode, Response{
Code: httpCode,
Message: message,
})
}
// ErrorWithCode 错误响应(自定义业务错误码)
func ErrorWithCode(c *gin.Context, code int, message string) {
// 根据业务错误码映射到HTTP状态码
httpCode := http.StatusBadRequest
switch code {
case 401:
httpCode = http.StatusUnauthorized
case 403:
httpCode = http.StatusForbidden
case 404:
httpCode = http.StatusNotFound
case 409:
httpCode = http.StatusConflict
case 500:
httpCode = http.StatusInternalServerError
}
writeJSON(c, httpCode, Response{
Code: code,
Message: message,
})
}
// BadRequest 400 错误请求
func BadRequest(c *gin.Context, message string) {
if message == "" {
message = "请求参数错误"
}
Error(c, http.StatusBadRequest, message)
}
// Unauthorized 401 未授权
func Unauthorized(c *gin.Context, message string) {
if message == "" {
message = "未授权,请先登录"
}
Error(c, http.StatusUnauthorized, message)
}
// Forbidden 403 禁止访问
func Forbidden(c *gin.Context, message string) {
if message == "" {
message = "权限不足,禁止访问"
}
Error(c, http.StatusForbidden, message)
}
// NotFound 404 未找到
func NotFound(c *gin.Context, message string) {
if message == "" {
message = "资源不存在"
}
Error(c, http.StatusNotFound, message)
}
// InternalError 500 服务器内部错误
func InternalError(c *gin.Context, message string) {
if message == "" {
message = "服务器内部错误"
}
Error(c, http.StatusInternalServerError, message)
}
// CleanErrorMessage 清理错误消息,转换为中文友好提示
func CleanErrorMessage(err error) string {
if err == nil {
return ""
}
msg := err.Error()
// 移除 gRPC 错误前缀
if idx := strings.Index(msg, "code_"); idx >= 0 {
if colonIdx := strings.Index(msg[idx:], ": "); colonIdx >= 0 {
msg = msg[idx+colonIdx+2:]
}
}
// 中英文错误映射
errorMap := map[string]string{
"user not found": "用户不存在",
"user already exists": "该手机号已被注册",
"invalid password": "密码错误",
"invalid mobile format": "手机号格式不正确",
"password too short": "密码长度不足至少需要6位",
"star not found": "明星不存在",
"fan profile not found": "粉丝档案不存在",
"invalid token": "Token无效",
"token expired": "Token已过期",
"user is inactive": "用户已被禁用",
"maximum number of identities reached": "已达到最大身份数量限制",
"invalid star_id": "明星ID无效",
"invalid user_id": "用户ID无效",
"invalid nickname": "昵称格式不正确",
"nickname too long": "昵称过长最多20个字符",
"nickname too short": "昵称过短至少需要2个字符",
"user info not found in Dubbo attachments": "请先登录",
"missing or invalid authorization token": "Token缺失或无效",
"已达到最大好友数量限制": "已达到最大好友数量限制",
}
msgLower := strings.ToLower(msg)
for eng, chn := range errorMap {
if strings.Contains(msgLower, eng) {
return chn
}
}
// 如果没有匹配,返回原始消息
return msg
}
// HandleError 统一处理错误并返回
func HandleError(c *gin.Context, err error) {
if err == nil {
InternalError(c, "未知错误")
return
}
msg := CleanErrorMessage(err)
// 根据错误类型返回对应的 HTTP 状态码
switch {
case strings.Contains(msg, "not found") || strings.Contains(msg, "不存在"):
NotFound(c, msg)
case strings.Contains(msg, "unauthorized") ||
strings.Contains(msg, "token") ||
strings.Contains(msg, "请先登录"):
Unauthorized(c, msg)
case strings.Contains(msg, "invalid") ||
strings.Contains(msg, "格式不正确") ||
strings.Contains(msg, "过长") ||
strings.Contains(msg, "过短"):
BadRequest(c, msg)
case strings.Contains(msg, "already exists") ||
strings.Contains(msg, "已被注册"):
Error(c, http.StatusConflict, msg)
case strings.Contains(msg, "forbidden") ||
strings.Contains(msg, "permission denied") ||
strings.Contains(msg, "权限不足") ||
strings.Contains(msg, "账号已被冻结") ||
strings.Contains(msg, "账号已被封禁"):
Forbidden(c, msg)
default:
InternalError(c, msg)
}
}