Files
pig-farm-controller/internal/app/api/api.go

140 lines
5.7 KiB
Go
Raw Normal View History

2025-09-12 14:39:44 +08:00
package api
import (
"context"
"fmt"
"net/http"
"time"
2025-09-12 17:18:14 +08:00
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/device"
2025-09-12 14:39:44 +08:00
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/user"
"git.huangwc.com/pig/pig-farm-controller/internal/app/service/token"
2025-09-12 16:58:39 +08:00
"git.huangwc.com/pig/pig-farm-controller/internal/infra/config"
2025-09-12 14:39:44 +08:00
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
"github.com/gin-gonic/gin"
2025-09-12 16:58:39 +08:00
_ "git.huangwc.com/pig/pig-farm-controller/docs" // 引入 swag 生成的 docs
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
2025-09-12 14:39:44 +08:00
)
// API 结构体定义了 HTTP 服务器及其依赖
type API struct {
2025-09-12 17:18:14 +08:00
engine *gin.Engine // Gin 引擎实例,用于处理 HTTP 请求
logger *logs.Logger // 日志记录器,用于输出日志信息
userRepo repository.UserRepository // 用户数据仓库接口,用于用户数据操作
tokenService token.TokenService // Token 服务接口,用于 JWT token 的生成和解析
httpServer *http.Server // 标准库的 HTTP 服务器实例,用于启动和停止服务
config config.ServerConfig // API 服务器的配置,使用 infra/config 包中的 ServerConfig
userController *user.Controller // 用户控制器实例
deviceController *device.Controller // 设备控制器实例
2025-09-12 14:39:44 +08:00
}
// NewAPI 创建并返回一个新的 API 实例
// 负责初始化 Gin 引擎、设置全局中间件,并注入所有必要的依赖。
2025-09-12 17:18:14 +08:00
func NewAPI(cfg config.ServerConfig, logger *logs.Logger, userRepo repository.UserRepository, deviceRepository repository.DeviceRepository, tokenService token.TokenService) *API {
2025-09-12 14:39:44 +08:00
// 设置 Gin 模式,例如 gin.ReleaseMode (生产模式) 或 gin.DebugMode (开发模式)
// 从配置中获取 Gin 模式
gin.SetMode(cfg.Mode)
// 使用 gin.New() 创建一个 Gin 引擎实例,而不是 gin.Default()
// 这样可以手动添加所需的中间件,避免 gin.Default() 默认包含的 Logger 和 Recovery 中间件
engine := gin.New()
// 添加 Gin Recovery 中间件,用于捕获 panic 并恢复,防止服务崩溃
// gin.Logger() 已移除,因为我们使用自定义的 logger
engine.Use(gin.Recovery())
// 初始化 API 结构体
api := &API{
engine: engine,
logger: logger,
userRepo: userRepo,
tokenService: tokenService,
config: cfg,
// 在 NewAPI 中初始化用户控制器,并将其作为 API 结构体的成员
userController: user.NewController(userRepo, logger, tokenService),
2025-09-12 17:18:14 +08:00
// 在 NewAPI 中初始化设备控制器,并将其作为 API 结构体的成员
deviceController: device.NewController(deviceRepository, logger),
2025-09-12 14:39:44 +08:00
}
api.setupRoutes() // 设置所有路由
return api
}
// setupRoutes 设置所有 API 路由
// 在此方法中,使用已初始化的控制器实例将其路由注册到 Gin 引擎中。
func (a *API) setupRoutes() {
// 创建 /api/v1 路由组
v1 := a.engine.Group("/api/v1")
{
// 用户相关路由组
userGroup := v1.Group("/users")
{
userGroup.POST("", a.userController.CreateUser) // 注册创建用户接口 (POST /api/v1/users)
userGroup.POST("/login", a.userController.Login) // 注册用户登录接口 (POST /api/v1/users/login)
}
2025-09-12 17:18:14 +08:00
// 设备相关路由组
deviceGroup := v1.Group("/devices")
{
deviceGroup.POST("", a.deviceController.CreateDevice)
deviceGroup.GET("", a.deviceController.ListDevices)
deviceGroup.GET("/:id", a.deviceController.GetDevice)
deviceGroup.PUT("/:id", a.deviceController.UpdateDevice)
deviceGroup.DELETE("/:id", a.deviceController.DeleteDevice)
}
2025-09-12 14:39:44 +08:00
}
2025-09-12 16:58:39 +08:00
// 添加 Swagger UI 路由
a.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
a.logger.Info("Swagger UI is available at /swagger/index.html")
2025-09-12 14:39:44 +08:00
}
// Start 启动 HTTP 服务器
// 接收一个地址字符串 (例如 ":8080"),并在一个新的 goroutine 中启动服务器,
// 以便主线程可以继续执行其他任务(例如监听操作系统信号)。
func (a *API) Start() {
// 构建监听地址字符串
addr := fmt.Sprintf(":%d", a.config.Port)
// 初始化标准库的 http.Server 实例
a.httpServer = &http.Server{
Addr: addr, // 服务器监听的地址从配置中获取
Handler: a.engine, // 将 Gin 引擎作为 HTTP 请求的处理程序
}
// 在独立的 goroutine 中启动服务器
// ListenAndServe 会阻塞,所以放在 goroutine 中可以避免阻塞主线程
go func() {
// 启动服务器并检查错误。http.ErrServerClosed 是正常关闭时的错误,无需处理。
if err := a.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
2025-09-12 16:58:39 +08:00
a.logger.Fatalf("HTTP 服务器监听失败: %s", err)
2025-09-12 14:39:44 +08:00
}
}()
// 记录服务器已启动的信息
2025-09-12 14:54:07 +08:00
a.logger.Infof("HTTP 服务器正在监听 %s", addr)
2025-09-12 14:39:44 +08:00
}
// Stop 优雅地停止 HTTP 服务器
// 在停止服务器时,会给一个超时时间,确保正在处理的请求能够完成。
func (a *API) Stop() {
// 记录服务器正在关闭的信息
2025-09-12 14:54:07 +08:00
a.logger.Info("正在关闭 HTTP 服务器...")
2025-09-12 14:39:44 +08:00
// 创建一个带有 5 秒超时时间的上下文
// 在此时间内,服务器会尝试完成所有活跃的连接。
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // 确保上下文在函数退出时被取消,释放资源
// 尝试优雅地关闭 HTTP 服务器
// 如果在超时时间内未能关闭Shutdown 会返回错误。
if err := a.httpServer.Shutdown(ctx); err != nil {
// 如果关闭失败,记录致命错误并退出
2025-09-12 14:54:07 +08:00
a.logger.Fatalf("HTTP 服务器关闭失败: %s", err)
2025-09-12 14:39:44 +08:00
}
// 记录服务器已停止的信息
2025-09-12 14:54:07 +08:00
a.logger.Info("HTTP 服务器已停止。")
2025-09-12 14:39:44 +08:00
}