完成任务3.1
This commit is contained in:
		| @@ -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 中设置业务相关的审计信息。 | ||||
|   | ||||
| @@ -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 <token>" | ||||
| 		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 <token>" | ||||
| 			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() | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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` 更新为 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user