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": "已达到最大身份数量限制(最多2个)", "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) msgLower := strings.ToLower(msg) // 根据错误类型返回对应的 HTTP 状态码 switch { case strings.Contains(msgLower, "not found") || strings.Contains(msgLower, "不存在"): NotFound(c, msg) case strings.Contains(msgLower, "unauthorized") || strings.Contains(msgLower, "token") || strings.Contains(msgLower, "请先登录"): Unauthorized(c, msg) case strings.Contains(msgLower, "invalid") || strings.Contains(msgLower, "格式不正确") || strings.Contains(msgLower, "过长") || strings.Contains(msgLower, "过短"): BadRequest(c, msg) case strings.Contains(msgLower, "already exists") || strings.Contains(msgLower, "已被注册"): Error(c, http.StatusConflict, msg) case strings.Contains(msgLower, "forbidden") || strings.Contains(msgLower, "permission denied") || strings.Contains(msgLower, "权限不足"): Forbidden(c, msg) default: InternalError(c, msg) } }