重构 #4
							
								
								
									
										117
									
								
								internal/app/api/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								internal/app/api/api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | |||||||
|  | package api | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/user" | ||||||
|  | 	"git.huangwc.com/pig/pig-farm-controller/internal/app/service/token" | ||||||
|  | 	"git.huangwc.com/pig/pig-farm-controller/internal/infra/config" // 引入 config 包 | ||||||
|  | 	"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" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // API 结构体定义了 HTTP 服务器及其依赖 | ||||||
|  | type API struct { | ||||||
|  | 	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          // 用户控制器实例 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewAPI 创建并返回一个新的 API 实例 | ||||||
|  | // 负责初始化 Gin 引擎、设置全局中间件,并注入所有必要的依赖。 | ||||||
|  | func NewAPI(cfg config.ServerConfig, logger *logs.Logger, userRepo repository.UserRepository, tokenService token.TokenService) *API { | ||||||
|  | 	// 设置 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), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	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) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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 { | ||||||
|  | 			a.logger.Fatalf("HTTP server listen: %s", err) // 如果是其他错误,则记录致命错误并退出 | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 	// 记录服务器已启动的信息 | ||||||
|  | 	a.logger.Infof("HTTP server listening on %s", addr) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Stop 优雅地停止 HTTP 服务器 | ||||||
|  | // 在停止服务器时,会给一个超时时间,确保正在处理的请求能够完成。 | ||||||
|  | func (a *API) Stop() { | ||||||
|  | 	// 记录服务器正在关闭的信息 | ||||||
|  | 	a.logger.Info("Shutting down HTTP server...") | ||||||
|  |  | ||||||
|  | 	// 创建一个带有 5 秒超时时间的上下文 | ||||||
|  | 	// 在此时间内,服务器会尝试完成所有活跃的连接。 | ||||||
|  | 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||||||
|  | 	defer cancel() // 确保上下文在函数退出时被取消,释放资源 | ||||||
|  |  | ||||||
|  | 	// 尝试优雅地关闭 HTTP 服务器 | ||||||
|  | 	// 如果在超时时间内未能关闭,Shutdown 会返回错误。 | ||||||
|  | 	if err := a.httpServer.Shutdown(ctx); err != nil { | ||||||
|  | 		// 如果关闭失败,记录致命错误并退出 | ||||||
|  | 		a.logger.Fatalf("HTTP server shutdown failed: %s", err) | ||||||
|  | 	} | ||||||
|  | 	// 记录服务器已停止的信息 | ||||||
|  | 	a.logger.Info("HTTP server stopped.") | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user