2025-09-12 14:39:44 +08:00
|
|
|
|
package api
|
|
|
|
|
|
|
2025-09-12 20:32:29 +08:00
|
|
|
|
// @title 猪场管理系统 API
|
|
|
|
|
|
// @version 1.0
|
|
|
|
|
|
// @description 这是一个用于管理猪场设备的后端服务。
|
|
|
|
|
|
// @contact.name Divano
|
|
|
|
|
|
// @contact.url http://www.example.com
|
|
|
|
|
|
// @contact.email divano@example.com
|
|
|
|
|
|
// @license.name Apache 2.0
|
|
|
|
|
|
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
2025-09-12 14:39:44 +08:00
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"net/http"
|
2025-09-24 18:09:29 +08:00
|
|
|
|
"net/http/pprof"
|
2025-09-12 14:39:44 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
|
2025-09-27 23:17:23 +08:00
|
|
|
|
_ "git.huangwc.com/pig/pig-farm-controller/docs" // 引入 swag 生成的 docs
|
2025-09-12 17:18:14 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/device"
|
2025-10-03 18:27:53 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/management"
|
2025-09-13 14:09:22 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/plan"
|
2025-09-12 14:39:44 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/user"
|
2025-09-27 23:17:23 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/middleware"
|
2025-10-03 18:27:53 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
|
2025-10-02 00:18:13 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/webhook"
|
|
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/domain/audit"
|
|
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/domain/task"
|
|
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/domain/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"
|
2025-09-12 16:58:39 +08:00
|
|
|
|
|
2025-09-27 23:17:23 +08:00
|
|
|
|
"github.com/gin-gonic/gin"
|
2025-09-12 16:58:39 +08:00
|
|
|
|
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-10-03 23:42:14 +08:00
|
|
|
|
engine *gin.Engine // Gin 引擎实例,用于处理 HTTP 请求
|
|
|
|
|
|
logger *logs.Logger // 日志记录器,用于输出日志信息
|
|
|
|
|
|
userRepo repository.UserRepository // 用户数据仓库接口,用于用户数据操作
|
|
|
|
|
|
tokenService token.TokenService // Token 服务接口,用于 JWT token 的生成和解析
|
|
|
|
|
|
auditService audit.Service // 审计服务,用于记录用户操作
|
|
|
|
|
|
httpServer *http.Server // 标准库的 HTTP 服务器实例,用于启动和停止服务
|
|
|
|
|
|
config config.ServerConfig // API 服务器的配置,使用 infra/config 包中的 ServerConfig
|
|
|
|
|
|
userController *user.Controller // 用户控制器实例
|
|
|
|
|
|
deviceController *device.Controller // 设备控制器实例
|
|
|
|
|
|
planController *plan.Controller // 计划控制器实例
|
|
|
|
|
|
pigFarmController *management.PigFarmController // 猪场管理控制器实例
|
2025-10-07 13:31:56 +08:00
|
|
|
|
pigBatchController *management.PigBatchController // 猪群控制器实例
|
2025-10-03 23:42:14 +08:00
|
|
|
|
listenHandler webhook.ListenHandler // 设备上行事件监听器
|
|
|
|
|
|
analysisTaskManager *task.AnalysisPlanTaskManager // 计划触发器管理器实例
|
2025-09-12 14:39:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewAPI 创建并返回一个新的 API 实例
|
|
|
|
|
|
// 负责初始化 Gin 引擎、设置全局中间件,并注入所有必要的依赖。
|
2025-09-17 23:01:15 +08:00
|
|
|
|
func NewAPI(cfg config.ServerConfig,
|
|
|
|
|
|
logger *logs.Logger,
|
|
|
|
|
|
userRepo repository.UserRepository,
|
|
|
|
|
|
deviceRepository repository.DeviceRepository,
|
2025-09-30 15:25:07 +08:00
|
|
|
|
areaControllerRepository repository.AreaControllerRepository,
|
2025-09-30 22:07:55 +08:00
|
|
|
|
deviceTemplateRepository repository.DeviceTemplateRepository, // 添加设备模板仓库
|
2025-09-17 23:01:15 +08:00
|
|
|
|
planRepository repository.PlanRepository,
|
2025-10-03 18:27:53 +08:00
|
|
|
|
pigFarmService service.PigFarmService,
|
2025-10-07 13:31:56 +08:00
|
|
|
|
pigBatchService service.PigBatchService, // 添加猪群服务
|
2025-09-28 01:48:15 +08:00
|
|
|
|
userActionLogRepository repository.UserActionLogRepository,
|
2025-09-17 23:01:15 +08:00
|
|
|
|
tokenService token.TokenService,
|
2025-09-28 00:13:47 +08:00
|
|
|
|
auditService audit.Service, // 注入审计服务
|
2025-10-02 00:18:13 +08:00
|
|
|
|
listenHandler webhook.ListenHandler,
|
2025-09-17 23:01:15 +08:00
|
|
|
|
analysisTaskManager *task.AnalysisPlanTaskManager) *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{
|
2025-09-15 22:01:00 +08:00
|
|
|
|
engine: engine,
|
|
|
|
|
|
logger: logger,
|
|
|
|
|
|
userRepo: userRepo,
|
|
|
|
|
|
tokenService: tokenService,
|
2025-09-28 00:13:47 +08:00
|
|
|
|
auditService: auditService,
|
2025-09-15 22:01:00 +08:00
|
|
|
|
config: cfg,
|
|
|
|
|
|
listenHandler: listenHandler,
|
2025-09-12 14:39:44 +08:00
|
|
|
|
// 在 NewAPI 中初始化用户控制器,并将其作为 API 结构体的成员
|
2025-09-28 01:48:15 +08:00
|
|
|
|
userController: user.NewController(userRepo, userActionLogRepository, logger, tokenService),
|
2025-09-12 17:18:14 +08:00
|
|
|
|
// 在 NewAPI 中初始化设备控制器,并将其作为 API 结构体的成员
|
2025-09-30 22:07:55 +08:00
|
|
|
|
deviceController: device.NewController(deviceRepository, areaControllerRepository, deviceTemplateRepository, logger),
|
2025-09-13 14:09:22 +08:00
|
|
|
|
// 在 NewAPI 中初始化计划控制器,并将其作为 API 结构体的成员
|
2025-09-17 23:01:15 +08:00
|
|
|
|
planController: plan.NewController(logger, planRepository, analysisTaskManager),
|
2025-10-03 18:27:53 +08:00
|
|
|
|
// 在 NewAPI 中初始化猪场管理控制器
|
|
|
|
|
|
pigFarmController: management.NewPigFarmController(logger, pigFarmService),
|
2025-10-07 13:31:56 +08:00
|
|
|
|
// 在 NewAPI 中初始化猪群控制器
|
2025-10-03 23:42:14 +08:00
|
|
|
|
pigBatchController: management.NewPigBatchController(logger, pigBatchService),
|
2025-09-12 14:39:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
api.setupRoutes() // 设置所有路由
|
|
|
|
|
|
return api
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// setupRoutes 设置所有 API 路由
|
|
|
|
|
|
// 在此方法中,使用已初始化的控制器实例将其路由注册到 Gin 引擎中。
|
|
|
|
|
|
func (a *API) setupRoutes() {
|
2025-09-12 17:18:14 +08:00
|
|
|
|
|
2025-09-27 23:17:23 +08:00
|
|
|
|
// --- Public Routes ---
|
|
|
|
|
|
// 这些路由不需要身份验证
|
2025-09-13 14:09:22 +08:00
|
|
|
|
|
2025-09-27 23:17:23 +08:00
|
|
|
|
// 用户注册和登录
|
2025-10-07 13:31:56 +08:00
|
|
|
|
a.engine.POST("/api/v1/users", a.userController.CreateUser) // 注册新用户
|
|
|
|
|
|
a.engine.POST("/api/v1/users/login", a.userController.Login) // 用户登录
|
2025-09-27 23:17:23 +08:00
|
|
|
|
a.logger.Info("公开接口注册成功:用户注册、登录")
|
2025-09-12 16:58:39 +08:00
|
|
|
|
|
2025-09-24 18:09:29 +08:00
|
|
|
|
// 注册 pprof 路由
|
|
|
|
|
|
pprofGroup := a.engine.Group("/debug/pprof")
|
|
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
pprofGroup.GET("/", gin.WrapF(pprof.Index)) // pprof 索引页
|
|
|
|
|
|
pprofGroup.GET("/cmdline", gin.WrapF(pprof.Cmdline)) // pprof 命令行参数
|
|
|
|
|
|
pprofGroup.GET("/profile", gin.WrapF(pprof.Profile)) // pprof CPU profile
|
|
|
|
|
|
pprofGroup.POST("/symbol", gin.WrapF(pprof.Symbol)) // pprof 符号查找 (POST)
|
|
|
|
|
|
pprofGroup.GET("/symbol", gin.WrapF(pprof.Symbol)) // pprof 符号查找 (GET)
|
|
|
|
|
|
pprofGroup.GET("/trace", gin.WrapF(pprof.Trace)) // pprof 跟踪
|
|
|
|
|
|
pprofGroup.GET("/allocs", gin.WrapH(pprof.Handler("allocs"))) // pprof 内存分配
|
|
|
|
|
|
pprofGroup.GET("/block", gin.WrapH(pprof.Handler("block"))) // pprof 阻塞
|
2025-09-24 18:09:29 +08:00
|
|
|
|
pprofGroup.GET("/goroutine", gin.WrapH(pprof.Handler("goroutine")))
|
2025-10-07 13:31:56 +08:00
|
|
|
|
pprofGroup.GET("/heap", gin.WrapH(pprof.Handler("heap"))) // pprof 堆内存
|
|
|
|
|
|
pprofGroup.GET("/mutex", gin.WrapH(pprof.Handler("mutex"))) // pprof 互斥锁
|
2025-09-24 18:09:29 +08:00
|
|
|
|
pprofGroup.GET("/threadcreate", gin.WrapH(pprof.Handler("threadcreate")))
|
|
|
|
|
|
}
|
|
|
|
|
|
a.logger.Info("pprof 接口注册成功")
|
|
|
|
|
|
|
2025-09-15 22:01:00 +08:00
|
|
|
|
// 上行事件监听路由
|
2025-10-07 13:31:56 +08:00
|
|
|
|
a.engine.POST("/upstream", gin.WrapH(a.listenHandler.Handler())) // 处理设备上行事件
|
2025-09-24 18:09:29 +08:00
|
|
|
|
a.logger.Info("上行事件监听接口注册成功")
|
2025-09-15 22:01:00 +08:00
|
|
|
|
|
2025-09-24 18:09:29 +08:00
|
|
|
|
// 添加 Swagger UI 路由, Swagger UI可在 /swagger/index.html 上找到
|
2025-10-07 13:31:56 +08:00
|
|
|
|
a.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // Swagger UI 接口
|
2025-09-24 18:09:29 +08:00
|
|
|
|
a.logger.Info("Swagger UI 接口注册成功")
|
|
|
|
|
|
|
2025-09-27 23:17:23 +08:00
|
|
|
|
// --- Authenticated Routes ---
|
|
|
|
|
|
// 所有在此注册的路由都需要通过 JWT 身份验证
|
|
|
|
|
|
authGroup := a.engine.Group("/api/v1")
|
2025-10-07 13:31:56 +08:00
|
|
|
|
authGroup.Use(middleware.AuthMiddleware(a.tokenService, a.userRepo)) // 1. 身份认证中间件
|
|
|
|
|
|
authGroup.Use(middleware.AuditLogMiddleware(a.auditService)) // 2. 审计日志中间件
|
2025-09-27 23:17:23 +08:00
|
|
|
|
{
|
2025-09-28 01:48:15 +08:00
|
|
|
|
// 用户相关路由组
|
|
|
|
|
|
userGroup := authGroup.Group("/users")
|
|
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
userGroup.GET("/:id/history", a.userController.ListUserHistory) // 获取用户操作历史
|
2025-09-28 01:48:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
a.logger.Info("用户相关接口注册成功 (需要认证和审计)")
|
|
|
|
|
|
|
2025-09-27 23:17:23 +08:00
|
|
|
|
// 设备相关路由组
|
|
|
|
|
|
deviceGroup := authGroup.Group("/devices")
|
|
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
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-27 23:17:23 +08:00
|
|
|
|
}
|
2025-09-28 00:13:47 +08:00
|
|
|
|
a.logger.Info("设备相关接口注册成功 (需要认证和审计)")
|
2025-09-27 23:17:23 +08:00
|
|
|
|
|
2025-09-30 15:25:07 +08:00
|
|
|
|
// 区域主控相关路由组
|
|
|
|
|
|
areaControllerGroup := authGroup.Group("/area-controllers")
|
|
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
areaControllerGroup.POST("", a.deviceController.CreateAreaController) // 创建区域主控
|
|
|
|
|
|
areaControllerGroup.GET("", a.deviceController.ListAreaControllers) // 获取区域主控列表
|
|
|
|
|
|
areaControllerGroup.GET("/:id", a.deviceController.GetAreaController) // 获取单个区域主控
|
|
|
|
|
|
areaControllerGroup.PUT("/:id", a.deviceController.UpdateAreaController) // 更新区域主控
|
|
|
|
|
|
areaControllerGroup.DELETE("/:id", a.deviceController.DeleteAreaController) // 删除区域主控
|
2025-09-30 15:25:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
a.logger.Info("区域主控相关接口注册成功 (需要认证和审计)")
|
|
|
|
|
|
|
2025-09-30 22:07:55 +08:00
|
|
|
|
// 设备模板相关路由组
|
|
|
|
|
|
deviceTemplateGroup := authGroup.Group("/device-templates")
|
|
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
deviceTemplateGroup.POST("", a.deviceController.CreateDeviceTemplate) // 创建设备模板
|
|
|
|
|
|
deviceTemplateGroup.GET("", a.deviceController.ListDeviceTemplates) // 获取设备模板列表
|
|
|
|
|
|
deviceTemplateGroup.GET("/:id", a.deviceController.GetDeviceTemplate) // 获取单个设备模板
|
|
|
|
|
|
deviceTemplateGroup.PUT("/:id", a.deviceController.UpdateDeviceTemplate) // 更新设备模板
|
|
|
|
|
|
deviceTemplateGroup.DELETE("/:id", a.deviceController.DeleteDeviceTemplate) // 删除设备模板
|
2025-09-30 22:07:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
a.logger.Info("设备模板相关接口注册成功 (需要认证和审计)")
|
|
|
|
|
|
|
2025-09-27 23:17:23 +08:00
|
|
|
|
// 计划相关路由组
|
|
|
|
|
|
planGroup := authGroup.Group("/plans")
|
|
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
planGroup.POST("", a.planController.CreatePlan) // 创建计划
|
|
|
|
|
|
planGroup.GET("", a.planController.ListPlans) // 获取计划列表
|
|
|
|
|
|
planGroup.GET("/:id", a.planController.GetPlan) // 获取单个计划
|
|
|
|
|
|
planGroup.PUT("/:id", a.planController.UpdatePlan) // 更新计划
|
|
|
|
|
|
planGroup.DELETE("/:id", a.planController.DeletePlan) // 删除计划
|
|
|
|
|
|
planGroup.POST("/:id/start", a.planController.StartPlan) // 启动计划
|
|
|
|
|
|
planGroup.POST("/:id/stop", a.planController.StopPlan) // 停止计划
|
2025-09-27 23:17:23 +08:00
|
|
|
|
}
|
2025-09-28 00:13:47 +08:00
|
|
|
|
a.logger.Info("计划相关接口注册成功 (需要认证和审计)")
|
2025-10-03 18:27:53 +08:00
|
|
|
|
|
|
|
|
|
|
// 猪舍相关路由组
|
2025-10-03 23:58:37 +08:00
|
|
|
|
pigHouseGroup := authGroup.Group("/pig-houses")
|
2025-10-03 18:27:53 +08:00
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
pigHouseGroup.POST("", a.pigFarmController.CreatePigHouse) // 创建猪舍
|
|
|
|
|
|
pigHouseGroup.GET("", a.pigFarmController.ListPigHouses) // 获取猪舍列表
|
|
|
|
|
|
pigHouseGroup.GET("/:id", a.pigFarmController.GetPigHouse) // 获取单个猪舍
|
|
|
|
|
|
pigHouseGroup.PUT("/:id", a.pigFarmController.UpdatePigHouse) // 更新猪舍
|
|
|
|
|
|
pigHouseGroup.DELETE("/:id", a.pigFarmController.DeletePigHouse) // 删除猪舍
|
2025-10-03 18:27:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
a.logger.Info("猪舍相关接口注册成功 (需要认证和审计)")
|
|
|
|
|
|
|
|
|
|
|
|
// 猪圈相关路由组
|
|
|
|
|
|
penGroup := authGroup.Group("/pens")
|
|
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
penGroup.POST("", a.pigFarmController.CreatePen) // 创建猪圈
|
|
|
|
|
|
penGroup.GET("", a.pigFarmController.ListPens) // 获取猪圈列表
|
|
|
|
|
|
penGroup.GET("/:id", a.pigFarmController.GetPen) // 获取单个猪圈
|
|
|
|
|
|
penGroup.PUT("/:id", a.pigFarmController.UpdatePen) // 更新猪圈
|
|
|
|
|
|
penGroup.DELETE("/:id", a.pigFarmController.DeletePen) // 删除猪圈
|
|
|
|
|
|
penGroup.PUT("/:id/status", a.pigFarmController.UpdatePenStatus) // 更新猪圈状态
|
2025-10-03 18:27:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
a.logger.Info("猪圈相关接口注册成功 (需要认证和审计)")
|
|
|
|
|
|
|
2025-10-06 23:22:47 +08:00
|
|
|
|
// 猪群相关路由组
|
2025-10-03 23:42:14 +08:00
|
|
|
|
pigBatchGroup := authGroup.Group("/pig-batches")
|
|
|
|
|
|
{
|
2025-10-07 13:31:56 +08:00
|
|
|
|
pigBatchGroup.POST("", a.pigBatchController.CreatePigBatch) // 创建猪群
|
|
|
|
|
|
pigBatchGroup.GET("", a.pigBatchController.ListPigBatches) // 获取猪群列表
|
|
|
|
|
|
pigBatchGroup.GET("/:id", a.pigBatchController.GetPigBatch) // 获取单个猪群
|
|
|
|
|
|
pigBatchGroup.PUT("/:id", a.pigBatchController.UpdatePigBatch) // 更新猪群
|
|
|
|
|
|
pigBatchGroup.DELETE("/:id", a.pigBatchController.DeletePigBatch) // 删除猪群
|
|
|
|
|
|
pigBatchGroup.POST("/:id/assign-pens", a.pigBatchController.AssignEmptyPensToBatch) // 为猪群分配空栏
|
|
|
|
|
|
pigBatchGroup.POST("/:fromBatchID/reclassify-pen", a.pigBatchController.ReclassifyPenToNewBatch) // 将猪栏划拨到新群
|
|
|
|
|
|
pigBatchGroup.DELETE("/:batchID/remove-pen/:penID", a.pigBatchController.RemoveEmptyPenFromBatch) // 从猪群移除空栏
|
|
|
|
|
|
pigBatchGroup.POST("/:id/move-pigs-into-pen", a.pigBatchController.MovePigsIntoPen) // 将猪只从“虚拟库存”移入指定猪栏
|
|
|
|
|
|
pigBatchGroup.POST("/:id/sell-pigs", a.pigBatchController.SellPigs) // 处理卖猪业务
|
|
|
|
|
|
pigBatchGroup.POST("/:id/buy-pigs", a.pigBatchController.BuyPigs) // 处理买猪业务
|
|
|
|
|
|
pigBatchGroup.POST("/:sourceBatchID/transfer-across-batches", a.pigBatchController.TransferPigsAcrossBatches) // 跨猪群调栏
|
|
|
|
|
|
pigBatchGroup.POST("/:id/transfer-within-batch", a.pigBatchController.TransferPigsWithinBatch) // 群内调栏
|
|
|
|
|
|
pigBatchGroup.POST("/:id/record-sick-pigs", a.pigBatchController.RecordSickPigs) // 记录新增病猪事件
|
|
|
|
|
|
pigBatchGroup.POST("/:id/record-sick-pig-recovery", a.pigBatchController.RecordSickPigRecovery) // 记录病猪康复事件
|
|
|
|
|
|
pigBatchGroup.POST("/:id/record-sick-pig-death", a.pigBatchController.RecordSickPigDeath) // 记录病猪死亡事件
|
|
|
|
|
|
pigBatchGroup.POST("/:id/record-sick-pig-cull", a.pigBatchController.RecordSickPigCull) // 记录病猪淘汰事件
|
|
|
|
|
|
pigBatchGroup.POST("/:id/record-death", a.pigBatchController.RecordDeath) // 记录正常猪只死亡事件
|
|
|
|
|
|
pigBatchGroup.POST("/:id/record-cull", a.pigBatchController.RecordCull) // 记录正常猪只淘汰事件
|
2025-10-03 23:42:14 +08:00
|
|
|
|
}
|
2025-10-07 13:31:56 +08:00
|
|
|
|
a.logger.Info("猪群相关接口注册成功 (需要认证和审计)")
|
2025-10-03 23:42:14 +08:00
|
|
|
|
|
2025-09-27 23:17:23 +08:00
|
|
|
|
}
|
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
|
|
|
|
}
|