From 9944340d17ce4947427274ce3e285beda01e0d19 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Wed, 10 Sep 2025 15:13:31 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=B3=A8=E5=86=8C=E9=A5=B2=E5=96=82=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E7=9B=B8=E5=85=B3=E8=B7=AF=E7=94=B1=202.=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0create=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/api/api.go | 16 +++- internal/controller/feed/feed.go | 156 +++++++++++++++++++++++++------ internal/core/application.go | 16 +++- 3 files changed, 159 insertions(+), 29 deletions(-) diff --git a/internal/api/api.go b/internal/api/api.go index b9782cb..8b7f304 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -69,7 +69,16 @@ type API struct { // NewAPI 创建并返回一个新的API实例 // 初始化Gin引擎和相关配置 -func NewAPI(cfg *config.Config, userRepo repository.UserRepo, operationHistoryRepo repository.OperationHistoryRepo, deviceControlRepo repository.DeviceControlRepo, deviceRepo repository.DeviceRepo, websocketManager *websocket.Manager, heartbeatService *service.HeartbeatService, deviceStatusPool *service.DeviceStatusPool) *API { +func NewAPI(cfg *config.Config, + userRepo repository.UserRepo, + operationHistoryRepo repository.OperationHistoryRepo, + deviceControlRepo repository.DeviceControlRepo, + deviceRepo repository.DeviceRepo, + feedRepo repository.FeedPlanRepo, + websocketManager *websocket.Manager, + heartbeatService *service.HeartbeatService, + deviceStatusPool *service.DeviceStatusPool, +) *API { // 设置Gin为发布模式 gin.SetMode(gin.DebugMode) @@ -103,7 +112,7 @@ func NewAPI(cfg *config.Config, userRepo repository.UserRepo, operationHistoryRe deviceController := device.NewController(deviceControlRepo, deviceRepo, websocketManager, heartbeatService, deviceStatusPool) // 创建饲喂管理控制器 - feedController := feed.NewController() + feedController := feed.NewController(feedRepo) // 创建远程控制控制器 remoteController := remote.NewController(websocketManager) @@ -235,6 +244,9 @@ func (a *API) setupRoutes() { { feedGroup.GET("/plan/list", a.feedController.ListPlans) feedGroup.GET("/plan/detail", a.feedController.Detail) + feedGroup.POST("/plan/create", a.feedController.Create) + feedGroup.POST("/plan/update", a.feedController.Update) + feedGroup.POST("/plan/delete", a.feedController.Delete) } // 远程控制相关路由 diff --git a/internal/controller/feed/feed.go b/internal/controller/feed/feed.go index 0612bfd..07095ae 100644 --- a/internal/controller/feed/feed.go +++ b/internal/controller/feed/feed.go @@ -29,6 +29,100 @@ func NewController(feedPlanRepo repository.FeedPlanRepo) *Controller { } } +// CreateRequest 创建计划请求结构体 +type CreateRequest struct { + // Name 计划名称 + Name string `json:"name"` + + // Description 计划描述 + Description string `json:"description"` + + // Type 计划类型(手动触发/自动触发) + Type model.FeedingPlanType `json:"type"` + + // Enabled 是否启用 + Enabled bool `json:"enabled"` + + // ScheduleCron 定时任务表达式(仅当Type为auto时有效) + ScheduleCron *string `json:"scheduleCron"` + + // ExecutionLimit 执行次数限制(0表示无限制,仅当Type为auto时有效) + ExecutionLimit int `json:"executionLimit"` + + // ParentID 父计划ID(用于支持子计划结构) + ParentID *uint `json:"parentID"` + + // OrderInParent 在父计划中的执行顺序 + OrderInParent *int `json:"orderInParent"` + + // IsMaster 是否为主计划(主计划可以包含子计划) + IsMaster bool `json:"isMaster"` + + // Steps 计划步骤列表 + Steps []FeedingPlanStep `json:"steps"` + + // SubPlans 子计划列表 + SubPlans []CreateRequest `json:"subPlans"` +} + +// Create 创建饲料计划 +func (c *Controller) Create(ctx *gin.Context) { + var req CreateRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + controller.SendErrorResponse(ctx, controller.InvalidParameterCode, "请求参数错误: "+err.Error()) + return + } + + // 转换请求结构体为模型 + plan := c.convertToCreateModel(&req) + + // 调用仓库创建计划 + if err := c.feedPlanRepo.CreateFeedingPlan(plan); err != nil { + c.logger.Error("创建计划失败: " + err.Error()) + controller.SendErrorResponse(ctx, controller.InternalServerErrorCode, "创建计划失败") + return + } + + controller.SendSuccessResponse(ctx, "创建计划成功", nil) +} + +// convertToCreateModel 将创建请求结构体转换为数据库模型 +func (c *Controller) convertToCreateModel(req *CreateRequest) *model.FeedingPlan { + plan := &model.FeedingPlan{ + Name: req.Name, + Description: req.Description, + Type: req.Type, + Enabled: req.Enabled, + ScheduleCron: req.ScheduleCron, + ExecutionLimit: req.ExecutionLimit, + ParentID: req.ParentID, + OrderInParent: req.OrderInParent, + } + + // 转换步骤 + plan.Steps = make([]model.FeedingPlanStep, len(req.Steps)) + for i, step := range req.Steps { + plan.Steps[i] = model.FeedingPlanStep{ + // ID在创建时不需要设置 + // PlanID会在创建过程中自动设置 + StepOrder: step.StepOrder, + DeviceID: step.DeviceID, + TargetValue: step.TargetValue, + Action: step.Action, + ScheduleCron: step.ScheduleCron, + ExecutionLimit: step.ExecutionLimit, + } + } + + // 转换子计划 + plan.SubPlans = make([]model.FeedingPlan, len(req.SubPlans)) + for i, subReq := range req.SubPlans { + plan.SubPlans[i] = *c.convertToCreateModel(&subReq) + } + + return plan +} + // Delete 删除饲料计划 func (c *Controller) Delete(ctx *gin.Context) { // 获取路径参数中的计划ID @@ -100,18 +194,41 @@ func (c *Controller) ListPlans(ctx *gin.Context) { // UpdateRequest 更新计划请求结构体 type UpdateRequest struct { - ID uint `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Type model.FeedingPlanType `json:"type"` - Enabled bool `json:"enabled"` - ScheduleCron *string `json:"scheduleCron"` - ExecutionLimit int `json:"executionLimit"` - ParentID *uint `json:"parentID"` - OrderInParent *int `json:"orderInParent"` - IsMaster bool `json:"isMaster"` - Steps []FeedingPlanStep `json:"steps"` - SubPlans []UpdateRequest `json:"subPlans"` + // ID 计划ID + ID uint `json:"id"` + + // Name 计划名称 + Name string `json:"name"` + + // Description 计划描述 + Description string `json:"description"` + + // Type 计划类型(手动触发/自动触发) + Type model.FeedingPlanType `json:"type"` + + // Enabled 是否启用 + Enabled bool `json:"enabled"` + + // ScheduleCron 定时任务表达式(仅当Type为auto时有效) + ScheduleCron *string `json:"scheduleCron"` + + // ExecutionLimit 执行次数限制(0表示无限制,仅当Type为auto时有效) + ExecutionLimit int `json:"executionLimit"` + + // ParentID 父计划ID(用于支持子计划结构) + ParentID *uint `json:"parentID"` + + // OrderInParent 在父计划中的执行顺序 + OrderInParent *int `json:"orderInParent"` + + // IsMaster 是否为主计划(主计划可以包含子计划) + IsMaster bool `json:"isMaster"` + + // Steps 计划步骤列表 + Steps []FeedingPlanStep `json:"steps"` + + // SubPlans 子计划列表 + SubPlans []UpdateRequest `json:"subPlans"` } // DetailResponse 喂料计划主表 @@ -293,20 +410,7 @@ func (c *Controller) convertToDetailResponse(plan *model.FeedingPlan) *DetailRes // 转换子计划 for i, subPlan := range plan.SubPlans { // 递归转换子计划 - subPlanModel := &model.FeedingPlan{ - ID: subPlan.ID, - Name: subPlan.Name, - Description: subPlan.Description, - Type: subPlan.Type, - Enabled: subPlan.Enabled, - ScheduleCron: subPlan.ScheduleCron, - ExecutionLimit: subPlan.ExecutionLimit, - ParentID: subPlan.ParentID, - OrderInParent: subPlan.OrderInParent, - Steps: subPlan.Steps, - SubPlans: subPlan.SubPlans, - } - resp.SubPlans[i] = *c.convertToDetailResponse(subPlanModel) + resp.SubPlans[i] = *c.convertToDetailResponse(&subPlan) } return resp diff --git a/internal/core/application.go b/internal/core/application.go index ce71b9a..18dde4c 100644 --- a/internal/core/application.go +++ b/internal/core/application.go @@ -40,6 +40,9 @@ type Application struct { // DeviceRepo 设备仓库实例 DeviceRepo repository.DeviceRepo + // FeedPlanRepo 投喂计划仓库实例 + FeedPlanRepo repository.FeedPlanRepo + // WebsocketManager WebSocket管理器 WebsocketManager *websocket.Manager @@ -97,6 +100,8 @@ func (app *Application) Start() error { // 初始化设备仓库 app.DeviceRepo = repository.NewDeviceRepo(app.Storage.GetDB()) + app.FeedPlanRepo = repository.NewFeedPlanRepo(app.Storage.GetDB()) + // 初始化设备状态池 app.DeviceStatusPool = service.NewDeviceStatusPool() @@ -109,7 +114,16 @@ func (app *Application) Start() error { app.HeartbeatService = service.NewHeartbeatService(app.WebsocketManager, app.DeviceStatusPool, app.DeviceRepo, app.Config) // 初始化API组件 - app.API = api.NewAPI(app.Config, app.UserRepo, app.OperationHistoryRepo, app.DeviceControlRepo, app.DeviceRepo, app.WebsocketManager, app.HeartbeatService, app.DeviceStatusPool) + app.API = api.NewAPI(app.Config, + app.UserRepo, + app.OperationHistoryRepo, + app.DeviceControlRepo, + app.DeviceRepo, + app.FeedPlanRepo, + app.WebsocketManager, + app.HeartbeatService, + app.DeviceStatusPool, + ) // 初始化任务执行器组件(使用5个工作协程) app.TaskExecutor = task.NewExecutor(5)