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"
|
||||||
@@ -16,19 +17,19 @@ 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.Errorf("%s: 服务层查询失败: %v", actionType, err)
|
||||||
|
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取用户历史记录失败", actionType, "服务层查询失败", opts)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
c.logger.Infof("%s: 成功获取用户 %d 的操作历史, 数量: %d", actionType, userID, len(historyResponses))
|
|
||||||
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取用户操作历史成功", resp, actionType, "获取用户操作历史成功", resp)
|
// 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