Compare commits
	
		
			21 Commits
		
	
	
		
			3b967aa449
			...
			71afbf5ff9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 71afbf5ff9 | |||
| 4e046021e3 | |||
| 4cbb4bb859 | |||
| 0038f20334 | |||
| 197af0181c | |||
| 1830fcd43e | |||
| 53845422c1 | |||
| 757d38645e | |||
| 5ee6cbce8f | |||
| fd39eb6450 | |||
| 89fbbbb75f | |||
| e150969ee3 | |||
| 4c6843afb4 | |||
| eb0786ca27 | |||
| 7299c8ebe6 | |||
| bcdcaa5631 | |||
| fab26ffca4 | |||
| 6a93346e87 | |||
| df0dfd62c6 | |||
| 51a873049e | |||
| 05820438d0 | 
							
								
								
									
										2402
									
								
								docs/docs.go
									
									
									
									
									
								
							
							
						
						
									
										2402
									
								
								docs/docs.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2402
									
								
								docs/swagger.json
									
									
									
									
									
								
							
							
						
						
									
										2402
									
								
								docs/swagger.json
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1490
									
								
								docs/swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										1490
									
								
								docs/swagger.yaml
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -16,15 +16,14 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/pprof"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_ "git.huangwc.com/pig/pig-farm-controller/docs" // 引入 swag 生成的 docs
 | 
						_ "git.huangwc.com/pig/pig-farm-controller/docs" // 引入 swag 生成的 docs
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/device"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/device"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/management"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/management"
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/monitor"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/plan"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/plan"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/user"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/controller/user"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/middleware"
 | 
					 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/webhook"
 | 
						"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/audit"
 | 
				
			||||||
@@ -36,8 +35,6 @@ import (
 | 
				
			|||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gin-gonic/gin"
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
	swaggerFiles "github.com/swaggo/files"
 | 
					 | 
				
			||||||
	ginSwagger "github.com/swaggo/gin-swagger"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// API 结构体定义了 HTTP 服务器及其依赖
 | 
					// API 结构体定义了 HTTP 服务器及其依赖
 | 
				
			||||||
@@ -54,6 +51,7 @@ type API struct {
 | 
				
			|||||||
	planController      *plan.Controller               // 计划控制器实例
 | 
						planController      *plan.Controller               // 计划控制器实例
 | 
				
			||||||
	pigFarmController   *management.PigFarmController  // 猪场管理控制器实例
 | 
						pigFarmController   *management.PigFarmController  // 猪场管理控制器实例
 | 
				
			||||||
	pigBatchController  *management.PigBatchController // 猪群控制器实例
 | 
						pigBatchController  *management.PigBatchController // 猪群控制器实例
 | 
				
			||||||
 | 
						monitorController   *monitor.Controller            // 数据监控控制器实例
 | 
				
			||||||
	listenHandler       webhook.ListenHandler          // 设备上行事件监听器
 | 
						listenHandler       webhook.ListenHandler          // 设备上行事件监听器
 | 
				
			||||||
	analysisTaskManager *task.AnalysisPlanTaskManager  // 计划触发器管理器实例
 | 
						analysisTaskManager *task.AnalysisPlanTaskManager  // 计划触发器管理器实例
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -69,6 +67,7 @@ func NewAPI(cfg config.ServerConfig,
 | 
				
			|||||||
	planRepository repository.PlanRepository,
 | 
						planRepository repository.PlanRepository,
 | 
				
			||||||
	pigFarmService service.PigFarmService,
 | 
						pigFarmService service.PigFarmService,
 | 
				
			||||||
	pigBatchService service.PigBatchService,
 | 
						pigBatchService service.PigBatchService,
 | 
				
			||||||
 | 
						monitorService service.MonitorService,
 | 
				
			||||||
	userActionLogRepository repository.UserActionLogRepository,
 | 
						userActionLogRepository repository.UserActionLogRepository,
 | 
				
			||||||
	tokenService token.TokenService,
 | 
						tokenService token.TokenService,
 | 
				
			||||||
	auditService audit.Service,
 | 
						auditService audit.Service,
 | 
				
			||||||
@@ -97,7 +96,7 @@ func NewAPI(cfg config.ServerConfig,
 | 
				
			|||||||
		config:        cfg,
 | 
							config:        cfg,
 | 
				
			||||||
		listenHandler: listenHandler,
 | 
							listenHandler: listenHandler,
 | 
				
			||||||
		// 在 NewAPI 中初始化用户控制器,并将其作为 API 结构体的成员
 | 
							// 在 NewAPI 中初始化用户控制器,并将其作为 API 结构体的成员
 | 
				
			||||||
		userController: user.NewController(userRepo, userActionLogRepository, logger, tokenService),
 | 
							userController: user.NewController(userRepo, monitorService, logger, tokenService),
 | 
				
			||||||
		// 在 NewAPI 中初始化设备控制器,并将其作为 API 结构体的成员
 | 
							// 在 NewAPI 中初始化设备控制器,并将其作为 API 结构体的成员
 | 
				
			||||||
		deviceController: device.NewController(deviceRepository, areaControllerRepository, deviceTemplateRepository, deviceService, logger),
 | 
							deviceController: device.NewController(deviceRepository, areaControllerRepository, deviceTemplateRepository, deviceService, logger),
 | 
				
			||||||
		// 在 NewAPI 中初始化计划控制器,并将其作为 API 结构体的成员
 | 
							// 在 NewAPI 中初始化计划控制器,并将其作为 API 结构体的成员
 | 
				
			||||||
@@ -106,161 +105,14 @@ func NewAPI(cfg config.ServerConfig,
 | 
				
			|||||||
		pigFarmController: management.NewPigFarmController(logger, pigFarmService),
 | 
							pigFarmController: management.NewPigFarmController(logger, pigFarmService),
 | 
				
			||||||
		// 在 NewAPI 中初始化猪群控制器
 | 
							// 在 NewAPI 中初始化猪群控制器
 | 
				
			||||||
		pigBatchController: management.NewPigBatchController(logger, pigBatchService),
 | 
							pigBatchController: management.NewPigBatchController(logger, pigBatchService),
 | 
				
			||||||
 | 
							// 在 NewAPI 中初始化数据监控控制器
 | 
				
			||||||
 | 
							monitorController: monitor.NewController(monitorService, logger),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api.setupRoutes() // 设置所有路由
 | 
						api.setupRoutes() // 设置所有路由
 | 
				
			||||||
	return api
 | 
						return api
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// setupRoutes 设置所有 API 路由
 | 
					 | 
				
			||||||
// 在此方法中,使用已初始化的控制器实例将其路由注册到 Gin 引擎中。
 | 
					 | 
				
			||||||
func (a *API) setupRoutes() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// --- Public Routes ---
 | 
					 | 
				
			||||||
	// 这些路由不需要身份验证
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 用户注册和登录
 | 
					 | 
				
			||||||
	a.engine.POST("/api/v1/users", a.userController.CreateUser)  // 注册新用户
 | 
					 | 
				
			||||||
	a.engine.POST("/api/v1/users/login", a.userController.Login) // 用户登录
 | 
					 | 
				
			||||||
	a.logger.Info("公开接口注册成功:用户注册、登录")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 注册 pprof 路由
 | 
					 | 
				
			||||||
	pprofGroup := a.engine.Group("/debug/pprof")
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		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 阻塞
 | 
					 | 
				
			||||||
		pprofGroup.GET("/goroutine", gin.WrapH(pprof.Handler("goroutine")))
 | 
					 | 
				
			||||||
		pprofGroup.GET("/heap", gin.WrapH(pprof.Handler("heap")))   // pprof 堆内存
 | 
					 | 
				
			||||||
		pprofGroup.GET("/mutex", gin.WrapH(pprof.Handler("mutex"))) // pprof 互斥锁
 | 
					 | 
				
			||||||
		pprofGroup.GET("/threadcreate", gin.WrapH(pprof.Handler("threadcreate")))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	a.logger.Info("pprof 接口注册成功")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 上行事件监听路由
 | 
					 | 
				
			||||||
	a.engine.POST("/upstream", gin.WrapH(a.listenHandler.Handler())) // 处理设备上行事件
 | 
					 | 
				
			||||||
	a.logger.Info("上行事件监听接口注册成功")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 添加 Swagger UI 路由, Swagger UI可在 /swagger/index.html 上找到
 | 
					 | 
				
			||||||
	a.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // Swagger UI 接口
 | 
					 | 
				
			||||||
	a.logger.Info("Swagger UI 接口注册成功")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// --- Authenticated Routes ---
 | 
					 | 
				
			||||||
	// 所有在此注册的路由都需要通过 JWT 身份验证
 | 
					 | 
				
			||||||
	authGroup := a.engine.Group("/api/v1")
 | 
					 | 
				
			||||||
	authGroup.Use(middleware.AuthMiddleware(a.tokenService, a.userRepo)) // 1. 身份认证中间件
 | 
					 | 
				
			||||||
	authGroup.Use(middleware.AuditLogMiddleware(a.auditService))         // 2. 审计日志中间件
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// 用户相关路由组
 | 
					 | 
				
			||||||
		userGroup := authGroup.Group("/users")
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			userGroup.GET("/:id/history", a.userController.ListUserHistory) // 获取用户操作历史
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.logger.Info("用户相关接口注册成功 (需要认证和审计)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 设备相关路由组
 | 
					 | 
				
			||||||
		deviceGroup := authGroup.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)               // 删除设备
 | 
					 | 
				
			||||||
			deviceGroup.POST("/manual-control/:id", a.deviceController.ManualControl) // 手动控制设备
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.logger.Info("设备相关接口注册成功 (需要认证和审计)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 区域主控相关路由组
 | 
					 | 
				
			||||||
		areaControllerGroup := authGroup.Group("/area-controllers")
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			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) // 删除区域主控
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.logger.Info("区域主控相关接口注册成功 (需要认证和审计)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 设备模板相关路由组
 | 
					 | 
				
			||||||
		deviceTemplateGroup := authGroup.Group("/device-templates")
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			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) // 删除设备模板
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.logger.Info("设备模板相关接口注册成功 (需要认证和审计)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 计划相关路由组
 | 
					 | 
				
			||||||
		planGroup := authGroup.Group("/plans")
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			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)   // 停止计划
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.logger.Info("计划相关接口注册成功 (需要认证和审计)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 猪舍相关路由组
 | 
					 | 
				
			||||||
		pigHouseGroup := authGroup.Group("/pig-houses")
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			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) // 删除猪舍
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.logger.Info("猪舍相关接口注册成功 (需要认证和审计)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 猪圈相关路由组
 | 
					 | 
				
			||||||
		penGroup := authGroup.Group("/pens")
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			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) // 更新猪圈状态
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.logger.Info("猪圈相关接口注册成功 (需要认证和审计)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 猪群相关路由组
 | 
					 | 
				
			||||||
		pigBatchGroup := authGroup.Group("/pig-batches")
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			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("/assign-pens/:id", a.pigBatchController.AssignEmptyPensToBatch)                           // 为猪群分配空栏
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/reclassify-pen/:fromBatchID", a.pigBatchController.ReclassifyPenToNewBatch)              // 将猪栏划拨到新群
 | 
					 | 
				
			||||||
			penGroup.DELETE("/remove-pen/:penID/:batchID", a.pigBatchController.RemoveEmptyPenFromBatch)                  // 从猪群移除空栏
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/move-pigs-into-pen/:id", a.pigBatchController.MovePigsIntoPen)                           // 将猪只从“虚拟库存”移入指定猪栏
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/sell-pigs/:id", a.pigBatchController.SellPigs)                                           // 处理卖猪业务
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/buy-pigs/:id", a.pigBatchController.BuyPigs)                                             // 处理买猪业务
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/transfer-across-batches/:sourceBatchID", a.pigBatchController.TransferPigsAcrossBatches) // 跨猪群调栏
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/transfer-within-batch/:id", a.pigBatchController.TransferPigsWithinBatch)                // 群内调栏
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/record-sick-pigs/:id", a.pigBatchController.RecordSickPigs)                              // 记录新增病猪事件
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/record-sick-pig-recovery/:id", a.pigBatchController.RecordSickPigRecovery)               // 记录病猪康复事件
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/record-sick-pig-death/:id", a.pigBatchController.RecordSickPigDeath)                     // 记录病猪死亡事件
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/record-sick-pig-cull/:id", a.pigBatchController.RecordSickPigCull)                       // 记录病猪淘汰事件
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/record-death/:id", a.pigBatchController.RecordDeath)                                     // 记录正常猪只死亡事件
 | 
					 | 
				
			||||||
			pigBatchGroup.POST("/record-cull/:id", a.pigBatchController.RecordCull)                                       // 记录正常猪只淘汰事件
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.logger.Info("猪群相关接口注册成功 (需要认证和审计)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Start 启动 HTTP 服务器
 | 
					// Start 启动 HTTP 服务器
 | 
				
			||||||
// 接收一个地址字符串 (例如 ":8080"),并在一个新的 goroutine 中启动服务器,
 | 
					// 接收一个地址字符串 (例如 ":8080"),并在一个新的 goroutine 中启动服务器,
 | 
				
			||||||
// 以便主线程可以继续执行其他任务(例如监听操作系统信号)。
 | 
					// 以便主线程可以继续执行其他任务(例如监听操作系统信号)。
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										181
									
								
								internal/app/api/router.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								internal/app/api/router.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,181 @@
 | 
				
			|||||||
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/http/pprof"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/middleware"
 | 
				
			||||||
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
 | 
						swaggerFiles "github.com/swaggo/files"
 | 
				
			||||||
 | 
						ginSwagger "github.com/swaggo/gin-swagger"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setupRoutes 设置所有 API 路由
 | 
				
			||||||
 | 
					// 在此方法中,使用已初始化的控制器实例将其路由注册到 Gin 引擎中。
 | 
				
			||||||
 | 
					func (a *API) setupRoutes() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- Public Routes ---
 | 
				
			||||||
 | 
						// 这些路由不需要身份验证
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 用户注册和登录
 | 
				
			||||||
 | 
						a.engine.POST("/api/v1/users", a.userController.CreateUser)  // 注册新用户
 | 
				
			||||||
 | 
						a.engine.POST("/api/v1/users/login", a.userController.Login) // 用户登录
 | 
				
			||||||
 | 
						a.logger.Info("公开接口注册成功:用户注册、登录")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 注册 pprof 路由
 | 
				
			||||||
 | 
						pprofGroup := a.engine.Group("/debug/pprof")
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							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 阻塞
 | 
				
			||||||
 | 
							pprofGroup.GET("/goroutine", gin.WrapH(pprof.Handler("goroutine")))
 | 
				
			||||||
 | 
							pprofGroup.GET("/heap", gin.WrapH(pprof.Handler("heap")))   // pprof 堆内存
 | 
				
			||||||
 | 
							pprofGroup.GET("/mutex", gin.WrapH(pprof.Handler("mutex"))) // pprof 互斥锁
 | 
				
			||||||
 | 
							pprofGroup.GET("/threadcreate", gin.WrapH(pprof.Handler("threadcreate")))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						a.logger.Info("pprof 接口注册成功")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 上行事件监听路由
 | 
				
			||||||
 | 
						a.engine.POST("/upstream", gin.WrapH(a.listenHandler.Handler())) // 处理设备上行事件
 | 
				
			||||||
 | 
						a.logger.Info("上行事件监听接口注册成功")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 添加 Swagger UI 路由, Swagger UI可在 /swagger/index.html 上找到
 | 
				
			||||||
 | 
						a.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // Swagger UI 接口
 | 
				
			||||||
 | 
						a.logger.Info("Swagger UI 接口注册成功")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- Authenticated Routes ---
 | 
				
			||||||
 | 
						// 所有在此注册的路由都需要通过 JWT 身份验证
 | 
				
			||||||
 | 
						authGroup := a.engine.Group("/api/v1")
 | 
				
			||||||
 | 
						authGroup.Use(middleware.AuthMiddleware(a.tokenService, a.userRepo)) // 1. 身份认证中间件
 | 
				
			||||||
 | 
						authGroup.Use(middleware.AuditLogMiddleware(a.auditService))         // 2. 审计日志中间件
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// 用户相关路由组
 | 
				
			||||||
 | 
							userGroup := authGroup.Group("/users")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								userGroup.GET("/:id/history", a.userController.ListUserHistory) // 获取用户操作历史
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("用户相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 设备相关路由组
 | 
				
			||||||
 | 
							deviceGroup := authGroup.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)               // 删除设备
 | 
				
			||||||
 | 
								deviceGroup.POST("/manual-control/:id", a.deviceController.ManualControl) // 手动控制设备
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("设备相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 区域主控相关路由组
 | 
				
			||||||
 | 
							areaControllerGroup := authGroup.Group("/area-controllers")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								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) // 删除区域主控
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("区域主控相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 设备模板相关路由组
 | 
				
			||||||
 | 
							deviceTemplateGroup := authGroup.Group("/device-templates")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								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) // 删除设备模板
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("设备模板相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 计划相关路由组
 | 
				
			||||||
 | 
							planGroup := authGroup.Group("/plans")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								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)   // 停止计划
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("计划相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 猪舍相关路由组
 | 
				
			||||||
 | 
							pigHouseGroup := authGroup.Group("/pig-houses")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								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) // 删除猪舍
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("猪舍相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 猪圈相关路由组
 | 
				
			||||||
 | 
							penGroup := authGroup.Group("/pens")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								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) // 更新猪圈状态
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("猪圈相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 猪群相关路由组
 | 
				
			||||||
 | 
							pigBatchGroup := authGroup.Group("/pig-batches")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								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("/assign-pens/:id", a.pigBatchController.AssignEmptyPensToBatch)                           // 为猪群分配空栏
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/reclassify-pen/:fromBatchID", a.pigBatchController.ReclassifyPenToNewBatch)              // 将猪栏划拨到新群
 | 
				
			||||||
 | 
								penGroup.DELETE("/remove-pen/:penID/:batchID", a.pigBatchController.RemoveEmptyPenFromBatch)                  // 从猪群移除空栏
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/move-pigs-into-pen/:id", a.pigBatchController.MovePigsIntoPen)                           // 将猪只从“虚拟库存”移入指定猪栏
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/sell-pigs/:id", a.pigBatchController.SellPigs)                                           // 处理卖猪业务
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/buy-pigs/:id", a.pigBatchController.BuyPigs)                                             // 处理买猪业务
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/transfer-across-batches/:sourceBatchID", a.pigBatchController.TransferPigsAcrossBatches) // 跨猪群调栏
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/transfer-within-batch/:id", a.pigBatchController.TransferPigsWithinBatch)                // 群内调栏
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/record-sick-pigs/:id", a.pigBatchController.RecordSickPigs)                              // 记录新增病猪事件
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/record-sick-pig-recovery/:id", a.pigBatchController.RecordSickPigRecovery)               // 记录病猪康复事件
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/record-sick-pig-death/:id", a.pigBatchController.RecordSickPigDeath)                     // 记录病猪死亡事件
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/record-sick-pig-cull/:id", a.pigBatchController.RecordSickPigCull)                       // 记录病猪淘汰事件
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/record-death/:id", a.pigBatchController.RecordDeath)                                     // 记录正常猪只死亡事件
 | 
				
			||||||
 | 
								pigBatchGroup.POST("/record-cull/:id", a.pigBatchController.RecordCull)                                       // 记录正常猪只淘汰事件
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("猪群相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 数据监控相关路由组
 | 
				
			||||||
 | 
							monitorGroup := authGroup.Group("/monitor")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								monitorGroup.GET("/sensor-data", a.monitorController.ListSensorData)
 | 
				
			||||||
 | 
								monitorGroup.GET("/device-command-logs", a.monitorController.ListDeviceCommandLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/plan-execution-logs", a.monitorController.ListPlanExecutionLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/task-execution-logs", a.monitorController.ListTaskExecutionLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/pending-collections", a.monitorController.ListPendingCollections)
 | 
				
			||||||
 | 
								monitorGroup.GET("/user-action-logs", a.monitorController.ListUserActionLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/raw-material-purchases", a.monitorController.ListRawMaterialPurchases)
 | 
				
			||||||
 | 
								monitorGroup.GET("/raw-material-stock-logs", a.monitorController.ListRawMaterialStockLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/feed-usage-records", a.monitorController.ListFeedUsageRecords)
 | 
				
			||||||
 | 
								monitorGroup.GET("/medication-logs", a.monitorController.ListMedicationLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/pig-batch-logs", a.monitorController.ListPigBatchLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/weighing-batches", a.monitorController.ListWeighingBatches)
 | 
				
			||||||
 | 
								monitorGroup.GET("/weighing-records", a.monitorController.ListWeighingRecords)
 | 
				
			||||||
 | 
								monitorGroup.GET("/pig-transfer-logs", a.monitorController.ListPigTransferLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/pig-sick-logs", a.monitorController.ListPigSickLogs)
 | 
				
			||||||
 | 
								monitorGroup.GET("/pig-purchases", a.monitorController.ListPigPurchases)
 | 
				
			||||||
 | 
								monitorGroup.GET("/pig-sales", a.monitorController.ListPigSales)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.logger.Info("数据监控相关接口注册成功 (需要认证和审计)")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										841
									
								
								internal/app/controller/monitor/monitor_controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										841
									
								
								internal/app/controller/monitor/monitor_controller.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,841 @@
 | 
				
			|||||||
 | 
					package monitor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
 | 
				
			||||||
 | 
						"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"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Controller 监控控制器,封装了所有与数据监控相关的业务逻辑
 | 
				
			||||||
 | 
					type Controller struct {
 | 
				
			||||||
 | 
						monitorService service.MonitorService
 | 
				
			||||||
 | 
						logger         *logs.Logger
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewController 创建一个新的监控控制器实例
 | 
				
			||||||
 | 
					func NewController(monitorService service.MonitorService, logger *logs.Logger) *Controller {
 | 
				
			||||||
 | 
						return &Controller{
 | 
				
			||||||
 | 
							monitorService: monitorService,
 | 
				
			||||||
 | 
							logger:         logger,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListSensorData godoc
 | 
				
			||||||
 | 
					// @Summary      获取传感器数据列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取传感器数据
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListSensorDataRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListSensorDataResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/sensor-data [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListSensorData(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取传感器数据列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListSensorDataRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.SensorDataListOptions{
 | 
				
			||||||
 | 
							DeviceID:  req.DeviceID,
 | 
				
			||||||
 | 
							OrderBy:   req.OrderBy,
 | 
				
			||||||
 | 
							StartTime: req.StartTime,
 | 
				
			||||||
 | 
							EndTime:   req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.SensorType != nil {
 | 
				
			||||||
 | 
							sensorType := models.SensorType(*req.SensorType)
 | 
				
			||||||
 | 
							opts.SensorType = &sensorType
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListSensorData(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取传感器数据失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListSensorDataResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取传感器数据成功", resp, actionType, "获取传感器数据成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListDeviceCommandLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取设备命令日志列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取设备命令日志
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListDeviceCommandLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListDeviceCommandLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/device-command-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListDeviceCommandLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取设备命令日志列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListDeviceCommandLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.DeviceCommandLogListOptions{
 | 
				
			||||||
 | 
							DeviceID:        req.DeviceID,
 | 
				
			||||||
 | 
							ReceivedSuccess: req.ReceivedSuccess,
 | 
				
			||||||
 | 
							OrderBy:         req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:       req.StartTime,
 | 
				
			||||||
 | 
							EndTime:         req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListDeviceCommandLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备命令日志失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListDeviceCommandLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备命令日志成功", resp, actionType, "获取设备命令日志成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPlanExecutionLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取计划执行日志列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取计划执行日志
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListPlanExecutionLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListPlanExecutionLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/plan-execution-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListPlanExecutionLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取计划执行日志列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListPlanExecutionLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.PlanExecutionLogListOptions{
 | 
				
			||||||
 | 
							PlanID:    req.PlanID,
 | 
				
			||||||
 | 
							OrderBy:   req.OrderBy,
 | 
				
			||||||
 | 
							StartTime: req.StartTime,
 | 
				
			||||||
 | 
							EndTime:   req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Status != nil {
 | 
				
			||||||
 | 
							status := models.ExecutionStatus(*req.Status)
 | 
				
			||||||
 | 
							opts.Status = &status
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListPlanExecutionLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取计划执行日志失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListPlanExecutionLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取计划执行日志成功", resp, actionType, "获取计划执行日志成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListTaskExecutionLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取任务执行日志列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取任务执行日志
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListTaskExecutionLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListTaskExecutionLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/task-execution-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListTaskExecutionLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取任务执行日志列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListTaskExecutionLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.TaskExecutionLogListOptions{
 | 
				
			||||||
 | 
							PlanExecutionLogID: req.PlanExecutionLogID,
 | 
				
			||||||
 | 
							TaskID:             req.TaskID,
 | 
				
			||||||
 | 
							OrderBy:            req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:          req.StartTime,
 | 
				
			||||||
 | 
							EndTime:            req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Status != nil {
 | 
				
			||||||
 | 
							status := models.ExecutionStatus(*req.Status)
 | 
				
			||||||
 | 
							opts.Status = &status
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListTaskExecutionLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取任务执行日志失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListTaskExecutionLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取任务执行日志成功", resp, actionType, "获取任务执行日志成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPendingCollections godoc
 | 
				
			||||||
 | 
					// @Summary      获取待采集请求列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取待采集请求
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListPendingCollectionRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListPendingCollectionResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/pending-collections [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListPendingCollections(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取待采集请求列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListPendingCollectionRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.PendingCollectionListOptions{
 | 
				
			||||||
 | 
							DeviceID:  req.DeviceID,
 | 
				
			||||||
 | 
							OrderBy:   req.OrderBy,
 | 
				
			||||||
 | 
							StartTime: req.StartTime,
 | 
				
			||||||
 | 
							EndTime:   req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Status != nil {
 | 
				
			||||||
 | 
							status := models.PendingCollectionStatus(*req.Status)
 | 
				
			||||||
 | 
							opts.Status = &status
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListPendingCollections(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取待采集请求失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListPendingCollectionResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取待采集请求成功", resp, actionType, "获取待采集请求成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListUserActionLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取用户操作日志列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取用户操作日志
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListUserActionLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListUserActionLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/user-action-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListUserActionLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取用户操作日志列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListUserActionLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.UserActionLogListOptions{
 | 
				
			||||||
 | 
							UserID:     req.UserID,
 | 
				
			||||||
 | 
							Username:   req.Username,
 | 
				
			||||||
 | 
							ActionType: req.ActionType,
 | 
				
			||||||
 | 
							OrderBy:    req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:  req.StartTime,
 | 
				
			||||||
 | 
							EndTime:    req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Status != nil {
 | 
				
			||||||
 | 
							status := models.AuditStatus(*req.Status)
 | 
				
			||||||
 | 
							opts.Status = &status
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListUserActionLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取用户操作日志失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListUserActionLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取用户操作日志成功", resp, actionType, "获取用户操作日志成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialPurchases godoc
 | 
				
			||||||
 | 
					// @Summary      获取原料采购记录列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取原料采购记录
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListRawMaterialPurchaseRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListRawMaterialPurchaseResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/raw-material-purchases [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListRawMaterialPurchases(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取原料采购记录列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListRawMaterialPurchaseRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.RawMaterialPurchaseListOptions{
 | 
				
			||||||
 | 
							RawMaterialID: req.RawMaterialID,
 | 
				
			||||||
 | 
							Supplier:      req.Supplier,
 | 
				
			||||||
 | 
							OrderBy:       req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:     req.StartTime,
 | 
				
			||||||
 | 
							EndTime:       req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListRawMaterialPurchases(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取原料采购记录失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListRawMaterialPurchaseResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取原料采购记录成功", resp, actionType, "获取原料采购记录成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialStockLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取原料库存日志列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取原料库存日志
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListRawMaterialStockLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListRawMaterialStockLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/raw-material-stock-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListRawMaterialStockLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取原料库存日志列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListRawMaterialStockLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.RawMaterialStockLogListOptions{
 | 
				
			||||||
 | 
							RawMaterialID: req.RawMaterialID,
 | 
				
			||||||
 | 
							SourceID:      req.SourceID,
 | 
				
			||||||
 | 
							OrderBy:       req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:     req.StartTime,
 | 
				
			||||||
 | 
							EndTime:       req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.SourceType != nil {
 | 
				
			||||||
 | 
							sourceType := models.StockLogSourceType(*req.SourceType)
 | 
				
			||||||
 | 
							opts.SourceType = &sourceType
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListRawMaterialStockLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取原料库存日志失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListRawMaterialStockLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取原料库存日志成功", resp, actionType, "获取原料库存日志成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListFeedUsageRecords godoc
 | 
				
			||||||
 | 
					// @Summary      获取饲料使用记录列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取饲料使用记录
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListFeedUsageRecordRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListFeedUsageRecordResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/feed-usage-records [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListFeedUsageRecords(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取饲料使用记录列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListFeedUsageRecordRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.FeedUsageRecordListOptions{
 | 
				
			||||||
 | 
							PenID:         req.PenID,
 | 
				
			||||||
 | 
							FeedFormulaID: req.FeedFormulaID,
 | 
				
			||||||
 | 
							OperatorID:    req.OperatorID,
 | 
				
			||||||
 | 
							OrderBy:       req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:     req.StartTime,
 | 
				
			||||||
 | 
							EndTime:       req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListFeedUsageRecords(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取饲料使用记录失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListFeedUsageRecordResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取饲料使用记录成功", resp, actionType, "获取饲料使用记录成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListMedicationLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取用药记录列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取用药记录
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListMedicationLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListMedicationLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/medication-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListMedicationLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取用药记录列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListMedicationLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.MedicationLogListOptions{
 | 
				
			||||||
 | 
							PigBatchID:   req.PigBatchID,
 | 
				
			||||||
 | 
							MedicationID: req.MedicationID,
 | 
				
			||||||
 | 
							OperatorID:   req.OperatorID,
 | 
				
			||||||
 | 
							OrderBy:      req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:    req.StartTime,
 | 
				
			||||||
 | 
							EndTime:      req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Reason != nil {
 | 
				
			||||||
 | 
							reason := models.MedicationReasonType(*req.Reason)
 | 
				
			||||||
 | 
							opts.Reason = &reason
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListMedicationLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取用药记录失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListMedicationLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取用药记录成功", resp, actionType, "获取用药记录成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigBatchLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取猪批次日志列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取猪批次日志
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListPigBatchLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListPigBatchLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/pig-batch-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListPigBatchLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取猪批次日志列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListPigBatchLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.PigBatchLogListOptions{
 | 
				
			||||||
 | 
							PigBatchID: req.PigBatchID,
 | 
				
			||||||
 | 
							OperatorID: req.OperatorID,
 | 
				
			||||||
 | 
							OrderBy:    req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:  req.StartTime,
 | 
				
			||||||
 | 
							EndTime:    req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.ChangeType != nil {
 | 
				
			||||||
 | 
							changeType := models.LogChangeType(*req.ChangeType)
 | 
				
			||||||
 | 
							opts.ChangeType = &changeType
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListPigBatchLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪批次日志失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListPigBatchLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取猪批次日志成功", resp, actionType, "获取猪批次日志成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingBatches godoc
 | 
				
			||||||
 | 
					// @Summary      获取批次称重记录列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取批次称重记录
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListWeighingBatchRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListWeighingBatchResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/weighing-batches [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListWeighingBatches(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取批次称重记录列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListWeighingBatchRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.WeighingBatchListOptions{
 | 
				
			||||||
 | 
							PigBatchID: req.PigBatchID,
 | 
				
			||||||
 | 
							OrderBy:    req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:  req.StartTime,
 | 
				
			||||||
 | 
							EndTime:    req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListWeighingBatches(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取批次称重记录失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListWeighingBatchResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取批次称重记录成功", resp, actionType, "获取批次称重记录成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingRecords godoc
 | 
				
			||||||
 | 
					// @Summary      获取单次称重记录列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取单次称重记录
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListWeighingRecordRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListWeighingRecordResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/weighing-records [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListWeighingRecords(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取单次称重记录列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListWeighingRecordRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.WeighingRecordListOptions{
 | 
				
			||||||
 | 
							WeighingBatchID: req.WeighingBatchID,
 | 
				
			||||||
 | 
							PenID:           req.PenID,
 | 
				
			||||||
 | 
							OperatorID:      req.OperatorID,
 | 
				
			||||||
 | 
							OrderBy:         req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:       req.StartTime,
 | 
				
			||||||
 | 
							EndTime:         req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListWeighingRecords(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取单次称重记录失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListWeighingRecordResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取单次称重记录成功", resp, actionType, "获取单次称重记录成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigTransferLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取猪只迁移日志列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取猪只迁移日志
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListPigTransferLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListPigTransferLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/pig-transfer-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListPigTransferLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取猪只迁移日志列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListPigTransferLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.PigTransferLogListOptions{
 | 
				
			||||||
 | 
							PigBatchID:    req.PigBatchID,
 | 
				
			||||||
 | 
							PenID:         req.PenID,
 | 
				
			||||||
 | 
							OperatorID:    req.OperatorID,
 | 
				
			||||||
 | 
							CorrelationID: req.CorrelationID,
 | 
				
			||||||
 | 
							OrderBy:       req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:     req.StartTime,
 | 
				
			||||||
 | 
							EndTime:       req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.TransferType != nil {
 | 
				
			||||||
 | 
							transferType := models.PigTransferType(*req.TransferType)
 | 
				
			||||||
 | 
							opts.TransferType = &transferType
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListPigTransferLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪只迁移日志失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListPigTransferLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取猪只迁移日志成功", resp, actionType, "获取猪只迁移日志成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSickLogs godoc
 | 
				
			||||||
 | 
					// @Summary      获取病猪日志列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取病猪日志
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListPigSickLogRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListPigSickLogResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/pig-sick-logs [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListPigSickLogs(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取病猪日志列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListPigSickLogRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.PigSickLogListOptions{
 | 
				
			||||||
 | 
							PigBatchID: req.PigBatchID,
 | 
				
			||||||
 | 
							PenID:      req.PenID,
 | 
				
			||||||
 | 
							OperatorID: req.OperatorID,
 | 
				
			||||||
 | 
							OrderBy:    req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:  req.StartTime,
 | 
				
			||||||
 | 
							EndTime:    req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Reason != nil {
 | 
				
			||||||
 | 
							reason := models.PigBatchSickPigReasonType(*req.Reason)
 | 
				
			||||||
 | 
							opts.Reason = &reason
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.TreatmentLocation != nil {
 | 
				
			||||||
 | 
							treatmentLocation := models.PigBatchSickPigTreatmentLocation(*req.TreatmentLocation)
 | 
				
			||||||
 | 
							opts.TreatmentLocation = &treatmentLocation
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListPigSickLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取病猪日志失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListPigSickLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取病猪日志成功", resp, actionType, "获取病猪日志成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigPurchases godoc
 | 
				
			||||||
 | 
					// @Summary      获取猪只采购记录列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取猪只采购记录
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListPigPurchaseRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListPigPurchaseResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/pig-purchases [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListPigPurchases(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取猪只采购记录列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListPigPurchaseRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.PigPurchaseListOptions{
 | 
				
			||||||
 | 
							PigBatchID: req.PigBatchID,
 | 
				
			||||||
 | 
							Supplier:   req.Supplier,
 | 
				
			||||||
 | 
							OperatorID: req.OperatorID,
 | 
				
			||||||
 | 
							OrderBy:    req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:  req.StartTime,
 | 
				
			||||||
 | 
							EndTime:    req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListPigPurchases(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪只采购记录失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListPigPurchaseResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取猪只采购记录成功", resp, actionType, "获取猪只采购记录成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSales godoc
 | 
				
			||||||
 | 
					// @Summary      获取猪只售卖记录列表
 | 
				
			||||||
 | 
					// @Description  根据提供的过滤条件,分页获取猪只售卖记录
 | 
				
			||||||
 | 
					// @Tags         数据监控
 | 
				
			||||||
 | 
					// @Security     BearerAuth
 | 
				
			||||||
 | 
					// @Produce      json
 | 
				
			||||||
 | 
					// @Param        query query dto.ListPigSaleRequest true "查询参数"
 | 
				
			||||||
 | 
					// @Success      200 {object} controller.Response{data=dto.ListPigSaleResponse}
 | 
				
			||||||
 | 
					// @Router       /api/v1/monitor/pig-sales [get]
 | 
				
			||||||
 | 
					func (c *Controller) ListPigSales(ctx *gin.Context) {
 | 
				
			||||||
 | 
						const actionType = "获取猪只售卖记录列表"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var req dto.ListPigSaleRequest
 | 
				
			||||||
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := repository.PigSaleListOptions{
 | 
				
			||||||
 | 
							PigBatchID: req.PigBatchID,
 | 
				
			||||||
 | 
							Buyer:      req.Buyer,
 | 
				
			||||||
 | 
							OperatorID: req.OperatorID,
 | 
				
			||||||
 | 
							OrderBy:    req.OrderBy,
 | 
				
			||||||
 | 
							StartTime:  req.StartTime,
 | 
				
			||||||
 | 
							EndTime:    req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, total, err := c.monitorService.ListPigSales(opts, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", req)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取猪只售卖记录失败: "+err.Error(), actionType, "服务层查询失败", req)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := dto.NewListPigSaleResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功, 获取到 %d 条记录, 总计 %d 条", actionType, len(data), total)
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取猪只售卖记录成功", resp, actionType, "获取猪只售卖记录成功", req)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,11 +1,12 @@
 | 
				
			|||||||
package user
 | 
					package user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/controller"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/token"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/domain/token"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
@@ -17,18 +18,18 @@ import (
 | 
				
			|||||||
// Controller 用户控制器
 | 
					// Controller 用户控制器
 | 
				
			||||||
type Controller struct {
 | 
					type Controller struct {
 | 
				
			||||||
	userRepo       repository.UserRepository
 | 
						userRepo       repository.UserRepository
 | 
				
			||||||
	auditRepo    repository.UserActionLogRepository
 | 
						monitorService service.MonitorService
 | 
				
			||||||
	logger       *logs.Logger
 | 
					 | 
				
			||||||
	tokenService   token.TokenService // 注入 token 服务
 | 
						tokenService   token.TokenService // 注入 token 服务
 | 
				
			||||||
 | 
						logger         *logs.Logger
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewController 创建用户控制器实例
 | 
					// NewController 创建用户控制器实例
 | 
				
			||||||
func NewController(userRepo repository.UserRepository, auditRepo repository.UserActionLogRepository, logger *logs.Logger, tokenService token.TokenService) *Controller {
 | 
					func NewController(userRepo repository.UserRepository, monitorService service.MonitorService, logger *logs.Logger, tokenService token.TokenService) *Controller {
 | 
				
			||||||
	return &Controller{
 | 
						return &Controller{
 | 
				
			||||||
		userRepo:       userRepo,
 | 
							userRepo:       userRepo,
 | 
				
			||||||
		auditRepo:    auditRepo,
 | 
							monitorService: monitorService,
 | 
				
			||||||
		logger:       logger,
 | 
					 | 
				
			||||||
		tokenService:   tokenService,
 | 
							tokenService:   tokenService,
 | 
				
			||||||
 | 
							logger:         logger,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -128,20 +129,18 @@ func (c *Controller) Login(ctx *gin.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ListUserHistory godoc
 | 
					// ListUserHistory godoc
 | 
				
			||||||
// @Summary      获取指定用户的操作历史
 | 
					// @Summary      获取指定用户的操作历史
 | 
				
			||||||
// @Description  根据用户ID,分页获取该用户的操作审计日志。
 | 
					// @Description  根据用户ID,分页获取该用户的操作审计日志。支持与通用日志查询接口相同的过滤和排序参数。
 | 
				
			||||||
// @Tags         用户管理
 | 
					// @Tags         用户管理
 | 
				
			||||||
// @Security     BearerAuth
 | 
					// @Security     BearerAuth
 | 
				
			||||||
// @Produce      json
 | 
					// @Produce      json
 | 
				
			||||||
// @Param        id    path      int  true  "用户ID"
 | 
					// @Param        id    path      int  true  "用户ID"
 | 
				
			||||||
// @Param        page query     int    false "页码" default(1)
 | 
					// @Param        query query     dto.ListUserActionLogRequest false "查询参数 (除了 user_id,它被路径中的ID覆盖)"
 | 
				
			||||||
// @Param        page_size query int    false "每页大小" default(10)
 | 
					// @Success      200   {object}  controller.Response{data=dto.ListUserActionLogResponse} "业务码为200代表成功获取"
 | 
				
			||||||
// @Param        action_type query string false "按操作类型过滤"
 | 
					 | 
				
			||||||
// @Success      200  {object}  controller.Response{data=dto.ListHistoryResponse} "业务码为200代表成功获取"
 | 
					 | 
				
			||||||
// @Router       /api/v1/users/{id}/history [get]
 | 
					// @Router       /api/v1/users/{id}/history [get]
 | 
				
			||||||
func (c *Controller) ListUserHistory(ctx *gin.Context) {
 | 
					func (c *Controller) ListUserHistory(ctx *gin.Context) {
 | 
				
			||||||
	const actionType = "获取用户操作历史"
 | 
						const actionType = "获取用户操作历史"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 1. 解析路径中的用户ID
 | 
						// 1. 解析路径中的用户ID,它的优先级最高
 | 
				
			||||||
	userIDStr := ctx.Param("id")
 | 
						userIDStr := ctx.Param("id")
 | 
				
			||||||
	userID, err := strconv.ParseUint(userIDStr, 10, 64)
 | 
						userID, err := strconv.ParseUint(userIDStr, 10, 64)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -150,52 +149,46 @@ func (c *Controller) ListUserHistory(ctx *gin.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 2. 解析分页和过滤参数
 | 
						// 2. 绑定通用的查询请求 DTO
 | 
				
			||||||
	page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
 | 
						var req dto.ListUserActionLogRequest
 | 
				
			||||||
	pageSize, _ := strconv.Atoi(ctx.DefaultQuery("page_size", "10"))
 | 
						if err := ctx.ShouldBindQuery(&req); err != nil {
 | 
				
			||||||
	actionTypeFilter := ctx.Query("action_type")
 | 
							c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
 | 
				
			||||||
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的查询参数: "+err.Error(), actionType, "参数绑定失败", req)
 | 
				
			||||||
	// 确保分页参数有效
 | 
					 | 
				
			||||||
	if page <= 0 {
 | 
					 | 
				
			||||||
		page = 1
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if pageSize <= 0 || pageSize > 100 {
 | 
					 | 
				
			||||||
		pageSize = 10
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 3. 调用审计仓库层获取历史数据
 | 
					 | 
				
			||||||
	id := uint(userID)
 | 
					 | 
				
			||||||
	findOptions := repository.FindAuditLogOptions{
 | 
					 | 
				
			||||||
		UserID:     &id,
 | 
					 | 
				
			||||||
		ActionType: actionTypeFilter,
 | 
					 | 
				
			||||||
		Page:       page,
 | 
					 | 
				
			||||||
		PageSize:   pageSize,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	logs, total, err := c.auditRepo.List(findOptions)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		c.logger.Errorf("%s: 查询历史记录失败: %v, Options: %+v", actionType, err, findOptions)
 | 
					 | 
				
			||||||
		controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "查询历史记录失败", actionType, "查询历史记录失败", findOptions)
 | 
					 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 4. 将数据库模型转换为响应 DTO
 | 
						// 3. 准备 Service 调用参数,并强制使用路径中的 UserID
 | 
				
			||||||
	historyResponses := make([]dto.HistoryResponse, 0, len(logs))
 | 
						uid := uint(userID)
 | 
				
			||||||
	for _, log := range logs {
 | 
						req.UserID = &uid // 强制覆盖
 | 
				
			||||||
		historyResponses = append(historyResponses, dto.HistoryResponse{
 | 
					
 | 
				
			||||||
			UserID:         log.UserID,
 | 
						opts := repository.UserActionLogListOptions{
 | 
				
			||||||
			Username:       log.Username,
 | 
							UserID:     req.UserID,
 | 
				
			||||||
			ActionType:     log.ActionType,
 | 
							Username:   req.Username,
 | 
				
			||||||
			Description:    log.Description,
 | 
							ActionType: req.ActionType,
 | 
				
			||||||
			TargetResource: log.TargetResource,
 | 
							OrderBy:    req.OrderBy,
 | 
				
			||||||
			Time:           log.Time.Format(time.RFC3339),
 | 
							StartTime:  req.StartTime,
 | 
				
			||||||
		})
 | 
							EndTime:    req.EndTime,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Status != nil {
 | 
				
			||||||
 | 
							status := models.AuditStatus(*req.Status)
 | 
				
			||||||
 | 
							opts.Status = &status
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 5. 发送成功响应
 | 
						// 4. 调用 monitorService,复用其业务逻辑
 | 
				
			||||||
	resp := dto.ListHistoryResponse{
 | 
						data, total, err := c.monitorService.ListUserActionLogs(opts, req.Page, req.PageSize)
 | 
				
			||||||
		History: historyResponses,
 | 
						if err != nil {
 | 
				
			||||||
		Total:   total,
 | 
							if errors.Is(err, repository.ErrInvalidPagination) {
 | 
				
			||||||
 | 
								c.logger.Warnf("%s: 无效的分页参数: %v", actionType, err)
 | 
				
			||||||
 | 
								controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的分页参数: "+err.Error(), actionType, "无效分页参数", opts)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	c.logger.Infof("%s: 成功获取用户 %d 的操作历史, 数量: %d", actionType, userID, len(historyResponses))
 | 
							c.logger.Errorf("%s: 服务层查询失败: %v", actionType, err)
 | 
				
			||||||
	controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取用户操作历史成功", resp, actionType, "获取用户操作历史成功", resp)
 | 
							controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取用户历史记录失败", actionType, "服务层查询失败", opts)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 5. 使用复用的 DTO 构建并发送成功响应
 | 
				
			||||||
 | 
						resp := dto.NewListUserActionLogResponse(data, total, req.Page, req.PageSize)
 | 
				
			||||||
 | 
						c.logger.Infof("%s: 成功获取用户 %d 的操作历史, 数量: %d", actionType, userID, len(data))
 | 
				
			||||||
 | 
						controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取用户操作历史成功", resp, actionType, "获取用户操作历史成功", opts)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										483
									
								
								internal/app/dto/monitor_converter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								internal/app/dto/monitor_converter.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,483 @@
 | 
				
			|||||||
 | 
					package dto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListSensorDataResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListSensorDataResponse(data []models.SensorData, total int64, page, pageSize int) *ListSensorDataResponse {
 | 
				
			||||||
 | 
						dtos := make([]SensorDataDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = SensorDataDTO{
 | 
				
			||||||
 | 
								Time:                 item.Time,
 | 
				
			||||||
 | 
								DeviceID:             item.DeviceID,
 | 
				
			||||||
 | 
								RegionalControllerID: item.RegionalControllerID,
 | 
				
			||||||
 | 
								SensorType:           item.SensorType,
 | 
				
			||||||
 | 
								Data:                 json.RawMessage(item.Data),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListSensorDataResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListDeviceCommandLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListDeviceCommandLogResponse(data []models.DeviceCommandLog, total int64, page, pageSize int) *ListDeviceCommandLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]DeviceCommandLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = DeviceCommandLogDTO{
 | 
				
			||||||
 | 
								MessageID:       item.MessageID,
 | 
				
			||||||
 | 
								DeviceID:        item.DeviceID,
 | 
				
			||||||
 | 
								SentAt:          item.SentAt,
 | 
				
			||||||
 | 
								AcknowledgedAt:  item.AcknowledgedAt,
 | 
				
			||||||
 | 
								ReceivedSuccess: item.ReceivedSuccess,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListDeviceCommandLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListPlanExecutionLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListPlanExecutionLogResponse(data []models.PlanExecutionLog, total int64, page, pageSize int) *ListPlanExecutionLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]PlanExecutionLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = PlanExecutionLogDTO{
 | 
				
			||||||
 | 
								ID:        item.ID,
 | 
				
			||||||
 | 
								CreatedAt: item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt: item.UpdatedAt,
 | 
				
			||||||
 | 
								PlanID:    item.PlanID,
 | 
				
			||||||
 | 
								Status:    item.Status,
 | 
				
			||||||
 | 
								StartedAt: item.StartedAt,
 | 
				
			||||||
 | 
								EndedAt:   item.EndedAt,
 | 
				
			||||||
 | 
								Error:     item.Error,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListPlanExecutionLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListTaskExecutionLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListTaskExecutionLogResponse(data []models.TaskExecutionLog, total int64, page, pageSize int) *ListTaskExecutionLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]TaskExecutionLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = TaskExecutionLogDTO{
 | 
				
			||||||
 | 
								ID:                 item.ID,
 | 
				
			||||||
 | 
								CreatedAt:          item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt:          item.UpdatedAt,
 | 
				
			||||||
 | 
								PlanExecutionLogID: item.PlanExecutionLogID,
 | 
				
			||||||
 | 
								TaskID:             item.TaskID,
 | 
				
			||||||
 | 
								Task: TaskDTO{
 | 
				
			||||||
 | 
									ID:          uint(item.Task.ID),
 | 
				
			||||||
 | 
									Name:        item.Task.Name,
 | 
				
			||||||
 | 
									Description: item.Task.Description,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Status:    item.Status,
 | 
				
			||||||
 | 
								Output:    item.Output,
 | 
				
			||||||
 | 
								StartedAt: item.StartedAt,
 | 
				
			||||||
 | 
								EndedAt:   item.EndedAt,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListTaskExecutionLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListPendingCollectionResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListPendingCollectionResponse(data []models.PendingCollection, total int64, page, pageSize int) *ListPendingCollectionResponse {
 | 
				
			||||||
 | 
						dtos := make([]PendingCollectionDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = PendingCollectionDTO{
 | 
				
			||||||
 | 
								CorrelationID:   item.CorrelationID,
 | 
				
			||||||
 | 
								DeviceID:        item.DeviceID,
 | 
				
			||||||
 | 
								CommandMetadata: item.CommandMetadata,
 | 
				
			||||||
 | 
								Status:          item.Status,
 | 
				
			||||||
 | 
								FulfilledAt:     item.FulfilledAt,
 | 
				
			||||||
 | 
								CreatedAt:       item.CreatedAt,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListPendingCollectionResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListUserActionLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListUserActionLogResponse(data []models.UserActionLog, total int64, page, pageSize int) *ListUserActionLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]UserActionLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = UserActionLogDTO{
 | 
				
			||||||
 | 
								ID:             item.ID,
 | 
				
			||||||
 | 
								Time:           item.Time,
 | 
				
			||||||
 | 
								UserID:         item.UserID,
 | 
				
			||||||
 | 
								Username:       item.Username,
 | 
				
			||||||
 | 
								SourceIP:       item.SourceIP,
 | 
				
			||||||
 | 
								ActionType:     item.ActionType,
 | 
				
			||||||
 | 
								TargetResource: json.RawMessage(item.TargetResource),
 | 
				
			||||||
 | 
								Description:    item.Description,
 | 
				
			||||||
 | 
								Status:         item.Status,
 | 
				
			||||||
 | 
								HTTPPath:       item.HTTPPath,
 | 
				
			||||||
 | 
								HTTPMethod:     item.HTTPMethod,
 | 
				
			||||||
 | 
								ResultDetails:  item.ResultDetails,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListUserActionLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListRawMaterialPurchaseResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListRawMaterialPurchaseResponse(data []models.RawMaterialPurchase, total int64, page, pageSize int) *ListRawMaterialPurchaseResponse {
 | 
				
			||||||
 | 
						dtos := make([]RawMaterialPurchaseDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = RawMaterialPurchaseDTO{
 | 
				
			||||||
 | 
								ID:            item.ID,
 | 
				
			||||||
 | 
								RawMaterialID: item.RawMaterialID,
 | 
				
			||||||
 | 
								RawMaterial: RawMaterialDTO{
 | 
				
			||||||
 | 
									ID:   item.RawMaterial.ID,
 | 
				
			||||||
 | 
									Name: item.RawMaterial.Name,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Supplier:     item.Supplier,
 | 
				
			||||||
 | 
								Amount:       item.Amount,
 | 
				
			||||||
 | 
								UnitPrice:    item.UnitPrice,
 | 
				
			||||||
 | 
								TotalPrice:   item.TotalPrice,
 | 
				
			||||||
 | 
								PurchaseDate: item.PurchaseDate,
 | 
				
			||||||
 | 
								CreatedAt:    item.CreatedAt,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListRawMaterialPurchaseResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListRawMaterialStockLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListRawMaterialStockLogResponse(data []models.RawMaterialStockLog, total int64, page, pageSize int) *ListRawMaterialStockLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]RawMaterialStockLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = RawMaterialStockLogDTO{
 | 
				
			||||||
 | 
								ID:            item.ID,
 | 
				
			||||||
 | 
								RawMaterialID: item.RawMaterialID,
 | 
				
			||||||
 | 
								ChangeAmount:  item.ChangeAmount,
 | 
				
			||||||
 | 
								SourceType:    item.SourceType,
 | 
				
			||||||
 | 
								SourceID:      item.SourceID,
 | 
				
			||||||
 | 
								HappenedAt:    item.HappenedAt,
 | 
				
			||||||
 | 
								Remarks:       item.Remarks,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListRawMaterialStockLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListFeedUsageRecordResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListFeedUsageRecordResponse(data []models.FeedUsageRecord, total int64, page, pageSize int) *ListFeedUsageRecordResponse {
 | 
				
			||||||
 | 
						dtos := make([]FeedUsageRecordDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = FeedUsageRecordDTO{
 | 
				
			||||||
 | 
								ID:    item.ID,
 | 
				
			||||||
 | 
								PenID: item.PenID,
 | 
				
			||||||
 | 
								Pen: PenDTO{
 | 
				
			||||||
 | 
									ID:   item.Pen.ID,
 | 
				
			||||||
 | 
									Name: item.Pen.PenNumber,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								FeedFormulaID: item.FeedFormulaID,
 | 
				
			||||||
 | 
								FeedFormula: FeedFormulaDTO{
 | 
				
			||||||
 | 
									ID:   item.FeedFormula.ID,
 | 
				
			||||||
 | 
									Name: item.FeedFormula.Name,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Amount:     item.Amount,
 | 
				
			||||||
 | 
								RecordedAt: item.RecordedAt,
 | 
				
			||||||
 | 
								OperatorID: item.OperatorID,
 | 
				
			||||||
 | 
								Remarks:    item.Remarks,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListFeedUsageRecordResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListMedicationLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListMedicationLogResponse(data []models.MedicationLog, total int64, page, pageSize int) *ListMedicationLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]MedicationLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = MedicationLogDTO{
 | 
				
			||||||
 | 
								ID:           item.ID,
 | 
				
			||||||
 | 
								PigBatchID:   item.PigBatchID,
 | 
				
			||||||
 | 
								MedicationID: item.MedicationID,
 | 
				
			||||||
 | 
								Medication: MedicationDTO{
 | 
				
			||||||
 | 
									ID:   item.Medication.ID,
 | 
				
			||||||
 | 
									Name: item.Medication.Name,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								DosageUsed:  item.DosageUsed,
 | 
				
			||||||
 | 
								TargetCount: item.TargetCount,
 | 
				
			||||||
 | 
								Reason:      item.Reason,
 | 
				
			||||||
 | 
								Description: item.Description,
 | 
				
			||||||
 | 
								OperatorID:  item.OperatorID,
 | 
				
			||||||
 | 
								HappenedAt:  item.HappenedAt,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListMedicationLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListPigBatchLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListPigBatchLogResponse(data []models.PigBatchLog, total int64, page, pageSize int) *ListPigBatchLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]PigBatchLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = PigBatchLogDTO{
 | 
				
			||||||
 | 
								ID:          item.ID,
 | 
				
			||||||
 | 
								CreatedAt:   item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt:   item.UpdatedAt,
 | 
				
			||||||
 | 
								PigBatchID:  item.PigBatchID,
 | 
				
			||||||
 | 
								ChangeType:  item.ChangeType,
 | 
				
			||||||
 | 
								ChangeCount: item.ChangeCount,
 | 
				
			||||||
 | 
								Reason:      item.Reason,
 | 
				
			||||||
 | 
								BeforeCount: item.BeforeCount,
 | 
				
			||||||
 | 
								AfterCount:  item.AfterCount,
 | 
				
			||||||
 | 
								OperatorID:  item.OperatorID,
 | 
				
			||||||
 | 
								HappenedAt:  item.HappenedAt,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListPigBatchLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListWeighingBatchResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListWeighingBatchResponse(data []models.WeighingBatch, total int64, page, pageSize int) *ListWeighingBatchResponse {
 | 
				
			||||||
 | 
						dtos := make([]WeighingBatchDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = WeighingBatchDTO{
 | 
				
			||||||
 | 
								ID:           item.ID,
 | 
				
			||||||
 | 
								CreatedAt:    item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt:    item.UpdatedAt,
 | 
				
			||||||
 | 
								WeighingTime: item.WeighingTime,
 | 
				
			||||||
 | 
								Description:  item.Description,
 | 
				
			||||||
 | 
								PigBatchID:   item.PigBatchID,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListWeighingBatchResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListWeighingRecordResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListWeighingRecordResponse(data []models.WeighingRecord, total int64, page, pageSize int) *ListWeighingRecordResponse {
 | 
				
			||||||
 | 
						dtos := make([]WeighingRecordDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = WeighingRecordDTO{
 | 
				
			||||||
 | 
								ID:              item.ID,
 | 
				
			||||||
 | 
								CreatedAt:       item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt:       item.UpdatedAt,
 | 
				
			||||||
 | 
								Weight:          item.Weight,
 | 
				
			||||||
 | 
								WeighingBatchID: item.WeighingBatchID,
 | 
				
			||||||
 | 
								PenID:           item.PenID,
 | 
				
			||||||
 | 
								OperatorID:      item.OperatorID,
 | 
				
			||||||
 | 
								Remark:          item.Remark,
 | 
				
			||||||
 | 
								WeighingTime:    item.WeighingTime,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListWeighingRecordResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListPigTransferLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListPigTransferLogResponse(data []models.PigTransferLog, total int64, page, pageSize int) *ListPigTransferLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]PigTransferLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							// 注意:PigTransferLog 的 ID, CreatedAt, UpdatedAt 字段是 gorm.Model 嵌入的
 | 
				
			||||||
 | 
							dtos[i] = PigTransferLogDTO{
 | 
				
			||||||
 | 
								ID:            item.ID,
 | 
				
			||||||
 | 
								CreatedAt:     item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt:     item.UpdatedAt,
 | 
				
			||||||
 | 
								TransferTime:  item.TransferTime,
 | 
				
			||||||
 | 
								PigBatchID:    item.PigBatchID,
 | 
				
			||||||
 | 
								PenID:         item.PenID,
 | 
				
			||||||
 | 
								Quantity:      item.Quantity,
 | 
				
			||||||
 | 
								Type:          item.Type,
 | 
				
			||||||
 | 
								CorrelationID: item.CorrelationID,
 | 
				
			||||||
 | 
								OperatorID:    item.OperatorID,
 | 
				
			||||||
 | 
								Remarks:       item.Remarks,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListPigTransferLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListPigSickLogResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListPigSickLogResponse(data []models.PigSickLog, total int64, page, pageSize int) *ListPigSickLogResponse {
 | 
				
			||||||
 | 
						dtos := make([]PigSickLogDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = PigSickLogDTO{
 | 
				
			||||||
 | 
								ID:                item.ID,
 | 
				
			||||||
 | 
								CreatedAt:         item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt:         item.UpdatedAt,
 | 
				
			||||||
 | 
								PigBatchID:        item.PigBatchID,
 | 
				
			||||||
 | 
								PenID:             item.PenID,
 | 
				
			||||||
 | 
								ChangeCount:       item.ChangeCount,
 | 
				
			||||||
 | 
								Reason:            item.Reason,
 | 
				
			||||||
 | 
								BeforeCount:       item.BeforeCount,
 | 
				
			||||||
 | 
								AfterCount:        item.AfterCount,
 | 
				
			||||||
 | 
								Remarks:           item.Remarks,
 | 
				
			||||||
 | 
								TreatmentLocation: item.TreatmentLocation,
 | 
				
			||||||
 | 
								OperatorID:        item.OperatorID,
 | 
				
			||||||
 | 
								HappenedAt:        item.HappenedAt,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListPigSickLogResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListPigPurchaseResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListPigPurchaseResponse(data []models.PigPurchase, total int64, page, pageSize int) *ListPigPurchaseResponse {
 | 
				
			||||||
 | 
						dtos := make([]PigPurchaseDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = PigPurchaseDTO{
 | 
				
			||||||
 | 
								ID:           item.ID,
 | 
				
			||||||
 | 
								CreatedAt:    item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt:    item.UpdatedAt,
 | 
				
			||||||
 | 
								PigBatchID:   item.PigBatchID,
 | 
				
			||||||
 | 
								PurchaseDate: item.PurchaseDate,
 | 
				
			||||||
 | 
								Supplier:     item.Supplier,
 | 
				
			||||||
 | 
								Quantity:     item.Quantity,
 | 
				
			||||||
 | 
								UnitPrice:    item.UnitPrice,
 | 
				
			||||||
 | 
								TotalPrice:   item.TotalPrice,
 | 
				
			||||||
 | 
								Remarks:      item.Remarks,
 | 
				
			||||||
 | 
								OperatorID:   item.OperatorID,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListPigPurchaseResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewListPigSaleResponse 从模型数据创建列表响应 DTO
 | 
				
			||||||
 | 
					func NewListPigSaleResponse(data []models.PigSale, total int64, page, pageSize int) *ListPigSaleResponse {
 | 
				
			||||||
 | 
						dtos := make([]PigSaleDTO, len(data))
 | 
				
			||||||
 | 
						for i, item := range data {
 | 
				
			||||||
 | 
							dtos[i] = PigSaleDTO{
 | 
				
			||||||
 | 
								ID:         item.ID,
 | 
				
			||||||
 | 
								CreatedAt:  item.CreatedAt,
 | 
				
			||||||
 | 
								UpdatedAt:  item.UpdatedAt,
 | 
				
			||||||
 | 
								PigBatchID: item.PigBatchID,
 | 
				
			||||||
 | 
								SaleDate:   item.SaleDate,
 | 
				
			||||||
 | 
								Buyer:      item.Buyer,
 | 
				
			||||||
 | 
								Quantity:   item.Quantity,
 | 
				
			||||||
 | 
								UnitPrice:  item.UnitPrice,
 | 
				
			||||||
 | 
								TotalPrice: item.TotalPrice,
 | 
				
			||||||
 | 
								Remarks:    item.Remarks,
 | 
				
			||||||
 | 
								OperatorID: item.OperatorID,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ListPigSaleResponse{
 | 
				
			||||||
 | 
							List: dtos,
 | 
				
			||||||
 | 
							Pagination: PaginationDTO{
 | 
				
			||||||
 | 
								Total:    total,
 | 
				
			||||||
 | 
								Page:     page,
 | 
				
			||||||
 | 
								PageSize: pageSize,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										608
									
								
								internal/app/dto/monitor_dto.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										608
									
								
								internal/app/dto/monitor_dto.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,608 @@
 | 
				
			|||||||
 | 
					package dto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- General ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PaginationDTO 定义了分页信息的标准结构
 | 
				
			||||||
 | 
					type PaginationDTO struct {
 | 
				
			||||||
 | 
						Total    int64 `json:"total"`
 | 
				
			||||||
 | 
						Page     int   `json:"page"`
 | 
				
			||||||
 | 
						PageSize int   `json:"pageSize"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- SensorData ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListSensorDataRequest 定义了获取传感器数据列表的请求参数
 | 
				
			||||||
 | 
					type ListSensorDataRequest struct {
 | 
				
			||||||
 | 
						Page       int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize   int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						DeviceID   *uint      `form:"device_id"`
 | 
				
			||||||
 | 
						SensorType *string    `form:"sensor_type"`
 | 
				
			||||||
 | 
						StartTime  *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime    *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy    string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SensorDataDTO 是用于API响应的传感器数据结构
 | 
				
			||||||
 | 
					type SensorDataDTO struct {
 | 
				
			||||||
 | 
						Time                 time.Time         `json:"time"`
 | 
				
			||||||
 | 
						DeviceID             uint              `json:"device_id"`
 | 
				
			||||||
 | 
						RegionalControllerID uint              `json:"regional_controller_id"`
 | 
				
			||||||
 | 
						SensorType           models.SensorType `json:"sensor_type"`
 | 
				
			||||||
 | 
						Data                 json.RawMessage   `json:"data"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListSensorDataResponse 是获取传感器数据列表的响应结构
 | 
				
			||||||
 | 
					type ListSensorDataResponse struct {
 | 
				
			||||||
 | 
						List       []SensorDataDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO   `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- DeviceCommandLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListDeviceCommandLogRequest 定义了获取设备命令日志列表的请求参数
 | 
				
			||||||
 | 
					type ListDeviceCommandLogRequest struct {
 | 
				
			||||||
 | 
						Page            int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize        int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						DeviceID        *uint      `form:"device_id"`
 | 
				
			||||||
 | 
						ReceivedSuccess *bool      `form:"received_success"`
 | 
				
			||||||
 | 
						StartTime       *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime         *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy         string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeviceCommandLogDTO 是用于API响应的设备命令日志结构
 | 
				
			||||||
 | 
					type DeviceCommandLogDTO struct {
 | 
				
			||||||
 | 
						MessageID       string     `json:"message_id"`
 | 
				
			||||||
 | 
						DeviceID        uint       `json:"device_id"`
 | 
				
			||||||
 | 
						SentAt          time.Time  `json:"sent_at"`
 | 
				
			||||||
 | 
						AcknowledgedAt  *time.Time `json:"acknowledged_at"`
 | 
				
			||||||
 | 
						ReceivedSuccess bool       `json:"received_success"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListDeviceCommandLogResponse 是获取设备命令日志列表的响应结构
 | 
				
			||||||
 | 
					type ListDeviceCommandLogResponse struct {
 | 
				
			||||||
 | 
						List       []DeviceCommandLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO         `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- PlanExecutionLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPlanExecutionLogRequest 定义了获取计划执行日志列表的请求参数
 | 
				
			||||||
 | 
					type ListPlanExecutionLogRequest struct {
 | 
				
			||||||
 | 
						Page      int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize  int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PlanID    *uint      `form:"plan_id"`
 | 
				
			||||||
 | 
						Status    *string    `form:"status"`
 | 
				
			||||||
 | 
						StartTime *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime   *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy   string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PlanExecutionLogDTO 是用于API响应的计划执行日志结构
 | 
				
			||||||
 | 
					type PlanExecutionLogDTO struct {
 | 
				
			||||||
 | 
						ID        uint                   `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt time.Time              `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt time.Time              `json:"updated_at"`
 | 
				
			||||||
 | 
						PlanID    uint                   `json:"plan_id"`
 | 
				
			||||||
 | 
						Status    models.ExecutionStatus `json:"status"`
 | 
				
			||||||
 | 
						StartedAt time.Time              `json:"started_at"`
 | 
				
			||||||
 | 
						EndedAt   time.Time              `json:"ended_at"`
 | 
				
			||||||
 | 
						Error     string                 `json:"error"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPlanExecutionLogResponse 是获取计划执行日志列表的响应结构
 | 
				
			||||||
 | 
					type ListPlanExecutionLogResponse struct {
 | 
				
			||||||
 | 
						List       []PlanExecutionLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO         `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- TaskExecutionLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListTaskExecutionLogRequest 定义了获取任务执行日志列表的请求参数
 | 
				
			||||||
 | 
					type ListTaskExecutionLogRequest struct {
 | 
				
			||||||
 | 
						Page               int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize           int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PlanExecutionLogID *uint      `form:"plan_execution_log_id"`
 | 
				
			||||||
 | 
						TaskID             *int       `form:"task_id"`
 | 
				
			||||||
 | 
						Status             *string    `form:"status"`
 | 
				
			||||||
 | 
						StartTime          *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime            *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy            string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TaskDTO 是用于API响应的简化版任务结构
 | 
				
			||||||
 | 
					type TaskDTO struct {
 | 
				
			||||||
 | 
						ID          uint   `json:"id"`
 | 
				
			||||||
 | 
						Name        string `json:"name"`
 | 
				
			||||||
 | 
						Description string `json:"description"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TaskExecutionLogDTO 是用于API响应的任务执行日志结构
 | 
				
			||||||
 | 
					type TaskExecutionLogDTO struct {
 | 
				
			||||||
 | 
						ID                 uint                   `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt          time.Time              `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt          time.Time              `json:"updated_at"`
 | 
				
			||||||
 | 
						PlanExecutionLogID uint                   `json:"plan_execution_log_id"`
 | 
				
			||||||
 | 
						TaskID             int                    `json:"task_id"`
 | 
				
			||||||
 | 
						Task               TaskDTO                `json:"task"` // 嵌套的任务信息
 | 
				
			||||||
 | 
						Status             models.ExecutionStatus `json:"status"`
 | 
				
			||||||
 | 
						Output             string                 `json:"output"`
 | 
				
			||||||
 | 
						StartedAt          time.Time              `json:"started_at"`
 | 
				
			||||||
 | 
						EndedAt            time.Time              `json:"ended_at"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListTaskExecutionLogResponse 是获取任务执行日志列表的响应结构
 | 
				
			||||||
 | 
					type ListTaskExecutionLogResponse struct {
 | 
				
			||||||
 | 
						List       []TaskExecutionLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO         `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- PendingCollection ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPendingCollectionRequest 定义了获取待采集请求列表的请求参数
 | 
				
			||||||
 | 
					type ListPendingCollectionRequest struct {
 | 
				
			||||||
 | 
						Page      int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize  int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						DeviceID  *uint      `form:"device_id"`
 | 
				
			||||||
 | 
						Status    *string    `form:"status"`
 | 
				
			||||||
 | 
						StartTime *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime   *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy   string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PendingCollectionDTO 是用于API响应的待采集请求结构
 | 
				
			||||||
 | 
					type PendingCollectionDTO struct {
 | 
				
			||||||
 | 
						CorrelationID   string                         `json:"correlation_id"`
 | 
				
			||||||
 | 
						DeviceID        uint                           `json:"device_id"`
 | 
				
			||||||
 | 
						CommandMetadata models.UintArray               `json:"command_metadata"`
 | 
				
			||||||
 | 
						Status          models.PendingCollectionStatus `json:"status"`
 | 
				
			||||||
 | 
						FulfilledAt     *time.Time                     `json:"fulfilled_at"`
 | 
				
			||||||
 | 
						CreatedAt       time.Time                      `json:"created_at"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPendingCollectionResponse 是获取待采集请求列表的响应结构
 | 
				
			||||||
 | 
					type ListPendingCollectionResponse struct {
 | 
				
			||||||
 | 
						List       []PendingCollectionDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO          `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- UserActionLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListUserActionLogRequest 定义了获取用户操作日志列表的请求参数
 | 
				
			||||||
 | 
					type ListUserActionLogRequest struct {
 | 
				
			||||||
 | 
						Page       int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize   int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						UserID     *uint      `form:"user_id"`
 | 
				
			||||||
 | 
						Username   *string    `form:"username"`
 | 
				
			||||||
 | 
						ActionType *string    `form:"action_type"`
 | 
				
			||||||
 | 
						Status     *string    `form:"status"`
 | 
				
			||||||
 | 
						StartTime  *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime    *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy    string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UserActionLogDTO 是用于API响应的用户操作日志结构
 | 
				
			||||||
 | 
					type UserActionLogDTO struct {
 | 
				
			||||||
 | 
						ID             uint               `json:"id"`
 | 
				
			||||||
 | 
						Time           time.Time          `json:"time"`
 | 
				
			||||||
 | 
						UserID         uint               `json:"user_id"`
 | 
				
			||||||
 | 
						Username       string             `json:"username"`
 | 
				
			||||||
 | 
						SourceIP       string             `json:"source_ip"`
 | 
				
			||||||
 | 
						ActionType     string             `json:"action_type"`
 | 
				
			||||||
 | 
						TargetResource json.RawMessage    `json:"target_resource"`
 | 
				
			||||||
 | 
						Description    string             `json:"description"`
 | 
				
			||||||
 | 
						Status         models.AuditStatus `json:"status"`
 | 
				
			||||||
 | 
						HTTPPath       string             `json:"http_path"`
 | 
				
			||||||
 | 
						HTTPMethod     string             `json:"http_method"`
 | 
				
			||||||
 | 
						ResultDetails  string             `json:"result_details"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListUserActionLogResponse 是获取用户操作日志列表的响应结构
 | 
				
			||||||
 | 
					type ListUserActionLogResponse struct {
 | 
				
			||||||
 | 
						List       []UserActionLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO      `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- RawMaterialPurchase ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialPurchaseRequest 定义了获取原料采购列表的请求参数
 | 
				
			||||||
 | 
					type ListRawMaterialPurchaseRequest struct {
 | 
				
			||||||
 | 
						Page          int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize      int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						RawMaterialID *uint      `form:"raw_material_id"`
 | 
				
			||||||
 | 
						Supplier      *string    `form:"supplier"`
 | 
				
			||||||
 | 
						StartTime     *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime       *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy       string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RawMaterialDTO 是用于API响应的简化版原料结构
 | 
				
			||||||
 | 
					type RawMaterialDTO struct {
 | 
				
			||||||
 | 
						ID   uint   `json:"id"`
 | 
				
			||||||
 | 
						Name string `json:"name"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RawMaterialPurchaseDTO 是用于API响应的原料采购结构
 | 
				
			||||||
 | 
					type RawMaterialPurchaseDTO struct {
 | 
				
			||||||
 | 
						ID            uint           `json:"id"`
 | 
				
			||||||
 | 
						RawMaterialID uint           `json:"raw_material_id"`
 | 
				
			||||||
 | 
						RawMaterial   RawMaterialDTO `json:"raw_material"`
 | 
				
			||||||
 | 
						Supplier      string         `json:"supplier"`
 | 
				
			||||||
 | 
						Amount        float64        `json:"amount"`
 | 
				
			||||||
 | 
						UnitPrice     float64        `json:"unit_price"`
 | 
				
			||||||
 | 
						TotalPrice    float64        `json:"total_price"`
 | 
				
			||||||
 | 
						PurchaseDate  time.Time      `json:"purchase_date"`
 | 
				
			||||||
 | 
						CreatedAt     time.Time      `json:"created_at"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialPurchaseResponse 是获取原料采购列表的响应结构
 | 
				
			||||||
 | 
					type ListRawMaterialPurchaseResponse struct {
 | 
				
			||||||
 | 
						List       []RawMaterialPurchaseDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO            `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- RawMaterialStockLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialStockLogRequest 定义了获取原料库存日志列表的请求参数
 | 
				
			||||||
 | 
					type ListRawMaterialStockLogRequest struct {
 | 
				
			||||||
 | 
						Page          int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize      int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						RawMaterialID *uint      `form:"raw_material_id"`
 | 
				
			||||||
 | 
						SourceType    *string    `form:"source_type"`
 | 
				
			||||||
 | 
						SourceID      *uint      `form:"source_id"`
 | 
				
			||||||
 | 
						StartTime     *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime       *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy       string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RawMaterialStockLogDTO 是用于API响应的原料库存日志结构
 | 
				
			||||||
 | 
					type RawMaterialStockLogDTO struct {
 | 
				
			||||||
 | 
						ID            uint                      `json:"id"`
 | 
				
			||||||
 | 
						RawMaterialID uint                      `json:"raw_material_id"`
 | 
				
			||||||
 | 
						ChangeAmount  float64                   `json:"change_amount"`
 | 
				
			||||||
 | 
						SourceType    models.StockLogSourceType `json:"source_type"`
 | 
				
			||||||
 | 
						SourceID      uint                      `json:"source_id"`
 | 
				
			||||||
 | 
						HappenedAt    time.Time                 `json:"happened_at"`
 | 
				
			||||||
 | 
						Remarks       string                    `json:"remarks"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialStockLogResponse 是获取原料库存日志列表的响应结构
 | 
				
			||||||
 | 
					type ListRawMaterialStockLogResponse struct {
 | 
				
			||||||
 | 
						List       []RawMaterialStockLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO            `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- FeedUsageRecord ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListFeedUsageRecordRequest 定义了获取饲料使用记录列表的请求参数
 | 
				
			||||||
 | 
					type ListFeedUsageRecordRequest struct {
 | 
				
			||||||
 | 
						Page          int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize      int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PenID         *uint      `form:"pen_id"`
 | 
				
			||||||
 | 
						FeedFormulaID *uint      `form:"feed_formula_id"`
 | 
				
			||||||
 | 
						OperatorID    *uint      `form:"operator_id"`
 | 
				
			||||||
 | 
						StartTime     *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime       *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy       string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PenDTO 是用于API响应的简化版猪栏结构
 | 
				
			||||||
 | 
					type PenDTO struct {
 | 
				
			||||||
 | 
						ID   uint   `json:"id"`
 | 
				
			||||||
 | 
						Name string `json:"name"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FeedFormulaDTO 是用于API响应的简化版饲料配方结构
 | 
				
			||||||
 | 
					type FeedFormulaDTO struct {
 | 
				
			||||||
 | 
						ID   uint   `json:"id"`
 | 
				
			||||||
 | 
						Name string `json:"name"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FeedUsageRecordDTO 是用于API响应的饲料使用记录结构
 | 
				
			||||||
 | 
					type FeedUsageRecordDTO struct {
 | 
				
			||||||
 | 
						ID            uint           `json:"id"`
 | 
				
			||||||
 | 
						PenID         uint           `json:"pen_id"`
 | 
				
			||||||
 | 
						Pen           PenDTO         `json:"pen"`
 | 
				
			||||||
 | 
						FeedFormulaID uint           `json:"feed_formula_id"`
 | 
				
			||||||
 | 
						FeedFormula   FeedFormulaDTO `json:"feed_formula"`
 | 
				
			||||||
 | 
						Amount        float64        `json:"amount"`
 | 
				
			||||||
 | 
						RecordedAt    time.Time      `json:"recorded_at"`
 | 
				
			||||||
 | 
						OperatorID    uint           `json:"operator_id"`
 | 
				
			||||||
 | 
						Remarks       string         `json:"remarks"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListFeedUsageRecordResponse 是获取饲料使用记录列表的响应结构
 | 
				
			||||||
 | 
					type ListFeedUsageRecordResponse struct {
 | 
				
			||||||
 | 
						List       []FeedUsageRecordDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO        `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- MedicationLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListMedicationLogRequest 定义了获取用药记录列表的请求参数
 | 
				
			||||||
 | 
					type ListMedicationLogRequest struct {
 | 
				
			||||||
 | 
						Page         int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize     int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PigBatchID   *uint      `form:"pig_batch_id"`
 | 
				
			||||||
 | 
						MedicationID *uint      `form:"medication_id"`
 | 
				
			||||||
 | 
						Reason       *string    `form:"reason"`
 | 
				
			||||||
 | 
						OperatorID   *uint      `form:"operator_id"`
 | 
				
			||||||
 | 
						StartTime    *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime      *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy      string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MedicationDTO 是用于API响应的简化版药品结构
 | 
				
			||||||
 | 
					type MedicationDTO struct {
 | 
				
			||||||
 | 
						ID   uint   `json:"id"`
 | 
				
			||||||
 | 
						Name string `json:"name"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MedicationLogDTO 是用于API响应的用药记录结构
 | 
				
			||||||
 | 
					type MedicationLogDTO struct {
 | 
				
			||||||
 | 
						ID           uint                        `json:"id"`
 | 
				
			||||||
 | 
						PigBatchID   uint                        `json:"pig_batch_id"`
 | 
				
			||||||
 | 
						MedicationID uint                        `json:"medication_id"`
 | 
				
			||||||
 | 
						Medication   MedicationDTO               `json:"medication"`
 | 
				
			||||||
 | 
						DosageUsed   float64                     `json:"dosage_used"`
 | 
				
			||||||
 | 
						TargetCount  int                         `json:"target_count"`
 | 
				
			||||||
 | 
						Reason       models.MedicationReasonType `json:"reason"`
 | 
				
			||||||
 | 
						Description  string                      `json:"description"`
 | 
				
			||||||
 | 
						OperatorID   uint                        `json:"operator_id"`
 | 
				
			||||||
 | 
						HappenedAt   time.Time                   `json:"happened_at"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListMedicationLogResponse 是获取用药记录列表的响应结构
 | 
				
			||||||
 | 
					type ListMedicationLogResponse struct {
 | 
				
			||||||
 | 
						List       []MedicationLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO      `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- PigBatchLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigBatchLogRequest 定义了获取猪批次日志列表的请求参数
 | 
				
			||||||
 | 
					type ListPigBatchLogRequest struct {
 | 
				
			||||||
 | 
						Page       int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize   int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PigBatchID *uint      `form:"pig_batch_id"`
 | 
				
			||||||
 | 
						ChangeType *string    `form:"change_type"`
 | 
				
			||||||
 | 
						OperatorID *uint      `form:"operator_id"`
 | 
				
			||||||
 | 
						StartTime  *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime    *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy    string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigBatchLogDTO 是用于API响应的猪批次日志结构
 | 
				
			||||||
 | 
					type PigBatchLogDTO struct {
 | 
				
			||||||
 | 
						ID          uint                 `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt   time.Time            `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt   time.Time            `json:"updated_at"`
 | 
				
			||||||
 | 
						PigBatchID  uint                 `json:"pig_batch_id"`
 | 
				
			||||||
 | 
						ChangeType  models.LogChangeType `json:"change_type"`
 | 
				
			||||||
 | 
						ChangeCount int                  `json:"change_count"`
 | 
				
			||||||
 | 
						Reason      string               `json:"reason"`
 | 
				
			||||||
 | 
						BeforeCount int                  `json:"before_count"`
 | 
				
			||||||
 | 
						AfterCount  int                  `json:"after_count"`
 | 
				
			||||||
 | 
						OperatorID  uint                 `json:"operator_id"`
 | 
				
			||||||
 | 
						HappenedAt  time.Time            `json:"happened_at"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigBatchLogResponse 是获取猪批次日志列表的响应结构
 | 
				
			||||||
 | 
					type ListPigBatchLogResponse struct {
 | 
				
			||||||
 | 
						List       []PigBatchLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO    `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- WeighingBatch ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingBatchRequest 定义了获取批次称重记录列表的请求参数
 | 
				
			||||||
 | 
					type ListWeighingBatchRequest struct {
 | 
				
			||||||
 | 
						Page       int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize   int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PigBatchID *uint      `form:"pig_batch_id"`
 | 
				
			||||||
 | 
						StartTime  *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime    *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy    string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WeighingBatchDTO 是用于API响应的批次称重记录结构
 | 
				
			||||||
 | 
					type WeighingBatchDTO struct {
 | 
				
			||||||
 | 
						ID           uint      `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt    time.Time `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt    time.Time `json:"updated_at"`
 | 
				
			||||||
 | 
						WeighingTime time.Time `json:"weighing_time"`
 | 
				
			||||||
 | 
						Description  string    `json:"description"`
 | 
				
			||||||
 | 
						PigBatchID   uint      `json:"pig_batch_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingBatchResponse 是获取批次称重记录列表的响应结构
 | 
				
			||||||
 | 
					type ListWeighingBatchResponse struct {
 | 
				
			||||||
 | 
						List       []WeighingBatchDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO      `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- WeighingRecord ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingRecordRequest 定义了获取单次称重记录列表的请求参数
 | 
				
			||||||
 | 
					type ListWeighingRecordRequest struct {
 | 
				
			||||||
 | 
						Page            int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize        int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						WeighingBatchID *uint      `form:"weighing_batch_id"`
 | 
				
			||||||
 | 
						PenID           *uint      `form:"pen_id"`
 | 
				
			||||||
 | 
						OperatorID      *uint      `form:"operator_id"`
 | 
				
			||||||
 | 
						StartTime       *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime         *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy         string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WeighingRecordDTO 是用于API响应的单次称重记录结构
 | 
				
			||||||
 | 
					type WeighingRecordDTO struct {
 | 
				
			||||||
 | 
						ID              uint      `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt       time.Time `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt       time.Time `json:"updated_at"`
 | 
				
			||||||
 | 
						Weight          float64   `json:"weight"`
 | 
				
			||||||
 | 
						WeighingBatchID uint      `json:"weighing_batch_id"`
 | 
				
			||||||
 | 
						PenID           uint      `json:"pen_id"`
 | 
				
			||||||
 | 
						OperatorID      uint      `json:"operator_id"`
 | 
				
			||||||
 | 
						Remark          string    `json:"remark"`
 | 
				
			||||||
 | 
						WeighingTime    time.Time `json:"weighing_time"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingRecordResponse 是获取单次称重记录列表的响应结构
 | 
				
			||||||
 | 
					type ListWeighingRecordResponse struct {
 | 
				
			||||||
 | 
						List       []WeighingRecordDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO       `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- PigTransferLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigTransferLogRequest 定义了获取猪只迁移日志列表的请求参数
 | 
				
			||||||
 | 
					type ListPigTransferLogRequest struct {
 | 
				
			||||||
 | 
						Page          int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize      int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PigBatchID    *uint      `form:"pig_batch_id"`
 | 
				
			||||||
 | 
						PenID         *uint      `form:"pen_id"`
 | 
				
			||||||
 | 
						TransferType  *string    `form:"transfer_type"`
 | 
				
			||||||
 | 
						OperatorID    *uint      `form:"operator_id"`
 | 
				
			||||||
 | 
						CorrelationID *string    `form:"correlation_id"`
 | 
				
			||||||
 | 
						StartTime     *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime       *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy       string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigTransferLogDTO 是用于API响应的猪只迁移日志结构
 | 
				
			||||||
 | 
					type PigTransferLogDTO struct {
 | 
				
			||||||
 | 
						ID            uint                   `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt     time.Time              `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt     time.Time              `json:"updated_at"`
 | 
				
			||||||
 | 
						TransferTime  time.Time              `json:"transfer_time"`
 | 
				
			||||||
 | 
						PigBatchID    uint                   `json:"pig_batch_id"`
 | 
				
			||||||
 | 
						PenID         uint                   `json:"pen_id"`
 | 
				
			||||||
 | 
						Quantity      int                    `json:"quantity"`
 | 
				
			||||||
 | 
						Type          models.PigTransferType `json:"type"`
 | 
				
			||||||
 | 
						CorrelationID string                 `json:"correlation_id"`
 | 
				
			||||||
 | 
						OperatorID    uint                   `json:"operator_id"`
 | 
				
			||||||
 | 
						Remarks       string                 `json:"remarks"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigTransferLogResponse 是获取猪只迁移日志列表的响应结构
 | 
				
			||||||
 | 
					type ListPigTransferLogResponse struct {
 | 
				
			||||||
 | 
						List       []PigTransferLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO       `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- PigSickLog ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSickLogRequest 定义了获取病猪日志列表的请求参数
 | 
				
			||||||
 | 
					type ListPigSickLogRequest struct {
 | 
				
			||||||
 | 
						Page              int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize          int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PigBatchID        *uint      `form:"pig_batch_id"`
 | 
				
			||||||
 | 
						PenID             *uint      `form:"pen_id"`
 | 
				
			||||||
 | 
						Reason            *string    `form:"reason"`
 | 
				
			||||||
 | 
						TreatmentLocation *string    `form:"treatment_location"`
 | 
				
			||||||
 | 
						OperatorID        *uint      `form:"operator_id"`
 | 
				
			||||||
 | 
						StartTime         *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime           *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy           string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigSickLogDTO 是用于API响应的病猪日志结构
 | 
				
			||||||
 | 
					type PigSickLogDTO struct {
 | 
				
			||||||
 | 
						ID                uint                                    `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt         time.Time                               `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt         time.Time                               `json:"updated_at"`
 | 
				
			||||||
 | 
						PigBatchID        uint                                    `json:"pig_batch_id"`
 | 
				
			||||||
 | 
						PenID             uint                                    `json:"pen_id"`
 | 
				
			||||||
 | 
						ChangeCount       int                                     `json:"change_count"`
 | 
				
			||||||
 | 
						Reason            models.PigBatchSickPigReasonType        `json:"reason"`
 | 
				
			||||||
 | 
						BeforeCount       int                                     `json:"before_count"`
 | 
				
			||||||
 | 
						AfterCount        int                                     `json:"after_count"`
 | 
				
			||||||
 | 
						Remarks           string                                  `json:"remarks"`
 | 
				
			||||||
 | 
						TreatmentLocation models.PigBatchSickPigTreatmentLocation `json:"treatment_location"`
 | 
				
			||||||
 | 
						OperatorID        uint                                    `json:"operator_id"`
 | 
				
			||||||
 | 
						HappenedAt        time.Time                               `json:"happened_at"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSickLogResponse 是获取病猪日志列表的响应结构
 | 
				
			||||||
 | 
					type ListPigSickLogResponse struct {
 | 
				
			||||||
 | 
						List       []PigSickLogDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO   `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- PigPurchase ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigPurchaseRequest 定义了获取猪只采购记录列表的请求参数
 | 
				
			||||||
 | 
					type ListPigPurchaseRequest struct {
 | 
				
			||||||
 | 
						Page       int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize   int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PigBatchID *uint      `form:"pig_batch_id"`
 | 
				
			||||||
 | 
						Supplier   *string    `form:"supplier"`
 | 
				
			||||||
 | 
						OperatorID *uint      `form:"operator_id"`
 | 
				
			||||||
 | 
						StartTime  *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime    *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy    string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigPurchaseDTO 是用于API响应的猪只采购记录结构
 | 
				
			||||||
 | 
					type PigPurchaseDTO struct {
 | 
				
			||||||
 | 
						ID           uint      `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt    time.Time `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt    time.Time `json:"updated_at"`
 | 
				
			||||||
 | 
						PigBatchID   uint      `json:"pig_batch_id"`
 | 
				
			||||||
 | 
						PurchaseDate time.Time `json:"purchase_date"`
 | 
				
			||||||
 | 
						Supplier     string    `json:"supplier"`
 | 
				
			||||||
 | 
						Quantity     int       `json:"quantity"`
 | 
				
			||||||
 | 
						UnitPrice    float64   `json:"unit_price"`
 | 
				
			||||||
 | 
						TotalPrice   float64   `json:"total_price"`
 | 
				
			||||||
 | 
						Remarks      string    `json:"remarks"`
 | 
				
			||||||
 | 
						OperatorID   uint      `json:"operator_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigPurchaseResponse 是获取猪只采购记录列表的响应结构
 | 
				
			||||||
 | 
					type ListPigPurchaseResponse struct {
 | 
				
			||||||
 | 
						List       []PigPurchaseDTO `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO    `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- PigSale ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSaleRequest 定义了获取猪只销售记录列表的请求参数
 | 
				
			||||||
 | 
					type ListPigSaleRequest struct {
 | 
				
			||||||
 | 
						Page       int        `form:"page,default=1"`
 | 
				
			||||||
 | 
						PageSize   int        `form:"pageSize,default=10"`
 | 
				
			||||||
 | 
						PigBatchID *uint      `form:"pig_batch_id"`
 | 
				
			||||||
 | 
						Buyer      *string    `form:"buyer"`
 | 
				
			||||||
 | 
						OperatorID *uint      `form:"operator_id"`
 | 
				
			||||||
 | 
						StartTime  *time.Time `form:"start_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						EndTime    *time.Time `form:"end_time" time_format:"rfc3339"`
 | 
				
			||||||
 | 
						OrderBy    string     `form:"order_by"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigSaleDTO 是用于API响应的猪只销售记录结构
 | 
				
			||||||
 | 
					type PigSaleDTO struct {
 | 
				
			||||||
 | 
						ID         uint      `json:"id"`
 | 
				
			||||||
 | 
						CreatedAt  time.Time `json:"created_at"`
 | 
				
			||||||
 | 
						UpdatedAt  time.Time `json:"updated_at"`
 | 
				
			||||||
 | 
						PigBatchID uint      `json:"pig_batch_id"`
 | 
				
			||||||
 | 
						SaleDate   time.Time `json:"sale_date"`
 | 
				
			||||||
 | 
						Buyer      string    `json:"buyer"`
 | 
				
			||||||
 | 
						Quantity   int       `json:"quantity"`
 | 
				
			||||||
 | 
						UnitPrice  float64   `json:"unit_price"`
 | 
				
			||||||
 | 
						TotalPrice float64   `json:"total_price"`
 | 
				
			||||||
 | 
						Remarks    string    `json:"remarks"`
 | 
				
			||||||
 | 
						OperatorID uint      `json:"operator_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSaleResponse 是获取猪只销售记录列表的响应结构
 | 
				
			||||||
 | 
					type ListPigSaleResponse struct {
 | 
				
			||||||
 | 
						List       []PigSaleDTO  `json:"list"`
 | 
				
			||||||
 | 
						Pagination PaginationDTO `json:"pagination"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,7 +25,7 @@ func AuthMiddleware(tokenService token.TokenService, userRepo repository.UserRep
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// 授权标头的格式应为 "Bearer <token>"
 | 
							// 授权标头的格式应为 "Bearer <token>"
 | 
				
			||||||
		parts := strings.Split(authHeader, " ")
 | 
							parts := strings.Split(authHeader, " ")
 | 
				
			||||||
		if len(parts) != 2 || parts[0] != "Bearer" {
 | 
							if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
 | 
				
			||||||
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "授权标头格式不正确"})
 | 
								c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "授权标头格式不正确"})
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										159
									
								
								internal/app/service/monitor_service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								internal/app/service/monitor_service.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,159 @@
 | 
				
			|||||||
 | 
					package service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MonitorService 定义了监控相关的业务逻辑服务接口
 | 
				
			||||||
 | 
					type MonitorService interface {
 | 
				
			||||||
 | 
						ListSensorData(opts repository.SensorDataListOptions, page, pageSize int) ([]models.SensorData, int64, error)
 | 
				
			||||||
 | 
						ListDeviceCommandLogs(opts repository.DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error)
 | 
				
			||||||
 | 
						ListPlanExecutionLogs(opts repository.PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error)
 | 
				
			||||||
 | 
						ListTaskExecutionLogs(opts repository.TaskExecutionLogListOptions, page, pageSize int) ([]models.TaskExecutionLog, int64, error)
 | 
				
			||||||
 | 
						ListPendingCollections(opts repository.PendingCollectionListOptions, page, pageSize int) ([]models.PendingCollection, int64, error)
 | 
				
			||||||
 | 
						ListUserActionLogs(opts repository.UserActionLogListOptions, page, pageSize int) ([]models.UserActionLog, int64, error)
 | 
				
			||||||
 | 
						ListRawMaterialPurchases(opts repository.RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error)
 | 
				
			||||||
 | 
						ListRawMaterialStockLogs(opts repository.RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error)
 | 
				
			||||||
 | 
						ListFeedUsageRecords(opts repository.FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error)
 | 
				
			||||||
 | 
						ListMedicationLogs(opts repository.MedicationLogListOptions, page, pageSize int) ([]models.MedicationLog, int64, error)
 | 
				
			||||||
 | 
						ListPigBatchLogs(opts repository.PigBatchLogListOptions, page, pageSize int) ([]models.PigBatchLog, int64, error)
 | 
				
			||||||
 | 
						ListWeighingBatches(opts repository.WeighingBatchListOptions, page, pageSize int) ([]models.WeighingBatch, int64, error)
 | 
				
			||||||
 | 
						ListWeighingRecords(opts repository.WeighingRecordListOptions, page, pageSize int) ([]models.WeighingRecord, int64, error)
 | 
				
			||||||
 | 
						ListPigTransferLogs(opts repository.PigTransferLogListOptions, page, pageSize int) ([]models.PigTransferLog, int64, error)
 | 
				
			||||||
 | 
						ListPigSickLogs(opts repository.PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error)
 | 
				
			||||||
 | 
						ListPigPurchases(opts repository.PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error)
 | 
				
			||||||
 | 
						ListPigSales(opts repository.PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// monitorService 是 MonitorService 接口的具体实现
 | 
				
			||||||
 | 
					type monitorService struct {
 | 
				
			||||||
 | 
						sensorDataRepo        repository.SensorDataRepository
 | 
				
			||||||
 | 
						deviceCommandLogRepo  repository.DeviceCommandLogRepository
 | 
				
			||||||
 | 
						executionLogRepo      repository.ExecutionLogRepository
 | 
				
			||||||
 | 
						pendingCollectionRepo repository.PendingCollectionRepository
 | 
				
			||||||
 | 
						userActionLogRepo     repository.UserActionLogRepository
 | 
				
			||||||
 | 
						rawMaterialRepo       repository.RawMaterialRepository
 | 
				
			||||||
 | 
						medicationRepo        repository.MedicationLogRepository
 | 
				
			||||||
 | 
						pigBatchRepo          repository.PigBatchRepository
 | 
				
			||||||
 | 
						pigBatchLogRepo       repository.PigBatchLogRepository
 | 
				
			||||||
 | 
						pigTransferLogRepo    repository.PigTransferLogRepository
 | 
				
			||||||
 | 
						pigSickLogRepo        repository.PigSickLogRepository
 | 
				
			||||||
 | 
						pigTradeRepo          repository.PigTradeRepository
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewMonitorService 创建一个新的 MonitorService 实例
 | 
				
			||||||
 | 
					func NewMonitorService(
 | 
				
			||||||
 | 
						sensorDataRepo repository.SensorDataRepository,
 | 
				
			||||||
 | 
						deviceCommandLogRepo repository.DeviceCommandLogRepository,
 | 
				
			||||||
 | 
						executionLogRepo repository.ExecutionLogRepository,
 | 
				
			||||||
 | 
						pendingCollectionRepo repository.PendingCollectionRepository,
 | 
				
			||||||
 | 
						userActionLogRepo repository.UserActionLogRepository,
 | 
				
			||||||
 | 
						rawMaterialRepo repository.RawMaterialRepository,
 | 
				
			||||||
 | 
						medicationRepo repository.MedicationLogRepository,
 | 
				
			||||||
 | 
						pigBatchRepo repository.PigBatchRepository,
 | 
				
			||||||
 | 
						pigBatchLogRepo repository.PigBatchLogRepository,
 | 
				
			||||||
 | 
						pigTransferLogRepo repository.PigTransferLogRepository,
 | 
				
			||||||
 | 
						pigSickLogRepo repository.PigSickLogRepository,
 | 
				
			||||||
 | 
						pigTradeRepo repository.PigTradeRepository,
 | 
				
			||||||
 | 
					) MonitorService {
 | 
				
			||||||
 | 
						return &monitorService{
 | 
				
			||||||
 | 
							sensorDataRepo:        sensorDataRepo,
 | 
				
			||||||
 | 
							deviceCommandLogRepo:  deviceCommandLogRepo,
 | 
				
			||||||
 | 
							executionLogRepo:      executionLogRepo,
 | 
				
			||||||
 | 
							pendingCollectionRepo: pendingCollectionRepo,
 | 
				
			||||||
 | 
							userActionLogRepo:     userActionLogRepo,
 | 
				
			||||||
 | 
							rawMaterialRepo:       rawMaterialRepo,
 | 
				
			||||||
 | 
							medicationRepo:        medicationRepo,
 | 
				
			||||||
 | 
							pigBatchRepo:          pigBatchRepo,
 | 
				
			||||||
 | 
							pigBatchLogRepo:       pigBatchLogRepo,
 | 
				
			||||||
 | 
							pigTransferLogRepo:    pigTransferLogRepo,
 | 
				
			||||||
 | 
							pigSickLogRepo:        pigSickLogRepo,
 | 
				
			||||||
 | 
							pigTradeRepo:          pigTradeRepo,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListSensorData 负责处理查询传感器数据列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListSensorData(opts repository.SensorDataListOptions, page, pageSize int) ([]models.SensorData, int64, error) {
 | 
				
			||||||
 | 
						return s.sensorDataRepo.List(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListDeviceCommandLogs 负责处理查询设备命令日志列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListDeviceCommandLogs(opts repository.DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error) {
 | 
				
			||||||
 | 
						return s.deviceCommandLogRepo.List(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPlanExecutionLogs 负责处理查询计划执行日志列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListPlanExecutionLogs(opts repository.PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) {
 | 
				
			||||||
 | 
						return s.executionLogRepo.ListPlanExecutionLogs(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListTaskExecutionLogs 负责处理查询任务执行日志列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListTaskExecutionLogs(opts repository.TaskExecutionLogListOptions, page, pageSize int) ([]models.TaskExecutionLog, int64, error) {
 | 
				
			||||||
 | 
						return s.executionLogRepo.ListTaskExecutionLogs(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPendingCollections 负责处理查询待采集请求列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListPendingCollections(opts repository.PendingCollectionListOptions, page, pageSize int) ([]models.PendingCollection, int64, error) {
 | 
				
			||||||
 | 
						return s.pendingCollectionRepo.List(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListUserActionLogs 负责处理查询用户操作日志列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListUserActionLogs(opts repository.UserActionLogListOptions, page, pageSize int) ([]models.UserActionLog, int64, error) {
 | 
				
			||||||
 | 
						return s.userActionLogRepo.List(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialPurchases 负责处理查询原料采购记录列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListRawMaterialPurchases(opts repository.RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error) {
 | 
				
			||||||
 | 
						return s.rawMaterialRepo.ListRawMaterialPurchases(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialStockLogs 负责处理查询原料库存日志列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListRawMaterialStockLogs(opts repository.RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error) {
 | 
				
			||||||
 | 
						return s.rawMaterialRepo.ListRawMaterialStockLogs(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListFeedUsageRecords 负责处理查询饲料使用记录列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListFeedUsageRecords(opts repository.FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error) {
 | 
				
			||||||
 | 
						return s.rawMaterialRepo.ListFeedUsageRecords(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListMedicationLogs 负责处理查询用药记录列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListMedicationLogs(opts repository.MedicationLogListOptions, page, pageSize int) ([]models.MedicationLog, int64, error) {
 | 
				
			||||||
 | 
						return s.medicationRepo.ListMedicationLogs(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigBatchLogs 负责处理查询猪批次日志列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListPigBatchLogs(opts repository.PigBatchLogListOptions, page, pageSize int) ([]models.PigBatchLog, int64, error) {
 | 
				
			||||||
 | 
						return s.pigBatchLogRepo.List(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingBatches 负责处理查询批次称重记录列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListWeighingBatches(opts repository.WeighingBatchListOptions, page, pageSize int) ([]models.WeighingBatch, int64, error) {
 | 
				
			||||||
 | 
						return s.pigBatchRepo.ListWeighingBatches(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingRecords 负责处理查询单次称重记录列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListWeighingRecords(opts repository.WeighingRecordListOptions, page, pageSize int) ([]models.WeighingRecord, int64, error) {
 | 
				
			||||||
 | 
						return s.pigBatchRepo.ListWeighingRecords(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigTransferLogs 负责处理查询猪只迁移日志列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListPigTransferLogs(opts repository.PigTransferLogListOptions, page, pageSize int) ([]models.PigTransferLog, int64, error) {
 | 
				
			||||||
 | 
						return s.pigTransferLogRepo.ListPigTransferLogs(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSickLogs 负责处理查询病猪日志列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListPigSickLogs(opts repository.PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error) {
 | 
				
			||||||
 | 
						return s.pigSickLogRepo.ListPigSickLogs(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigPurchases 负责处理查询猪只采购记录列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListPigPurchases(opts repository.PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error) {
 | 
				
			||||||
 | 
						return s.pigTradeRepo.ListPigPurchases(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSales 负责处理查询猪只销售记录列表的业务逻辑
 | 
				
			||||||
 | 
					func (s *monitorService) ListPigSales(opts repository.PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) {
 | 
				
			||||||
 | 
						return s.pigTradeRepo.ListPigSales(opts, page, pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -84,6 +84,7 @@ func NewApplication(configPath string) (*Application, error) {
 | 
				
			|||||||
	pigTradeRepo := repository.NewGormPigTradeRepository(storage.GetDB())
 | 
						pigTradeRepo := repository.NewGormPigTradeRepository(storage.GetDB())
 | 
				
			||||||
	pigSickPigLogRepo := repository.NewGormPigSickLogRepository(storage.GetDB())
 | 
						pigSickPigLogRepo := repository.NewGormPigSickLogRepository(storage.GetDB())
 | 
				
			||||||
	medicationLogRepo := repository.NewGormMedicationLogRepository(storage.GetDB())
 | 
						medicationLogRepo := repository.NewGormMedicationLogRepository(storage.GetDB())
 | 
				
			||||||
 | 
						rawMaterialRepo := repository.NewGormRawMaterialRepository(storage.GetDB())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 初始化事务管理器
 | 
						// 初始化事务管理器
 | 
				
			||||||
	unitOfWork := repository.NewGormUnitOfWork(storage.GetDB(), logger)
 | 
						unitOfWork := repository.NewGormUnitOfWork(storage.GetDB(), logger)
 | 
				
			||||||
@@ -98,6 +99,20 @@ func NewApplication(configPath string) (*Application, error) {
 | 
				
			|||||||
	// --- 业务逻辑处理器初始化 ---
 | 
						// --- 业务逻辑处理器初始化 ---
 | 
				
			||||||
	pigFarmService := service.NewPigFarmService(pigFarmRepo, pigPenRepo, pigBatchRepo, unitOfWork, logger)
 | 
						pigFarmService := service.NewPigFarmService(pigFarmRepo, pigPenRepo, pigBatchRepo, unitOfWork, logger)
 | 
				
			||||||
	pigBatchService := service.NewPigBatchService(pigBatchDomain, logger)
 | 
						pigBatchService := service.NewPigBatchService(pigBatchDomain, logger)
 | 
				
			||||||
 | 
						monitorService := service.NewMonitorService(
 | 
				
			||||||
 | 
							sensorDataRepo,
 | 
				
			||||||
 | 
							deviceCommandLogRepo,
 | 
				
			||||||
 | 
							executionLogRepo,
 | 
				
			||||||
 | 
							pendingCollectionRepo,
 | 
				
			||||||
 | 
							userActionLogRepo,
 | 
				
			||||||
 | 
							rawMaterialRepo,
 | 
				
			||||||
 | 
							medicationLogRepo,
 | 
				
			||||||
 | 
							pigBatchRepo,
 | 
				
			||||||
 | 
							pigBatchLogRepo,
 | 
				
			||||||
 | 
							pigTransferLogRepo,
 | 
				
			||||||
 | 
							pigSickPigLogRepo,
 | 
				
			||||||
 | 
							pigTradeRepo,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 初始化审计服务
 | 
						// 初始化审计服务
 | 
				
			||||||
	auditService := audit.NewService(userActionLogRepo, logger)
 | 
						auditService := audit.NewService(userActionLogRepo, logger)
 | 
				
			||||||
@@ -160,6 +175,7 @@ func NewApplication(configPath string) (*Application, error) {
 | 
				
			|||||||
		planRepo,
 | 
							planRepo,
 | 
				
			||||||
		pigFarmService,
 | 
							pigFarmService,
 | 
				
			||||||
		pigBatchService,
 | 
							pigBatchService,
 | 
				
			||||||
 | 
							monitorService,
 | 
				
			||||||
		userActionLogRepo,
 | 
							userActionLogRepo,
 | 
				
			||||||
		tokenService,
 | 
							tokenService,
 | 
				
			||||||
		auditService,
 | 
							auditService,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,13 +7,22 @@ import (
 | 
				
			|||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeviceCommandLogListOptions 定义了查询设备命令日志时的可选参数
 | 
				
			||||||
 | 
					type DeviceCommandLogListOptions struct {
 | 
				
			||||||
 | 
						DeviceID        *uint
 | 
				
			||||||
 | 
						ReceivedSuccess *bool
 | 
				
			||||||
 | 
						StartTime       *time.Time // 基于 sent_at 字段
 | 
				
			||||||
 | 
						EndTime         *time.Time // 基于 sent_at 字段
 | 
				
			||||||
 | 
						OrderBy         string     // 例如 "sent_at asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeviceCommandLogRepository 定义了设备下行命令历史记录的数据访问接口
 | 
					// DeviceCommandLogRepository 定义了设备下行命令历史记录的数据访问接口
 | 
				
			||||||
type DeviceCommandLogRepository interface {
 | 
					type DeviceCommandLogRepository interface {
 | 
				
			||||||
	Create(record *models.DeviceCommandLog) error
 | 
						Create(record *models.DeviceCommandLog) error
 | 
				
			||||||
	FindByMessageID(messageID string) (*models.DeviceCommandLog, error)
 | 
						FindByMessageID(messageID string) (*models.DeviceCommandLog, error)
 | 
				
			||||||
	// UpdateAcknowledgedAt 用于更新指定 MessageID 的下行命令记录的确认时间及接收成功状态。
 | 
					 | 
				
			||||||
	// AcknowledgedAt 和 ReceivedSuccess 字段会被更新。
 | 
					 | 
				
			||||||
	UpdateAcknowledgedAt(messageID string, acknowledgedAt time.Time, receivedSuccess bool) error
 | 
						UpdateAcknowledgedAt(messageID string, acknowledgedAt time.Time, receivedSuccess bool) error
 | 
				
			||||||
 | 
						// List 支持分页和过滤的列表查询
 | 
				
			||||||
 | 
						List(opts DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormDeviceCommandLogRepository 是 DeviceCommandLogRepository 接口的 GORM 实现
 | 
					// gormDeviceCommandLogRepository 是 DeviceCommandLogRepository 接口的 GORM 实现
 | 
				
			||||||
@@ -42,7 +51,6 @@ func (r *gormDeviceCommandLogRepository) FindByMessageID(messageID string) (*mod
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// UpdateAcknowledgedAt 实现 DeviceCommandLogRepository 接口的 UpdateAcknowledgedAt 方法
 | 
					// UpdateAcknowledgedAt 实现 DeviceCommandLogRepository 接口的 UpdateAcknowledgedAt 方法
 | 
				
			||||||
func (r *gormDeviceCommandLogRepository) UpdateAcknowledgedAt(messageID string, acknowledgedAt time.Time, receivedSuccess bool) error {
 | 
					func (r *gormDeviceCommandLogRepository) UpdateAcknowledgedAt(messageID string, acknowledgedAt time.Time, receivedSuccess bool) error {
 | 
				
			||||||
	// 使用 Updates 方法更新指定字段
 | 
					 | 
				
			||||||
	return r.db.Model(&models.DeviceCommandLog{}).
 | 
						return r.db.Model(&models.DeviceCommandLog{}).
 | 
				
			||||||
		Where("message_id = ?", messageID).
 | 
							Where("message_id = ?", messageID).
 | 
				
			||||||
		Updates(map[string]interface{}{
 | 
							Updates(map[string]interface{}{
 | 
				
			||||||
@@ -50,3 +58,48 @@ func (r *gormDeviceCommandLogRepository) UpdateAcknowledgedAt(messageID string,
 | 
				
			|||||||
			"received_success": receivedSuccess,
 | 
								"received_success": receivedSuccess,
 | 
				
			||||||
		}).Error
 | 
							}).Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List 实现了分页和过滤查询设备命令日志的功能
 | 
				
			||||||
 | 
					func (r *gormDeviceCommandLogRepository) List(opts DeviceCommandLogListOptions, page, pageSize int) ([]models.DeviceCommandLog, int64, error) {
 | 
				
			||||||
 | 
						// --- 校验分页参数 ---
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.DeviceCommandLog
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.DeviceCommandLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- 应用过滤条件 ---
 | 
				
			||||||
 | 
						if opts.DeviceID != nil {
 | 
				
			||||||
 | 
							query = query.Where("device_id = ?", *opts.DeviceID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.ReceivedSuccess != nil {
 | 
				
			||||||
 | 
							query = query.Where("received_success = ?", *opts.ReceivedSuccess)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("sent_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("sent_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- 计算总数 ---
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- 应用排序条件 ---
 | 
				
			||||||
 | 
						orderBy := "sent_at DESC" // 默认排序
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- 分页 ---
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,9 +8,28 @@ import (
 | 
				
			|||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PlanExecutionLogListOptions 定义了查询计划执行日志时的可选参数
 | 
				
			||||||
 | 
					type PlanExecutionLogListOptions struct {
 | 
				
			||||||
 | 
						PlanID    *uint
 | 
				
			||||||
 | 
						Status    *models.ExecutionStatus
 | 
				
			||||||
 | 
						StartTime *time.Time // 基于 created_at 字段
 | 
				
			||||||
 | 
						EndTime   *time.Time // 基于 created_at 字段
 | 
				
			||||||
 | 
						OrderBy   string     // 例如 "created_at asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TaskExecutionLogListOptions 定义了查询任务执行日志时的可选参数
 | 
				
			||||||
 | 
					type TaskExecutionLogListOptions struct {
 | 
				
			||||||
 | 
						PlanExecutionLogID *uint
 | 
				
			||||||
 | 
						TaskID             *int
 | 
				
			||||||
 | 
						Status             *models.ExecutionStatus
 | 
				
			||||||
 | 
						StartTime          *time.Time // 基于 created_at 字段
 | 
				
			||||||
 | 
						EndTime            *time.Time // 基于 created_at 字段
 | 
				
			||||||
 | 
						OrderBy            string     // 例如 "created_at asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ExecutionLogRepository 定义了与执行日志交互的接口。
 | 
					// ExecutionLogRepository 定义了与执行日志交互的接口。
 | 
				
			||||||
// 这为服务层提供了一个清晰的契约,并允许在测试中轻松地进行模拟。
 | 
					 | 
				
			||||||
type ExecutionLogRepository interface {
 | 
					type ExecutionLogRepository interface {
 | 
				
			||||||
 | 
						// --- Existing methods ---
 | 
				
			||||||
	UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error
 | 
						UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error
 | 
				
			||||||
	UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error
 | 
						UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error
 | 
				
			||||||
	CreateTaskExecutionLog(log *models.TaskExecutionLog) error
 | 
						CreateTaskExecutionLog(log *models.TaskExecutionLog) error
 | 
				
			||||||
@@ -48,6 +67,10 @@ type ExecutionLogRepository interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// CancelIncompleteTasksByPlanLogID 取消一个计划执行中的所有未完成任务
 | 
						// CancelIncompleteTasksByPlanLogID 取消一个计划执行中的所有未完成任务
 | 
				
			||||||
	CancelIncompleteTasksByPlanLogID(planLogID uint, reason string) error
 | 
						CancelIncompleteTasksByPlanLogID(planLogID uint, reason string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- New methods ---
 | 
				
			||||||
 | 
						ListPlanExecutionLogs(opts PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error)
 | 
				
			||||||
 | 
						ListTaskExecutionLogs(opts TaskExecutionLogListOptions, page, pageSize int) ([]models.TaskExecutionLog, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormExecutionLogRepository 是使用 GORM 的具体实现。
 | 
					// gormExecutionLogRepository 是使用 GORM 的具体实现。
 | 
				
			||||||
@@ -56,18 +79,101 @@ type gormExecutionLogRepository struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewGormExecutionLogRepository 创建一个新的执行日志仓库。
 | 
					// NewGormExecutionLogRepository 创建一个新的执行日志仓库。
 | 
				
			||||||
// 它接收一个 GORM DB 实例作为依赖。
 | 
					 | 
				
			||||||
func NewGormExecutionLogRepository(db *gorm.DB) ExecutionLogRepository {
 | 
					func NewGormExecutionLogRepository(db *gorm.DB) ExecutionLogRepository {
 | 
				
			||||||
	return &gormExecutionLogRepository{db: db}
 | 
						return &gormExecutionLogRepository{db: db}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPlanExecutionLogs 实现了分页和过滤查询计划执行日志的功能
 | 
				
			||||||
 | 
					func (r *gormExecutionLogRepository) ListPlanExecutionLogs(opts PlanExecutionLogListOptions, page, pageSize int) ([]models.PlanExecutionLog, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.PlanExecutionLog
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.PlanExecutionLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PlanID != nil {
 | 
				
			||||||
 | 
							query = query.Where("plan_id = ?", *opts.PlanID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Status != nil {
 | 
				
			||||||
 | 
							query = query.Where("status = ?", *opts.Status)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("created_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("created_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "created_at DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListTaskExecutionLogs 实现了分页和过滤查询任务执行日志的功能
 | 
				
			||||||
 | 
					func (r *gormExecutionLogRepository) ListTaskExecutionLogs(opts TaskExecutionLogListOptions, page, pageSize int) ([]models.TaskExecutionLog, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.TaskExecutionLog
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.TaskExecutionLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PlanExecutionLogID != nil {
 | 
				
			||||||
 | 
							query = query.Where("plan_execution_log_id = ?", *opts.PlanExecutionLogID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.TaskID != nil {
 | 
				
			||||||
 | 
							query = query.Where("task_id = ?", *opts.TaskID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Status != nil {
 | 
				
			||||||
 | 
							query = query.Where("status = ?", *opts.Status)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("created_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("created_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "created_at DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 预加载关联的Task信息
 | 
				
			||||||
 | 
						query = query.Order(orderBy).Preload("Task")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Existing method implementations ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error {
 | 
					func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatusByIDs(logIDs []uint, status models.ExecutionStatus) error {
 | 
				
			||||||
	if len(logIDs) == 0 {
 | 
						if len(logIDs) == 0 {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return r.db.Model(&models.TaskExecutionLog{}).
 | 
						return r.db.Model(&models.TaskExecutionLog{}).Where("id IN ?", logIDs).Update("status", status).Error
 | 
				
			||||||
		Where("id IN ?", logIDs).
 | 
					 | 
				
			||||||
		Update("status", status).Error
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error {
 | 
					func (r *gormExecutionLogRepository) UpdateTaskExecutionLogStatus(logID uint, status models.ExecutionStatus) error {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +0,0 @@
 | 
				
			|||||||
package repository
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
					 | 
				
			||||||
	"gorm.io/gorm"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// MedicationLogRepository 定义了与群体用药日志模型相关的数据库操作接口。
 | 
					 | 
				
			||||||
type MedicationLogRepository interface {
 | 
					 | 
				
			||||||
	CreateMedicationLog(log *models.MedicationLog) error
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// gormMedicationLogRepository 是 MedicationLogRepository 接口的 GORM 实现。
 | 
					 | 
				
			||||||
type gormMedicationLogRepository struct {
 | 
					 | 
				
			||||||
	db *gorm.DB
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewGormMedicationLogRepository 创建一个新的 MedicationLogRepository GORM 实现实例。
 | 
					 | 
				
			||||||
func NewGormMedicationLogRepository(db *gorm.DB) MedicationLogRepository {
 | 
					 | 
				
			||||||
	return &gormMedicationLogRepository{db: db}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CreateMedicationLog 创建一条新的群体用药日志记录
 | 
					 | 
				
			||||||
func (r *gormMedicationLogRepository) CreateMedicationLog(log *models.MedicationLog) error {
 | 
					 | 
				
			||||||
	return r.db.Create(log).Error
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										86
									
								
								internal/infra/repository/medication_log_repository.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								internal/infra/repository/medication_log_repository.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MedicationLogListOptions 定义了查询用药记录时的可选参数
 | 
				
			||||||
 | 
					type MedicationLogListOptions struct {
 | 
				
			||||||
 | 
						PigBatchID   *uint
 | 
				
			||||||
 | 
						MedicationID *uint
 | 
				
			||||||
 | 
						Reason       *models.MedicationReasonType
 | 
				
			||||||
 | 
						OperatorID   *uint
 | 
				
			||||||
 | 
						StartTime    *time.Time
 | 
				
			||||||
 | 
						EndTime      *time.Time
 | 
				
			||||||
 | 
						OrderBy      string // 例如 "happened_at desc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MedicationLogRepository 定义了与群体用药日志模型相关的数据库操作接口。
 | 
				
			||||||
 | 
					type MedicationLogRepository interface {
 | 
				
			||||||
 | 
						CreateMedicationLog(log *models.MedicationLog) error
 | 
				
			||||||
 | 
						ListMedicationLogs(opts MedicationLogListOptions, page, pageSize int) ([]models.MedicationLog, int64, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// gormMedicationLogRepository 是 MedicationLogRepository 接口的 GORM 实现。
 | 
				
			||||||
 | 
					type gormMedicationLogRepository struct {
 | 
				
			||||||
 | 
						db *gorm.DB
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewGormMedicationLogRepository 创建一个新的 MedicationLogRepository GORM 实现实例。
 | 
				
			||||||
 | 
					func NewGormMedicationLogRepository(db *gorm.DB) MedicationLogRepository {
 | 
				
			||||||
 | 
						return &gormMedicationLogRepository{db: db}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateMedicationLog 创建一条新的群体用药日志记录
 | 
				
			||||||
 | 
					func (r *gormMedicationLogRepository) CreateMedicationLog(log *models.MedicationLog) error {
 | 
				
			||||||
 | 
						return r.db.Create(log).Error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListMedicationLogs 实现了分页和过滤查询用药记录的功能
 | 
				
			||||||
 | 
					func (r *gormMedicationLogRepository) ListMedicationLogs(opts MedicationLogListOptions, page, pageSize int) ([]models.MedicationLog, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.MedicationLog
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.MedicationLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PigBatchID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pig_batch_id = ?", *opts.PigBatchID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.MedicationID != nil {
 | 
				
			||||||
 | 
							query = query.Where("medication_id = ?", *opts.MedicationID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Reason != nil {
 | 
				
			||||||
 | 
							query = query.Where("reason = ?", *opts.Reason)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.OperatorID != nil {
 | 
				
			||||||
 | 
							query = query.Where("operator_id = ?", *opts.OperatorID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("happened_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("happened_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "happened_at DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,6 +7,15 @@ import (
 | 
				
			|||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PendingCollectionListOptions 定义了查询待采集请求时的可选参数
 | 
				
			||||||
 | 
					type PendingCollectionListOptions struct {
 | 
				
			||||||
 | 
						DeviceID  *uint
 | 
				
			||||||
 | 
						Status    *models.PendingCollectionStatus
 | 
				
			||||||
 | 
						StartTime *time.Time // 基于 created_at 字段
 | 
				
			||||||
 | 
						EndTime   *time.Time // 基于 created_at 字段
 | 
				
			||||||
 | 
						OrderBy   string     // 例如 "created_at asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PendingCollectionRepository 定义了与待采集请求相关的数据库操作接口。
 | 
					// PendingCollectionRepository 定义了与待采集请求相关的数据库操作接口。
 | 
				
			||||||
type PendingCollectionRepository interface {
 | 
					type PendingCollectionRepository interface {
 | 
				
			||||||
	// Create 创建一个新的待采集请求。
 | 
						// Create 创建一个新的待采集请求。
 | 
				
			||||||
@@ -20,6 +29,9 @@ type PendingCollectionRepository interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// MarkAllPendingAsTimedOut 将所有“待处理”请求更新为“已超时”。
 | 
						// MarkAllPendingAsTimedOut 将所有“待处理”请求更新为“已超时”。
 | 
				
			||||||
	MarkAllPendingAsTimedOut() (int64, error)
 | 
						MarkAllPendingAsTimedOut() (int64, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// List 支持分页和过滤的列表查询
 | 
				
			||||||
 | 
						List(opts PendingCollectionListOptions, page, pageSize int) ([]models.PendingCollection, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormPendingCollectionRepository 是 PendingCollectionRepository 的 GORM 实现。
 | 
					// gormPendingCollectionRepository 是 PendingCollectionRepository 的 GORM 实现。
 | 
				
			||||||
@@ -65,3 +77,43 @@ func (r *gormPendingCollectionRepository) MarkAllPendingAsTimedOut() (int64, err
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return result.RowsAffected, result.Error
 | 
						return result.RowsAffected, result.Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List 实现了分页和过滤查询待采集请求的功能
 | 
				
			||||||
 | 
					func (r *gormPendingCollectionRepository) List(opts PendingCollectionListOptions, page, pageSize int) ([]models.PendingCollection, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.PendingCollection
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.PendingCollection{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.DeviceID != nil {
 | 
				
			||||||
 | 
							query = query.Where("device_id = ?", *opts.DeviceID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Status != nil {
 | 
				
			||||||
 | 
							query = query.Where("status = ?", *opts.Status)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("created_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("created_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "created_at DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,22 @@
 | 
				
			|||||||
package repository
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"time" // 引入 time 包
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigBatchLogListOptions 定义了查询猪批次日志时的可选参数
 | 
				
			||||||
 | 
					type PigBatchLogListOptions struct {
 | 
				
			||||||
 | 
						PigBatchID *uint
 | 
				
			||||||
 | 
						ChangeType *models.LogChangeType
 | 
				
			||||||
 | 
						OperatorID *uint
 | 
				
			||||||
 | 
						StartTime  *time.Time // 基于 happened_at 字段
 | 
				
			||||||
 | 
						EndTime    *time.Time // 基于 happened_at 字段
 | 
				
			||||||
 | 
						OrderBy    string     // 例如 "happened_at asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PigBatchLogRepository 定义了与猪批次日志相关的数据库操作接口。
 | 
					// PigBatchLogRepository 定义了与猪批次日志相关的数据库操作接口。
 | 
				
			||||||
type PigBatchLogRepository interface {
 | 
					type PigBatchLogRepository interface {
 | 
				
			||||||
	// CreateTx 在指定的事务中创建一条新的猪批次日志。
 | 
						// CreateTx 在指定的事务中创建一条新的猪批次日志。
 | 
				
			||||||
@@ -17,6 +27,9 @@ type PigBatchLogRepository interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// GetLastLogByBatchIDTx 在指定的事务中,获取某批次的最后一条日志记录。
 | 
						// GetLastLogByBatchIDTx 在指定的事务中,获取某批次的最后一条日志记录。
 | 
				
			||||||
	GetLastLogByBatchIDTx(tx *gorm.DB, batchID uint) (*models.PigBatchLog, error)
 | 
						GetLastLogByBatchIDTx(tx *gorm.DB, batchID uint) (*models.PigBatchLog, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// List 支持分页和过滤的列表查询
 | 
				
			||||||
 | 
						List(opts PigBatchLogListOptions, page, pageSize int) ([]models.PigBatchLog, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormPigBatchLogRepository 是 PigBatchLogRepository 的 GORM 实现。
 | 
					// gormPigBatchLogRepository 是 PigBatchLogRepository 的 GORM 实现。
 | 
				
			||||||
@@ -29,7 +42,7 @@ func NewGormPigBatchLogRepository(db *gorm.DB) PigBatchLogRepository {
 | 
				
			|||||||
	return &gormPigBatchLogRepository{db: db}
 | 
						return &gormPigBatchLogRepository{db: db}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create 实现了创建猪批次日志的逻辑。
 | 
					// CreateTx 实现了在事务中创建猪批次日志的逻辑。
 | 
				
			||||||
func (r *gormPigBatchLogRepository) CreateTx(tx *gorm.DB, log *models.PigBatchLog) error {
 | 
					func (r *gormPigBatchLogRepository) CreateTx(tx *gorm.DB, log *models.PigBatchLog) error {
 | 
				
			||||||
	return tx.Create(log).Error
 | 
						return tx.Create(log).Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -53,3 +66,46 @@ func (r *gormPigBatchLogRepository) GetLastLogByBatchIDTx(tx *gorm.DB, batchID u
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return &log, nil
 | 
						return &log, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List 实现了分页和过滤查询猪批次日志的功能
 | 
				
			||||||
 | 
					func (r *gormPigBatchLogRepository) List(opts PigBatchLogListOptions, page, pageSize int) ([]models.PigBatchLog, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.PigBatchLog
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.PigBatchLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PigBatchID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pig_batch_id = ?", *opts.PigBatchID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.ChangeType != nil {
 | 
				
			||||||
 | 
							query = query.Where("change_type = ?", *opts.ChangeType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.OperatorID != nil {
 | 
				
			||||||
 | 
							query = query.Where("operator_id = ?", *opts.OperatorID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("happened_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("happened_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "happened_at DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
package repository
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -17,6 +19,30 @@ type PigBatchRepository interface {
 | 
				
			|||||||
	DeletePigBatch(id uint) (int64, error)
 | 
						DeletePigBatch(id uint) (int64, error)
 | 
				
			||||||
	DeletePigBatchTx(tx *gorm.DB, id uint) (int64, error)
 | 
						DeletePigBatchTx(tx *gorm.DB, id uint) (int64, error)
 | 
				
			||||||
	ListPigBatches(isActive *bool) ([]*models.PigBatch, error)
 | 
						ListPigBatches(isActive *bool) ([]*models.PigBatch, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ListWeighingBatches 支持分页和过滤的批次称重列表查询
 | 
				
			||||||
 | 
						ListWeighingBatches(opts WeighingBatchListOptions, page, pageSize int) ([]models.WeighingBatch, int64, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ListWeighingRecords 支持分页和过滤的单次称重记录列表查询
 | 
				
			||||||
 | 
						ListWeighingRecords(opts WeighingRecordListOptions, page, pageSize int) ([]models.WeighingRecord, int64, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WeighingBatchListOptions 定义了查询批次称重记录时的可选参数
 | 
				
			||||||
 | 
					type WeighingBatchListOptions struct {
 | 
				
			||||||
 | 
						PigBatchID *uint
 | 
				
			||||||
 | 
						StartTime  *time.Time // 基于 weighing_time 字段
 | 
				
			||||||
 | 
						EndTime    *time.Time // 基于 weighing_time 字段
 | 
				
			||||||
 | 
						OrderBy    string     // 例如 "weighing_time asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WeighingRecordListOptions 定义了查询单次称重记录时的可选参数
 | 
				
			||||||
 | 
					type WeighingRecordListOptions struct {
 | 
				
			||||||
 | 
						WeighingBatchID *uint
 | 
				
			||||||
 | 
						PenID           *uint
 | 
				
			||||||
 | 
						OperatorID      *uint
 | 
				
			||||||
 | 
						StartTime       *time.Time // 基于 weighing_time 字段
 | 
				
			||||||
 | 
						EndTime         *time.Time // 基于 weighing_time 字段
 | 
				
			||||||
 | 
						OrderBy         string     // 例如 "weighing_time asc"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormPigBatchRepository 是 PigBatchRepository 的 GORM 实现
 | 
					// gormPigBatchRepository 是 PigBatchRepository 的 GORM 实现
 | 
				
			||||||
@@ -100,3 +126,83 @@ func (r *gormPigBatchRepository) GetPigBatchByIDTx(tx *gorm.DB, id uint) (*model
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return &batch, nil
 | 
						return &batch, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingBatches 实现了分页和过滤查询批次称重记录的功能
 | 
				
			||||||
 | 
					func (r *gormPigBatchRepository) ListWeighingBatches(opts WeighingBatchListOptions, page, pageSize int) ([]models.WeighingBatch, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.WeighingBatch
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.WeighingBatch{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PigBatchID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pig_batch_id = ?", *opts.PigBatchID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("weighing_time >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("weighing_time <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "weighing_time DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListWeighingRecords 实现了分页和过滤查询单次称重记录的功能
 | 
				
			||||||
 | 
					func (r *gormPigBatchRepository) ListWeighingRecords(opts WeighingRecordListOptions, page, pageSize int) ([]models.WeighingRecord, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.WeighingRecord
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.WeighingRecord{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.WeighingBatchID != nil {
 | 
				
			||||||
 | 
							query = query.Where("weighing_batch_id = ?", *opts.WeighingBatchID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.PenID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pen_id = ?", *opts.PenID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.OperatorID != nil {
 | 
				
			||||||
 | 
							query = query.Where("operator_id = ?", *opts.OperatorID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("weighing_time >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("weighing_time <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "weighing_time DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,11 +2,24 @@ package repository
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigSickLogListOptions 定义了查询病猪日志时的可选参数
 | 
				
			||||||
 | 
					type PigSickLogListOptions struct {
 | 
				
			||||||
 | 
						PigBatchID        *uint
 | 
				
			||||||
 | 
						PenID             *uint
 | 
				
			||||||
 | 
						Reason            *models.PigBatchSickPigReasonType
 | 
				
			||||||
 | 
						TreatmentLocation *models.PigBatchSickPigTreatmentLocation
 | 
				
			||||||
 | 
						OperatorID        *uint
 | 
				
			||||||
 | 
						StartTime         *time.Time // 基于 happened_at 字段
 | 
				
			||||||
 | 
						EndTime           *time.Time // 基于 happened_at 字段
 | 
				
			||||||
 | 
						OrderBy           string     // 例如 "happened_at desc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PigSickLogRepository 定义了与病猪日志模型相关的数据库操作接口。
 | 
					// PigSickLogRepository 定义了与病猪日志模型相关的数据库操作接口。
 | 
				
			||||||
type PigSickLogRepository interface {
 | 
					type PigSickLogRepository interface {
 | 
				
			||||||
	// CreatePigSickLog 创建一条新的病猪日志记录
 | 
						// CreatePigSickLog 创建一条新的病猪日志记录
 | 
				
			||||||
@@ -15,6 +28,9 @@ type PigSickLogRepository interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// GetLastLogByBatchTx 在事务中获取指定批次和猪栏的最新一条 PigSickLog 记录
 | 
						// GetLastLogByBatchTx 在事务中获取指定批次和猪栏的最新一条 PigSickLog 记录
 | 
				
			||||||
	GetLastLogByBatchTx(tx *gorm.DB, batchID uint) (*models.PigSickLog, error)
 | 
						GetLastLogByBatchTx(tx *gorm.DB, batchID uint) (*models.PigSickLog, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ListPigSickLogs 支持分页和过滤的病猪日志列表查询
 | 
				
			||||||
 | 
						ListPigSickLogs(opts PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormPigSickLogRepository 是 PigSickLogRepository 接口的 GORM 实现。
 | 
					// gormPigSickLogRepository 是 PigSickLogRepository 接口的 GORM 实现。
 | 
				
			||||||
@@ -51,3 +67,52 @@ func (r *gormPigSickLogRepository) GetLastLogByBatchTx(tx *gorm.DB, batchID uint
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return &lastLog, nil
 | 
						return &lastLog, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSickLogs 实现了分页和过滤查询病猪日志的功能
 | 
				
			||||||
 | 
					func (r *gormPigSickLogRepository) ListPigSickLogs(opts PigSickLogListOptions, page, pageSize int) ([]models.PigSickLog, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.PigSickLog
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.PigSickLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PigBatchID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pig_batch_id = ?", *opts.PigBatchID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.PenID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pen_id = ?", *opts.PenID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Reason != nil {
 | 
				
			||||||
 | 
							query = query.Where("reason = ?", *opts.Reason)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.TreatmentLocation != nil {
 | 
				
			||||||
 | 
							query = query.Where("treatment_location = ?", *opts.TreatmentLocation)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.OperatorID != nil {
 | 
				
			||||||
 | 
							query = query.Where("operator_id = ?", *opts.OperatorID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("happened_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("happened_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "happened_at DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,32 @@
 | 
				
			|||||||
package repository
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigPurchaseListOptions 定义了查询猪只采购记录时的可选参数
 | 
				
			||||||
 | 
					type PigPurchaseListOptions struct {
 | 
				
			||||||
 | 
						PigBatchID *uint
 | 
				
			||||||
 | 
						Supplier   *string
 | 
				
			||||||
 | 
						OperatorID *uint
 | 
				
			||||||
 | 
						StartTime  *time.Time // 基于 purchase_date 字段
 | 
				
			||||||
 | 
						EndTime    *time.Time // 基于 purchase_date 字段
 | 
				
			||||||
 | 
						OrderBy    string     // 例如 "purchase_date desc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigSaleListOptions 定义了查询猪只销售记录时的可选参数
 | 
				
			||||||
 | 
					type PigSaleListOptions struct {
 | 
				
			||||||
 | 
						PigBatchID *uint
 | 
				
			||||||
 | 
						Buyer      *string
 | 
				
			||||||
 | 
						OperatorID *uint
 | 
				
			||||||
 | 
						StartTime  *time.Time // 基于 sale_date 字段
 | 
				
			||||||
 | 
						EndTime    *time.Time // 基于 sale_date 字段
 | 
				
			||||||
 | 
						OrderBy    string     // 例如 "sale_date desc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PigTradeRepository 定义了猪只交易数据持久化的接口。
 | 
					// PigTradeRepository 定义了猪只交易数据持久化的接口。
 | 
				
			||||||
// 领域服务通过此接口与数据层交互,实现解耦。
 | 
					// 领域服务通过此接口与数据层交互,实现解耦。
 | 
				
			||||||
type PigTradeRepository interface {
 | 
					type PigTradeRepository interface {
 | 
				
			||||||
@@ -13,6 +35,12 @@ type PigTradeRepository interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// CreatePigPurchaseTx 在数据库中创建一条猪只采购记录。
 | 
						// CreatePigPurchaseTx 在数据库中创建一条猪只采购记录。
 | 
				
			||||||
	CreatePigPurchaseTx(tx *gorm.DB, purchase *models.PigPurchase) error
 | 
						CreatePigPurchaseTx(tx *gorm.DB, purchase *models.PigPurchase) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ListPigPurchases 支持分页和过滤的猪只采购记录列表查询
 | 
				
			||||||
 | 
						ListPigPurchases(opts PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ListPigSales 支持分页和过滤的猪只销售记录列表查询
 | 
				
			||||||
 | 
						ListPigSales(opts PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormPigTradeRepository 是 PigTradeRepository 接口的 GORM 实现。
 | 
					// gormPigTradeRepository 是 PigTradeRepository 接口的 GORM 实现。
 | 
				
			||||||
@@ -34,3 +62,89 @@ func (r *gormPigTradeRepository) CreatePigSaleTx(tx *gorm.DB, sale *models.PigSa
 | 
				
			|||||||
func (r *gormPigTradeRepository) CreatePigPurchaseTx(tx *gorm.DB, purchase *models.PigPurchase) error {
 | 
					func (r *gormPigTradeRepository) CreatePigPurchaseTx(tx *gorm.DB, purchase *models.PigPurchase) error {
 | 
				
			||||||
	return tx.Create(purchase).Error
 | 
						return tx.Create(purchase).Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigPurchases 实现了分页和过滤查询猪只采购记录的功能
 | 
				
			||||||
 | 
					func (r *gormPigTradeRepository) ListPigPurchases(opts PigPurchaseListOptions, page, pageSize int) ([]models.PigPurchase, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.PigPurchase
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.PigPurchase{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PigBatchID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pig_batch_id = ?", *opts.PigBatchID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Supplier != nil {
 | 
				
			||||||
 | 
							query = query.Where("supplier LIKE ?", "%"+*opts.Supplier+"%")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.OperatorID != nil {
 | 
				
			||||||
 | 
							query = query.Where("operator_id = ?", *opts.OperatorID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("purchase_date >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("purchase_date <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "purchase_date DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigSales 实现了分页和过滤查询猪只销售记录的功能
 | 
				
			||||||
 | 
					func (r *gormPigTradeRepository) ListPigSales(opts PigSaleListOptions, page, pageSize int) ([]models.PigSale, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.PigSale
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.PigSale{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PigBatchID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pig_batch_id = ?", *opts.PigBatchID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Buyer != nil {
 | 
				
			||||||
 | 
							query = query.Where("buyer LIKE ?", "%"+*opts.Buyer+"%")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.OperatorID != nil {
 | 
				
			||||||
 | 
							query = query.Where("operator_id = ?", *opts.OperatorID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("sale_date >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("sale_date <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "sale_date DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,18 @@ import (
 | 
				
			|||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PigTransferLogListOptions 定义了查询猪只迁移日志时的可选参数
 | 
				
			||||||
 | 
					type PigTransferLogListOptions struct {
 | 
				
			||||||
 | 
						PigBatchID    *uint
 | 
				
			||||||
 | 
						PenID         *uint
 | 
				
			||||||
 | 
						TransferType  *models.PigTransferType // 迁移类型
 | 
				
			||||||
 | 
						OperatorID    *uint
 | 
				
			||||||
 | 
						CorrelationID *string
 | 
				
			||||||
 | 
						StartTime     *time.Time // 基于 transfer_time 字段
 | 
				
			||||||
 | 
						EndTime       *time.Time // 基于 transfer_time 字段
 | 
				
			||||||
 | 
						OrderBy       string     // 例如 "transfer_time desc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PigTransferLogRepository 定义了猪只迁移日志数据持久化的接口。
 | 
					// PigTransferLogRepository 定义了猪只迁移日志数据持久化的接口。
 | 
				
			||||||
type PigTransferLogRepository interface {
 | 
					type PigTransferLogRepository interface {
 | 
				
			||||||
	// CreatePigTransferLog 在数据库中创建一条猪只迁移日志记录。
 | 
						// CreatePigTransferLog 在数据库中创建一条猪只迁移日志记录。
 | 
				
			||||||
@@ -14,6 +26,9 @@ type PigTransferLogRepository interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// GetLogsForPenSince 获取指定猪栏自特定时间点以来的所有迁移日志,按时间倒序排列。
 | 
						// GetLogsForPenSince 获取指定猪栏自特定时间点以来的所有迁移日志,按时间倒序排列。
 | 
				
			||||||
	GetLogsForPenSince(tx *gorm.DB, penID uint, since time.Time) ([]*models.PigTransferLog, error)
 | 
						GetLogsForPenSince(tx *gorm.DB, penID uint, since time.Time) ([]*models.PigTransferLog, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ListPigTransferLogs 支持分页和过滤的猪只迁移日志列表查询
 | 
				
			||||||
 | 
						ListPigTransferLogs(opts PigTransferLogListOptions, page, pageSize int) ([]models.PigTransferLog, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormPigTransferLogRepository 是 PigTransferLogRepository 接口的 GORM 实现。
 | 
					// gormPigTransferLogRepository 是 PigTransferLogRepository 接口的 GORM 实现。
 | 
				
			||||||
@@ -37,3 +52,52 @@ func (r *gormPigTransferLogRepository) GetLogsForPenSince(tx *gorm.DB, penID uin
 | 
				
			|||||||
	err := tx.Where("pen_id = ? AND transfer_time >= ?", penID, since).Order("transfer_time DESC").Find(&logs).Error
 | 
						err := tx.Where("pen_id = ? AND transfer_time >= ?", penID, since).Order("transfer_time DESC").Find(&logs).Error
 | 
				
			||||||
	return logs, err
 | 
						return logs, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListPigTransferLogs 实现了分页和过滤查询猪只迁移日志的功能
 | 
				
			||||||
 | 
					func (r *gormPigTransferLogRepository) ListPigTransferLogs(opts PigTransferLogListOptions, page, pageSize int) ([]models.PigTransferLog, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.PigTransferLog
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.PigTransferLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PigBatchID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pig_batch_id = ?", *opts.PigBatchID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.PenID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pen_id = ?", *opts.PenID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.TransferType != nil {
 | 
				
			||||||
 | 
							query = query.Where("type = ?", *opts.TransferType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.OperatorID != nil {
 | 
				
			||||||
 | 
							query = query.Where("operator_id = ?", *opts.OperatorID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.CorrelationID != nil {
 | 
				
			||||||
 | 
							query = query.Where("correlation_id = ?", *opts.CorrelationID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("transfer_time >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("transfer_time <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "transfer_time DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										180
									
								
								internal/infra/repository/raw_material_repository.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								internal/infra/repository/raw_material_repository.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
				
			|||||||
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RawMaterialPurchaseListOptions 定义了查询原料采购记录时的可选参数
 | 
				
			||||||
 | 
					type RawMaterialPurchaseListOptions struct {
 | 
				
			||||||
 | 
						RawMaterialID *uint
 | 
				
			||||||
 | 
						Supplier      *string
 | 
				
			||||||
 | 
						StartTime     *time.Time // 基于 purchase_date 字段
 | 
				
			||||||
 | 
						EndTime       *time.Time // 基于 purchase_date 字段
 | 
				
			||||||
 | 
						OrderBy       string     // 例如 "purchase_date asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RawMaterialStockLogListOptions 定义了查询原料库存日志时的可选参数
 | 
				
			||||||
 | 
					type RawMaterialStockLogListOptions struct {
 | 
				
			||||||
 | 
						RawMaterialID *uint
 | 
				
			||||||
 | 
						SourceType    *models.StockLogSourceType
 | 
				
			||||||
 | 
						SourceID      *uint
 | 
				
			||||||
 | 
						StartTime     *time.Time // 基于 happened_at 字段
 | 
				
			||||||
 | 
						EndTime       *time.Time // 基于 happened_at 字段
 | 
				
			||||||
 | 
						OrderBy       string     // 例如 "happened_at asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FeedUsageRecordListOptions 定义了查询饲料使用记录时的可选参数
 | 
				
			||||||
 | 
					type FeedUsageRecordListOptions struct {
 | 
				
			||||||
 | 
						PenID         *uint
 | 
				
			||||||
 | 
						FeedFormulaID *uint
 | 
				
			||||||
 | 
						OperatorID    *uint
 | 
				
			||||||
 | 
						StartTime     *time.Time // 基于 recorded_at 字段
 | 
				
			||||||
 | 
						EndTime       *time.Time // 基于 recorded_at 字段
 | 
				
			||||||
 | 
						OrderBy       string     // 例如 "recorded_at asc"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RawMaterialRepository 定义了与原料相关的数据库操作接口
 | 
				
			||||||
 | 
					type RawMaterialRepository interface {
 | 
				
			||||||
 | 
						ListRawMaterialPurchases(opts RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error)
 | 
				
			||||||
 | 
						ListRawMaterialStockLogs(opts RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error)
 | 
				
			||||||
 | 
						ListFeedUsageRecords(opts FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// gormRawMaterialRepository 是 RawMaterialRepository 的 GORM 实现
 | 
				
			||||||
 | 
					type gormRawMaterialRepository struct {
 | 
				
			||||||
 | 
						db *gorm.DB
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewGormRawMaterialRepository 创建一个新的 RawMaterialRepository GORM 实现实例
 | 
				
			||||||
 | 
					func NewGormRawMaterialRepository(db *gorm.DB) RawMaterialRepository {
 | 
				
			||||||
 | 
						return &gormRawMaterialRepository{db: db}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialPurchases 实现了分页和过滤查询原料采购记录的功能
 | 
				
			||||||
 | 
					func (r *gormRawMaterialRepository) ListRawMaterialPurchases(opts RawMaterialPurchaseListOptions, page, pageSize int) ([]models.RawMaterialPurchase, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.RawMaterialPurchase
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.RawMaterialPurchase{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.RawMaterialID != nil {
 | 
				
			||||||
 | 
							query = query.Where("raw_material_id = ?", *opts.RawMaterialID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Supplier != nil {
 | 
				
			||||||
 | 
							query = query.Where("supplier LIKE ?", "%"+*opts.Supplier+"%")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("purchase_date >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("purchase_date <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "purchase_date DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy).Preload("RawMaterial")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListRawMaterialStockLogs 实现了分页和过滤查询原料库存日志的功能
 | 
				
			||||||
 | 
					func (r *gormRawMaterialRepository) ListRawMaterialStockLogs(opts RawMaterialStockLogListOptions, page, pageSize int) ([]models.RawMaterialStockLog, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.RawMaterialStockLog
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.RawMaterialStockLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.RawMaterialID != nil {
 | 
				
			||||||
 | 
							query = query.Where("raw_material_id = ?", *opts.RawMaterialID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.SourceType != nil {
 | 
				
			||||||
 | 
							query = query.Where("source_type = ?", *opts.SourceType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.SourceID != nil {
 | 
				
			||||||
 | 
							query = query.Where("source_id = ?", *opts.SourceID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("happened_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("happened_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "happened_at DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListFeedUsageRecords 实现了分页和过滤查询饲料使用记录的功能
 | 
				
			||||||
 | 
					func (r *gormRawMaterialRepository) ListFeedUsageRecords(opts FeedUsageRecordListOptions, page, pageSize int) ([]models.FeedUsageRecord, int64, error) {
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.FeedUsageRecord
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.FeedUsageRecord{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PenID != nil {
 | 
				
			||||||
 | 
							query = query.Where("pen_id = ?", *opts.PenID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.FeedFormulaID != nil {
 | 
				
			||||||
 | 
							query = query.Where("feed_formula_id = ?", *opts.FeedFormulaID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.OperatorID != nil {
 | 
				
			||||||
 | 
							query = query.Where("operator_id = ?", *opts.OperatorID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("recorded_at >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("recorded_at <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderBy := "recorded_at DESC"
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy).Preload("Pen").Preload("FeedFormula")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,16 +1,31 @@
 | 
				
			|||||||
package repository
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrInvalidPagination 表示分页参数无效
 | 
				
			||||||
 | 
					var ErrInvalidPagination = errors.New("无效的分页参数:page和pageSize必须为大于0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SensorDataListOptions 定义了查询传感器数据列表时的可选参数
 | 
				
			||||||
 | 
					type SensorDataListOptions struct {
 | 
				
			||||||
 | 
						DeviceID   *uint
 | 
				
			||||||
 | 
						SensorType *models.SensorType
 | 
				
			||||||
 | 
						StartTime  *time.Time
 | 
				
			||||||
 | 
						EndTime    *time.Time
 | 
				
			||||||
 | 
						OrderBy    string // 例如 "time DESC"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SensorDataRepository 定义了与传感器数据相关的数据库操作接口。
 | 
					// SensorDataRepository 定义了与传感器数据相关的数据库操作接口。
 | 
				
			||||||
type SensorDataRepository interface {
 | 
					type SensorDataRepository interface {
 | 
				
			||||||
	Create(sensorData *models.SensorData) error
 | 
						Create(sensorData *models.SensorData) error
 | 
				
			||||||
	GetLatestSensorDataByDeviceIDAndSensorType(deviceID uint, sensorType models.SensorType) (*models.SensorData, error)
 | 
						GetLatestSensorDataByDeviceIDAndSensorType(deviceID uint, sensorType models.SensorType) (*models.SensorData, error)
 | 
				
			||||||
 | 
						// List 支持分页和过滤的列表查询
 | 
				
			||||||
 | 
						List(opts SensorDataListOptions, page, pageSize int) ([]models.SensorData, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormSensorDataRepository 是 SensorDataRepository 的 GORM 实现。
 | 
					// gormSensorDataRepository 是 SensorDataRepository 的 GORM 实现。
 | 
				
			||||||
@@ -19,7 +34,6 @@ type gormSensorDataRepository struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewGormSensorDataRepository 创建一个新的 SensorDataRepository GORM 实现实例。
 | 
					// NewGormSensorDataRepository 创建一个新的 SensorDataRepository GORM 实现实例。
 | 
				
			||||||
// 它直接接收一个 *gorm.DB 实例作为依赖,完全遵循项目中的既定模式。
 | 
					 | 
				
			||||||
func NewGormSensorDataRepository(db *gorm.DB) SensorDataRepository {
 | 
					func NewGormSensorDataRepository(db *gorm.DB) SensorDataRepository {
 | 
				
			||||||
	return &gormSensorDataRepository{db: db}
 | 
						return &gormSensorDataRepository{db: db}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -38,3 +52,48 @@ func (r *gormSensorDataRepository) GetLatestSensorDataByDeviceIDAndSensorType(de
 | 
				
			|||||||
		First(&sensorData).Error
 | 
							First(&sensorData).Error
 | 
				
			||||||
	return &sensorData, err
 | 
						return &sensorData, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List 实现了分页和过滤查询传感器数据的功能
 | 
				
			||||||
 | 
					func (r *gormSensorDataRepository) List(opts SensorDataListOptions, page, pageSize int) ([]models.SensorData, int64, error) {
 | 
				
			||||||
 | 
						// --- 校验分页参数 ---
 | 
				
			||||||
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var results []models.SensorData
 | 
				
			||||||
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := r.db.Model(&models.SensorData{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- 应用过滤条件 ---
 | 
				
			||||||
 | 
						if opts.DeviceID != nil {
 | 
				
			||||||
 | 
							query = query.Where("device_id = ?", *opts.DeviceID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.SensorType != nil {
 | 
				
			||||||
 | 
							query = query.Where("sensor_type = ?", *opts.SensorType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("time >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("time <= ?", *opts.EndTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- 计算总数 ---
 | 
				
			||||||
 | 
						if err := query.Count(&total).Error; err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- 应用排序条件 ---
 | 
				
			||||||
 | 
						orderBy := "time DESC" // 默认排序
 | 
				
			||||||
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// --- 分页 ---
 | 
				
			||||||
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
 | 
						err := query.Limit(pageSize).Offset(offset).Find(&results).Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return results, total, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,27 @@
 | 
				
			|||||||
package repository
 | 
					package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FindAuditLogOptions 定义了查询审计日志的选项
 | 
					// UserActionLogListOptions 定义了查询用户操作日志时的可选参数
 | 
				
			||||||
type FindAuditLogOptions struct {
 | 
					type UserActionLogListOptions struct {
 | 
				
			||||||
	UserID     *uint  // 根据用户ID过滤
 | 
						UserID     *uint
 | 
				
			||||||
	ActionType string // 根据操作类型过滤
 | 
						Username   *string
 | 
				
			||||||
	Page       int    // 页码
 | 
						ActionType *string
 | 
				
			||||||
	PageSize   int    // 每页大小
 | 
						Status     *models.AuditStatus
 | 
				
			||||||
 | 
						StartTime  *time.Time // 基于 time 字段
 | 
				
			||||||
 | 
						EndTime    *time.Time // 基于 time 字段
 | 
				
			||||||
 | 
						OrderBy    string     // 例如 "time asc"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UserActionLogRepository 定义了与用户操作日志相关的数据库操作接口
 | 
					// UserActionLogRepository 定义了与用户操作日志相关的数据库操作接口
 | 
				
			||||||
type UserActionLogRepository interface {
 | 
					type UserActionLogRepository interface {
 | 
				
			||||||
	Create(log *models.UserActionLog) error
 | 
						Create(log *models.UserActionLog) error
 | 
				
			||||||
	List(options FindAuditLogOptions) ([]*models.UserActionLog, int64, error)
 | 
						List(opts UserActionLogListOptions, page, pageSize int) ([]models.UserActionLog, int64, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// gormUserActionLogRepository 是 UserActionLogRepository 的 GORM 实现
 | 
					// gormUserActionLogRepository 是 UserActionLogRepository 的 GORM 实现
 | 
				
			||||||
@@ -35,17 +40,33 @@ func (r *gormUserActionLogRepository) Create(log *models.UserActionLog) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List 根据选项查询用户操作日志,并返回总数
 | 
					// List 根据选项查询用户操作日志,并返回总数
 | 
				
			||||||
func (r *gormUserActionLogRepository) List(options FindAuditLogOptions) ([]*models.UserActionLog, int64, error) {
 | 
					func (r *gormUserActionLogRepository) List(opts UserActionLogListOptions, page, pageSize int) ([]models.UserActionLog, int64, error) {
 | 
				
			||||||
	var logs []*models.UserActionLog
 | 
						if page <= 0 || pageSize <= 0 {
 | 
				
			||||||
 | 
							return nil, 0, ErrInvalidPagination
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var logs []models.UserActionLog
 | 
				
			||||||
	var total int64
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	query := r.db.Model(&models.UserActionLog{})
 | 
						query := r.db.Model(&models.UserActionLog{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if options.UserID != nil {
 | 
						if opts.UserID != nil {
 | 
				
			||||||
		query = query.Where("user_id = ?", *options.UserID)
 | 
							query = query.Where("user_id = ?", *opts.UserID)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if options.ActionType != "" {
 | 
						if opts.Username != nil {
 | 
				
			||||||
		query = query.Where("action_type = ?", options.ActionType)
 | 
							query = query.Where("username LIKE ?", "%"+*opts.Username+"%")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.ActionType != nil {
 | 
				
			||||||
 | 
							query = query.Where("action_type = ?", *opts.ActionType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Status != nil {
 | 
				
			||||||
 | 
							query = query.Where("status = ?", *opts.Status)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.StartTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("time >= ?", *opts.StartTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.EndTime != nil {
 | 
				
			||||||
 | 
							query = query.Where("time <= ?", *opts.EndTime)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 统计总数
 | 
						// 统计总数
 | 
				
			||||||
@@ -53,16 +74,14 @@ func (r *gormUserActionLogRepository) List(options FindAuditLogOptions) ([]*mode
 | 
				
			|||||||
		return nil, 0, err
 | 
							return nil, 0, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 分页查询
 | 
						orderBy := "time DESC"
 | 
				
			||||||
	if options.Page > 0 && options.PageSize > 0 {
 | 
						if opts.OrderBy != "" {
 | 
				
			||||||
		offset := (options.Page - 1) * options.PageSize
 | 
							orderBy = opts.OrderBy
 | 
				
			||||||
		query = query.Offset(offset).Limit(options.PageSize)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						query = query.Order(orderBy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 默认按创建时间倒序
 | 
						offset := (page - 1) * pageSize
 | 
				
			||||||
	query = query.Order("created_at DESC")
 | 
						if err := query.Limit(pageSize).Offset(offset).Find(&logs).Error; err != nil {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := query.Find(&logs).Error; err != nil {
 | 
					 | 
				
			||||||
		return nil, 0, err
 | 
							return nil, 0, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user