runningwater 8 mēneši atpakaļ
vecāks
revīzija
dd333f20d7

+ 3 - 2
app/http/controllers/api/v1/auth/signup_controller.go

@@ -5,6 +5,7 @@ import (
 	v1 "github.com/runningwater/gohub/app/http/controllers/api/v1"
 	"github.com/runningwater/gohub/app/models/user"
 	"github.com/runningwater/gohub/app/requests"
+	"github.com/runningwater/gohub/pkg/response"
 )
 
 // SignupController 处理用户注册相关的逻辑
@@ -21,7 +22,7 @@ func (controller *SignupController) IsPhoneExist(c *gin.Context) {
 	}
 
 	// 检查数据库并返回响应
-	c.JSON(200, gin.H{
+	response.JSON(c, gin.H{
 		"exist": user.IsPhoneExist(req.Phone),
 	})
 }
@@ -35,7 +36,7 @@ func (controller *SignupController) IsEmailExist(c *gin.Context) {
 	}
 
 	// 检查数据库并返回响应
-	c.JSON(200, gin.H{
+	response.JSON(c, gin.H{
 		"exist": user.IsEmailExist(req.Email),
 	})
 }

+ 2 - 1
app/http/controllers/api/v1/auth/verify_code_controller.go

@@ -6,6 +6,7 @@ import (
 	v1 "github.com/runningwater/gohub/app/http/controllers/api/v1"
 	"github.com/runningwater/gohub/pkg/captcha"
 	"github.com/runningwater/gohub/pkg/logger"
+	"github.com/runningwater/gohub/pkg/response"
 )
 
 type VerifyCodeController struct {
@@ -19,7 +20,7 @@ func (vc *VerifyCodeController) ShowCaptcha(c *gin.Context) {
 	logger.LogIf(err)
 	logger.DebugString("captchaController", "captcha id: ", id)
 	logger.DebugString("captchaController", "captcha answer: ", answer)
-	c.JSON(200, gin.H{
+	response.JSON(c, gin.H{
 		"captcha_id":    id,
 		"captcha_image": b64s,
 	})

+ 5 - 4
app/http/middlewares/recovery.go

@@ -2,7 +2,6 @@ package middlewares
 
 import (
 	"net"
-	"net/http"
 	"net/http/httputil"
 	"os"
 	"strings"
@@ -10,6 +9,7 @@ import (
 
 	"github.com/gin-gonic/gin"
 	"github.com/runningwater/gohub/pkg/logger"
+	"github.com/runningwater/gohub/pkg/response"
 	"go.uber.org/zap"
 )
 
@@ -52,9 +52,10 @@ func Recovery() gin.HandlerFunc {
 					zap.String("request", string(httpRquest)), // 记录请求信息
 					zap.Stack("stack"),                        // 记录堆栈信息
 				)
-				c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
-					"message": "服务器内部错误,请稍后再试",
-				})
+				// c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
+				// 	"message": "服务器内部错误,请稍后再试",
+				// })
+				response.Abort500(c)
 			}
 		}()
 

+ 11 - 10
app/requests/requests.go

@@ -2,9 +2,8 @@
 package requests
 
 import (
-	"net/http"
-
 	"github.com/gin-gonic/gin"
+	"github.com/runningwater/gohub/pkg/response"
 	"github.com/thedevsaddam/govalidator"
 )
 
@@ -28,10 +27,11 @@ func Validate(c *gin.Context, obj any, handler ValidatorFunc) bool {
 
 	// 1. 解析请求, 支持 JSON 数据、表单请求和 URL Query 参数
 	if err := c.ShouldBind(obj); err != nil {
-		c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
-			"message": "请求解析错误,请确认请求格式是否正确。上传文件请使用 multipart 标头, 参数请使用 JSON 格式",
-			"error":   err.Error(),
-		})
+		response.BadRequest(c, err, "请求解析错误,请确认请求格式是否正确。上传文件请使用 multipart 标头, 参数请使用 JSON 格式")
+		// c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
+		// 	"message": "请求解析错误,请确认请求格式是否正确。上传文件请使用 multipart 标头, 参数请使用 JSON 格式",
+		// 	"error":   err.Error(),
+		// })
 		return false
 	}
 
@@ -40,10 +40,11 @@ func Validate(c *gin.Context, obj any, handler ValidatorFunc) bool {
 
 	// 3. 如果验证失败,返回错误信息
 	if len(errs) > 0 {
-		c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
-			"message": "请求验证不通过,具体请查看 errors",
-			"errors":  errs,
-		})
+		// c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
+		// 	"message": "请求验证不通过,具体请查看 errors",
+		// 	"errors":  errs,
+		// })
+		response.ValidationError(c, errs)
 		return false
 	}
 	// 4. 返回验证成功

+ 132 - 0
pkg/response/response.go

@@ -0,0 +1,132 @@
+// Package response 响应处理工具
+package response
+
+import (
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"github.com/runningwater/gohub/pkg/logger"
+	"gorm.io/gorm"
+)
+
+// JSON 响应 200 和 JSON 数据
+func JSON(c *gin.Context, data any) {
+	c.JSON(http.StatusOK, data)
+}
+
+// Success 响应 200 和预设『操作成功!』的 JSON 数据
+// 执行某个『没有具体返回数据』的『变更』操作成功后调用,例如删除、修改密码、修改手机号
+func Success(c *gin.Context) {
+	JSON(c, gin.H{
+		"success": true,
+		"message": "操作成功!",
+	})
+}
+
+// Data 响应 200 和带 data 键的 JSON 数据
+// 执行『更新操作』成功后调用,例如更新话题,成功后返回已更新的话题
+func Data(c *gin.Context, data any) {
+	JSON(c, gin.H{
+		"success": true,
+		"data":    data,
+	})
+}
+
+// Created 响应 201 和带 data 键的 JSON 数据
+// 执行『更新操作』成功后调用,例如更新话题,成功后返回已更新的话题
+func Created(c *gin.Context, data any) {
+	c.JSON(http.StatusCreated, gin.H{
+		"success": true,
+		"data":    data,
+	})
+}
+
+// CreatedJSON 响应 201 和 JSON 数据
+func CreatedJSON(c *gin.Context, data any) {
+	c.JSON(http.StatusCreated, data)
+}
+
+// Abort404 响应 404,未传参 msg 时使用默认消息
+func Abort404(c *gin.Context, msg ...string) {
+	c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
+		"message": defaultMessage("数据不存在,请确定请求正确", msg...),
+	})
+}
+
+// Abort403 响应 403,未传参 msg 时使用默认消息
+func Abort403(c *gin.Context, msg ...string) {
+	c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
+		"message": defaultMessage("权限不足,请确定您有对应的权限", msg...),
+	})
+}
+
+// Abort500 响应 500,未传参 msg 时使用默认消息
+func Abort500(c *gin.Context, msg ...string) {
+	c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
+		"message": defaultMessage("服务器内部错误,请稍后再试", msg...),
+	})
+}
+
+// BadRequest 响应 400,传参 err 对象,未传参 msg 时使用默认消息
+// 在解析用户请求,请求的格式或者方法不符合预期时调用
+func BadRequest(c *gin.Context, err error, msg ...string) {
+	logger.LogIf(err)
+	c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
+		"message": defaultMessage("请求解析错误,请确认请求格式是否正确。上传文件请使用 multipart 标头,参数请使用 JSON 格式。", msg...),
+		"error":   err.Error(),
+	})
+}
+
+// Error 响应 404 或 422,未传参 msg 时使用默认消息
+// 处理请求时出现错误 err,会附带返回 error 信息,如登录错误、找不到 ID 对应的 Model
+func Error(c *gin.Context, err error, msg ...string) {
+	logger.LogIf(err)
+
+	// error 类型为『数据库未找到内容』
+	if err == gorm.ErrRecordNotFound {
+		Abort404(c)
+		return
+	}
+
+	c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
+		"message": defaultMessage("请求处理失败,请查看 error 的值", msg...),
+		"error":   err.Error(),
+	})
+}
+
+// ValidationError 处理表单验证不通过的错误,返回的 JSON 示例:
+//
+//	{
+//	    "errors": {
+//	        "phone": [
+//	            "手机号为必填项,参数名称 phone",
+//	            "手机号长度必须为 11 位的数字"
+//	        ]
+//	    },
+//	    "message": "请求验证不通过,具体请查看 errors"
+//	}
+func ValidationError(c *gin.Context, errors map[string][]string) {
+	c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
+		"message": "请求验证不通过,具体请查看 errors",
+		"errors":  errors,
+	})
+}
+
+// Unauthorized 响应 401,未传参 msg 时使用默认消息
+// 登录失败、jwt 解析失败时调用
+func Unauthorized(c *gin.Context, msg ...string) {
+	c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
+		"message": defaultMessage("请求解析错误,请确认请求格式是否正确。上传文件请使用 multipart 标头,参数请使用 JSON 格式。", msg...),
+	})
+}
+
+// defaultMessage 内用的辅助函数,用以支持默认参数默认值
+// Go 不支持参数默认值,只能使用多变参数来实现类似效果
+func defaultMessage(defaultMsg string, msg ...string) (message string) {
+	if len(msg) > 0 {
+		message = msg[0]
+	} else {
+		message = defaultMsg
+	}
+	return
+}