Files
pig-farm-controller/openspec/changes/refactor-migrate-gin-to-echo/design.md
2025-10-30 16:10:10 +08:00

6.0 KiB
Raw Blame History

Context

当前 API 服务基于 Gin 构建。本次任务的目标是将其完整迁移到 Echo 框架同时保持功能和接口的完全向后兼容。这包括路由、请求处理、中间件、Swagger 文档和 pprof 分析工具。

Goals / Non-Goals

  • Goals:
    • 成功将 Web 框架从 Gin 迁移到 Echo v4。
    • 保持所有现有 API 端点的路径、方法和行为不变。
    • 确保所有自定义中间件(认证、审计日志)功能正常。
    • 确保 Swagger UI 可以在 /swagger/index.html 正常访问。
    • 确保 pprof 调试端点在 /debug/pprof/* 路径下正常工作。
  • Non-Goals:
    • 增加任何新的 API 端点或功能。
    • 修改任何现有的 API 请求/响应模型。
    • 在本次变更中引入新的业务逻辑。

Decisions

以下是从 Gin 到 Echo 的关键组件映射决策:

  1. 框架实例:

    • From: gin.SetMode(cfg.Mode), engine := gin.New(), engine.Use(gin.Recovery())
    • To: e := echo.New(), e.Debug = (cfg.Mode == "debug"), e.Use(middleware.Recover())
    • Rationale: echo.New() 提供了干净的实例。Echo 的 Debug 属性控制调试模式可以根据配置设置。Echo 提供了内置的 middleware.Recover() 来替代 Gin 的 Recovery 中间件。
    • Implementation:
      • internal/app/api/api.go 中,将 engine *gin.Engine 替换为 engine *echo.Echo,并更新 NewAPI 方法中的初始化逻辑。
      • config.ymlconfig.example.yml 中, 更新关于 mode 配置项的注释, 将 "Gin 运行模式" 修改为 "服务运行模式", 因为该配置项现在控制 Echo 的调试模式。
  2. 上下文对象 (Context) 与处理器签名:

    • From: func(c *gin.Context)
    • To: func(c echo.Context) error
    • Rationale: 这是两个框架的核心区别。所有控制器处理函数签名都需要更新。常见方法映射如下:
      • ctx.ShouldBindJSON(&req) -> c.Bind(&req) (Echo 的 Bind 更通用,能根据 Content-Type 自动选择解析器)
      • ctx.ShouldBindQuery(&req) -> c.Bind(&req)
      • ctx.Param("id") -> c.Param("id") (签名相同)
      • ctx.GetHeader("Authorization") -> c.Request().Header.Get("Authorization")
      • ctx.Set("key", value) -> c.Set("key", value) (签名相同)
      • ctx.Get("key") -> c.Get("key") (签名相同)
      • ctx.ClientIP() -> c.RealIP()
      • controller.SendResponse(ctx, ...) -> return controller.SendResponse(c, ...) (控制器方法需要返回 error,辅助函数也需要修改以返回 error)
      • controller.SendErrorResponse(ctx, ...) -> return controller.SendErrorResponse(c, ...) (同上)
      • ctx.AbortWithStatusJSON(...) -> return c.JSON(...) (在中间件中,通过 return c.JSON(...) 来中断链并响应)
  3. 中间件 (Middleware):

    • From: func AuthMiddleware(...) gin.HandlerFunc { return func(c *gin.Context) { ... } }
    • To: func AuthMiddleware(...) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { ...; return next(c) } } }
    • Rationale: Echo 的中间件是一个包装器模式。我们需要将现有的 AuthMiddlewareAuditLogMiddleware 逻辑迁移到这个新的结构中。中断请求链的方式从 c.AbortWithStatusJSON() 变为从处理函数中 return c.JSON(...)
  4. Swagger 集成:

    • From: github.com/swaggo/gin-swagger
    • To: github.com/swaggo/echo-swagger
    • Rationale: 这是 swaggo 官方为 Echo 提供的适配库,可以无缝替换。
    • Implementation: 在 router.go 中使用 e.GET("/swagger/*", echoSwagger.WrapHandler)
  5. Pprof 与其他 net/http 处理器集成:

    • From: gin.WrapHgin.WrapF
    • To: echo.WrapHandlerecho.WrapFunc
    • Rationale: Echo 提供了类似的 net/http 处理器包装函数,可以轻松集成 pprof 和项目中的 listenHandler
    • Implementation: 在 router.go 中替换所有 gin.WrapHgin.WrapF 的调用。
  6. 控制器辅助函数:

    • Affected Files:
      • internal/app/controller/response.go
      • internal/app/controller/auth_utils.go
      • internal/app/controller/management/controller_helpers.go
    • Change:
      • response.goauth_utils.go 中, 所有接收 *gin.Context 的辅助函数 (如 SendResponse, GetOperatorIDFromContext 等) 签名都需要修改为接收 echo.Context
      • controller_helpers.go 中, handle... 系列的泛型辅助函数 (如 handleAPIRequest, handleNoBodyAPIRequest 等) 及其依赖的 extractOperatorAndPrimaryIDmapAndSendError 函数, 都需要将其中的 *gin.Context 参数和相关调用 (如 ShouldBindJSON) 替换为 echo.Context 的等效实现。
      • 所有这些辅助函数, 如果它们原本不返回 error, 现在需要修改为返回 error, 以便与 Echo 的处理器错误链兼容。例如, SendResponse 这类函数在调用 c.JSON(...) 后, 最终应 return nil
    • Rationale: 这些辅助函数封装了请求处理、响应发送和错误处理的核心逻辑, 必须进行适配以兼容 Echo 的 echo.Context 上下文对象和 return error 的错误处理模式。

Risks / Trade-offs

  • Risk: 迁移工作量大,可能遗漏某些 Gin 特有的功能或上下文用法,导致运行时错误。
  • Mitigation: 采用逐个文件、逐个控制器修改的方式,每修改完一部分就进行编译检查。在完成所有编码后,进行全面的手动 API 测试。
  • Risk: AuditLogMiddleware 中间件依赖 bodyLogWriter 捕获响应体,需要验证其与 Echo 的 ResponseWriter 是否兼容或需要寻找替代方案。
  • Mitigation: 在迁移中间件时,优先研究 Echo 官方推荐的 Body Dump 或类似中间件,如果不适用,再尝试适配 bodyLogWriter