From ff8a8d2b97c37eac22e404fbae58f9f3a9201657 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Thu, 30 Oct 2025 16:35:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BB=BB=E5=8A=A13.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/app/controller/response.go | 17 +++- internal/app/middleware/auth.go | 87 +++++++++---------- .../refactor-migrate-gin-to-echo/tasks.md | 12 +-- 3 files changed, 63 insertions(+), 53 deletions(-) diff --git a/internal/app/controller/response.go b/internal/app/controller/response.go index c16d5ce..651fbb1 100644 --- a/internal/app/controller/response.go +++ b/internal/app/controller/response.go @@ -31,12 +31,13 @@ const ( // Response 定义统一的API响应结构体 type Response struct { - Code ResponseCode `json:"code"` // 业务状态码 - Message string `json:"message"` // 提示信息 - Data interface{} `json:"data"` // 业务数据 + Code ResponseCode `json:"code"` // 业务状态码 + Message string `json:"message"` // 提示信息 + Data interface{} `json:"data,omitempty"` // 业务数据, omitempty表示如果为空则不序列化 } // SendResponse 发送统一格式的JSON响应 (基础函数,不带审计) +// 所有的业务API都应该使用这个函数返回,以确保HTTP状态码始终为200 OK。 func SendResponse(c echo.Context, code ResponseCode, message string, data interface{}) error { return c.JSON(http.StatusOK, Response{ Code: code, @@ -46,10 +47,20 @@ func SendResponse(c echo.Context, code ResponseCode, message string, data interf } // SendErrorResponse 发送统一格式的错误响应 (基础函数,不带审计) +// HTTP状态码为200 OK,通过业务码表示错误。 func SendErrorResponse(c echo.Context, code ResponseCode, message string) error { return SendResponse(c, code, message, nil) } +// SendErrorWithStatus 发送带有指定HTTP状态码的错误响应。 +// 这个函数主要用于中间件或特殊场景(如认证失败),在这些场景下需要返回非200的HTTP状态码。 +func SendErrorWithStatus(c echo.Context, httpStatus int, code ResponseCode, message string) error { + return c.JSON(httpStatus, Response{ + Code: code, + Message: message, + }) +} + // --- 带审计功能的响应函数 --- // setAuditDetails 是一个内部辅助函数,用于在 echo.Context 中设置业务相关的审计信息。 diff --git a/internal/app/middleware/auth.go b/internal/app/middleware/auth.go index d0fbdc1..57a757d 100644 --- a/internal/app/middleware/auth.go +++ b/internal/app/middleware/auth.go @@ -1,61 +1,60 @@ -// Package middleware 存放 gin 中间件 +// Package middleware 存放中间件 package middleware import ( + "errors" "net/http" "strings" + "git.huangwc.com/pig/pig-farm-controller/internal/app/controller" "git.huangwc.com/pig/pig-farm-controller/internal/domain/token" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" - "github.com/gin-gonic/gin" + "github.com/labstack/echo/v4" "gorm.io/gorm" ) -// AuthMiddleware 创建一个Gin中间件,用于JWT身份验证 +// AuthMiddleware 创建一个Echo中间件,用于JWT身份验证 // 它依赖于 TokenService 来解析和验证 token,并使用 UserRepository 来获取完整的用户信息 -func AuthMiddleware(tokenService token.Service, userRepo repository.UserRepository) gin.HandlerFunc { - return func(c *gin.Context) { - // 从 Authorization header 获取 token - authHeader := c.GetHeader("Authorization") - if authHeader == "" { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "请求未包含授权标头"}) - return - } - - // 授权标头的格式应为 "Bearer " - parts := strings.Split(authHeader, " ") - if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "授权标头格式不正确"}) - return - } - - tokenString := parts[1] - - // 解析和验证 token - claims, err := tokenService.ParseToken(tokenString) - if err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "无效的Token"}) - return - } - - // 根据 token 中的用户ID,从数据库中获取完整的用户信息 - user, err := userRepo.FindByID(claims.UserID) - if err != nil { - if err == gorm.ErrRecordNotFound { - // Token有效,但对应的用户已不存在 - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "授权用户不存在"}) - return +func AuthMiddleware(tokenService token.Service, userRepo repository.UserRepository) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + // 从 Authorization header 获取 token + authHeader := c.Request().Header.Get("Authorization") + if authHeader == "" { + return controller.SendErrorWithStatus(c, http.StatusUnauthorized, controller.CodeUnauthorized, "请求未包含授权标头") } - // 其他数据库查询错误 - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "获取用户信息失败"}) - return + + // 授权标头的格式应为 "Bearer " + parts := strings.Split(authHeader, " ") + if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" { + return controller.SendErrorWithStatus(c, http.StatusUnauthorized, controller.CodeUnauthorized, "授权标头格式不正确") + } + + tokenString := parts[1] + + // 解析和验证 token + claims, err := tokenService.ParseToken(tokenString) + if err != nil { + return controller.SendErrorWithStatus(c, http.StatusUnauthorized, controller.CodeUnauthorized, "无效的Token") + } + + // 根据 token 中的用户ID,从数据库中获取完整的用户信息 + user, err := userRepo.FindByID(claims.UserID) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + // Token有效,但对应的用户已不存在 + return controller.SendErrorWithStatus(c, http.StatusUnauthorized, controller.CodeUnauthorized, "授权用户不存在") + } + // 其他数据库查询错误 + return controller.SendErrorWithStatus(c, http.StatusInternalServerError, controller.CodeInternalError, "获取用户信息失败") + } + + // 将完整的用户对象存储在 context 中,以便后续的处理函数使用 + c.Set(models.ContextUserKey.String(), user) + + // 继续处理请求链中的下一个处理程序 + return next(c) } - - // 将完整的用户对象存储在 context 中,以便后续的处理函数使用 - c.Set(models.ContextUserKey.String(), user) - - // 继续处理请求链中的下一个处理程序 - c.Next() } } diff --git a/openspec/changes/refactor-migrate-gin-to-echo/tasks.md b/openspec/changes/refactor-migrate-gin-to-echo/tasks.md index 1b9de91..2516d39 100644 --- a/openspec/changes/refactor-migrate-gin-to-echo/tasks.md +++ b/openspec/changes/refactor-migrate-gin-to-echo/tasks.md @@ -14,14 +14,14 @@ - [x] 适配 `Get...FromContext` 系列函数,使用 `c.Get("key")` 提取数据。 - [ ] **3. 中间件 (`internal/app/middleware`)** - - [ ] **`auth.go`** - - [ ] 将 `import "github.com/gin-gonic/gin"` 替换为 `import "github.com/labstack/echo/v4"`。 - - [ ] 将中间件函数签名从 `func AuthMiddleware(...) gin.HandlerFunc` 更新为 + - [x] **`auth.go`** + - [x] 将 `import "github.com/gin-gonic/gin"` 替换为 `import "github.com/labstack/echo/v4"`。 + - [x] 将中间件函数签名从 `func AuthMiddleware(...) gin.HandlerFunc` 更新为 `func AuthMiddleware(...) echo.MiddlewareFunc`。 - - [ ] 适配中间件内部逻辑,将 `func(c *gin.Context)` 改造为 + - [x] 适配中间件内部逻辑,将 `func(c *gin.Context)` 改造为 `func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { ... } }` 的结构。 - - [ ] 将 `c.AbortWithStatusJSON(...)` 调用替换为 `return controller.SendErrorResponse(...)`。 - - [ ] 在逻辑正常通过的末尾,调用 `return next(c)`。 + - [x] 将 `c.AbortWithStatusJSON(...)` 调用替换为 `return controller.SendErrorResponse(...)`。 + - [x] 在逻辑正常通过的末尾,调用 `return next(c)`。 - [ ] **`audit.go`** - [ ] 将 `import "github.com/gin-gonic/gin"` 替换为 `import "github.com/labstack/echo/v4"`。 - [ ] 将中间件函数签名从 `func AuditMiddleware(...) gin.HandlerFunc` 更新为