198 lines
5.5 KiB
Go
198 lines
5.5 KiB
Go
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)
|
||
}
|
||
}
|