diff --git a/internal/api/api.go b/internal/api/api.go index 8939ea4..b9782cb 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -13,6 +13,7 @@ import ( "git.huangwc.com/pig/pig-farm-controller/internal/api/middleware" "git.huangwc.com/pig/pig-farm-controller/internal/config" "git.huangwc.com/pig/pig-farm-controller/internal/controller/device" + "git.huangwc.com/pig/pig-farm-controller/internal/controller/feed" "git.huangwc.com/pig/pig-farm-controller/internal/controller/operation" "git.huangwc.com/pig/pig-farm-controller/internal/controller/remote" "git.huangwc.com/pig/pig-farm-controller/internal/controller/user" @@ -44,6 +45,9 @@ type API struct { // deviceController 设备控制控制器 deviceController *device.Controller + // feedController 饲喂管理控制器 + feedController *feed.Controller + // remoteController 远程控制控制器 remoteController *remote.Controller @@ -98,6 +102,9 @@ func NewAPI(cfg *config.Config, userRepo repository.UserRepo, operationHistoryRe // 创建设备控制控制器 deviceController := device.NewController(deviceControlRepo, deviceRepo, websocketManager, heartbeatService, deviceStatusPool) + // 创建饲喂管理控制器 + feedController := feed.NewController() + // 创建远程控制控制器 remoteController := remote.NewController(websocketManager) @@ -110,6 +117,7 @@ func NewAPI(cfg *config.Config, userRepo repository.UserRepo, operationHistoryRe userController: userController, operationController: operationController, deviceController: deviceController, + feedController: feedController, remoteController: remoteController, authMiddleware: authMiddleware, websocketManager: websocketManager, @@ -222,6 +230,13 @@ func (a *API) setupRoutes() { deviceGroup.GET("/status", a.deviceController.GetDeviceStatus) } + // 饲喂相关路由 + feedGroup := protectedGroup.Group("/feed") + { + feedGroup.GET("/plan/list", a.feedController.ListPlans) + feedGroup.GET("/plan/detail", a.feedController.Detail) + } + // 远程控制相关路由 remoteGroup := protectedGroup.Group("/remote") { diff --git a/internal/controller/feed/feed.go b/internal/controller/feed/feed.go index 9050f85..42fb96a 100644 --- a/internal/controller/feed/feed.go +++ b/internal/controller/feed/feed.go @@ -3,14 +3,169 @@ // 通过任务执行器执行具体控制任务 package feed -// FeedController 饲料控制器 +import ( + "time" + + "git.huangwc.com/pig/pig-farm-controller/internal/controller" + "git.huangwc.com/pig/pig-farm-controller/internal/logs" + "git.huangwc.com/pig/pig-farm-controller/internal/model" + "git.huangwc.com/pig/pig-farm-controller/internal/storage/repository" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +// Controller 饲料控制器 // 管理饲料制备和分配设备的控制逻辑 -type FeedController struct { - // TODO: 定义饲料控制器结构 +type Controller struct { + feedPlanRepo repository.FeedPlanRepo + logger *logs.Logger } -// NewFeedController 创建并返回一个新的饲料控制器实例 -func NewFeedController() *FeedController { +// NewController 创建并返回一个新的饲料控制器实例 +func NewController(feedPlanRepo repository.FeedPlanRepo) *Controller { // TODO: 实现饲料控制器初始化 - return nil + return &Controller{ + feedPlanRepo: feedPlanRepo, + logger: logs.NewLogger(), + } +} + +type ListPlansResponse struct { + Plans []ListPlanResponseItem `json:"plans"` +} + +type ListPlanResponseItem struct { + // 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 定时任务表达式 + ScheduleCron *string `json:"schedule_cron,omitempty"` +} + +// ListPlans 获取饲料计划列表 +func (c *Controller) ListPlans(ctx *gin.Context) { + + introductions, err := c.feedPlanRepo.ListAllPlanIntroduction() + if err != nil { + c.logger.Error("获取设备列表失败: " + err.Error()) + controller.SendErrorResponse(ctx, controller.InternalServerErrorCode, "获取计划列表失败") + } + + resp := ListPlansResponse{ + Plans: []ListPlanResponseItem{}, + } + for _, introduction := range introductions { + resp.Plans = append(resp.Plans, ListPlanResponseItem{ + ID: introduction.ID, + Name: introduction.Name, + Description: introduction.Description, + Enabled: introduction.Enabled, + Type: introduction.Type, + }) + } + + controller.SendSuccessResponse(ctx, "success", resp) +} + +// DetailResponse 喂料计划主表 +type DetailResponse struct { + // 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"` + + // CreatedAt 创建时间 + CreatedAt time.Time `json:"createdAt"` + + // UpdatedAt 更新时间 + UpdatedAt time.Time `json:"updatedAt"` + + // DeletedAt 删除时间(用于软删除) + DeletedAt gorm.DeletedAt `json:"deletedAt"` + + // Steps 计划步骤列表 + Steps []FeedingPlanStep `json:"steps"` + + // SubPlans 子计划列表 + SubPlans []DetailResponse `json:"subPlans"` +} + +// FeedingPlanStep 喂料计划步骤表,表示计划中的每个设备动作 +type FeedingPlanStep struct { + // ID 步骤ID + ID uint `json:"id"` + + // PlanID 关联的计划ID + PlanID uint `json:"planID"` + + // StepOrder 步骤顺序 + StepOrder int `json:"stepOrder"` + + // DeviceID 关联的设备ID + DeviceID uint `json:"deviceID"` + + // TargetValue 目标值(达到该值后停止工作切换到下一个设备) + TargetValue float64 `json:"targetValue"` + + // Action 动作(如:打开设备) + Action string `json:"action"` + + // ScheduleCron 步骤定时任务表达式(可选) + ScheduleCron *string `json:"scheduleCron"` + + // ExecutionLimit 步骤执行次数限制(0表示无限制) + ExecutionLimit int `json:"executionLimit"` + + // CreatedAt 创建时间 + CreatedAt time.Time `json:"createdAt"` + + // UpdatedAt 更新时间 + UpdatedAt time.Time `json:"updatedAt"` + + // DeletedAt 删除时间(用于软删除) + DeletedAt gorm.DeletedAt `json:"deletedAt"` +} + +// Detail 获取饲料计划列细节 +func (c *Controller) Detail(ctx *gin.Context) { + // TODO: 实现获取饲料计划列表的逻辑 + + controller.SendSuccessResponse(ctx, "success", &DetailResponse{}) } diff --git a/internal/model/feeding.go b/internal/model/feed.go similarity index 100% rename from internal/model/feeding.go rename to internal/model/feed.go diff --git a/internal/storage/repository/feed.go b/internal/storage/repository/feed.go new file mode 100644 index 0000000..d92897f --- /dev/null +++ b/internal/storage/repository/feed.go @@ -0,0 +1,32 @@ +package repository + +import ( + "git.huangwc.com/pig/pig-farm-controller/internal/model" + "gorm.io/gorm" +) + +// FeedPlanRepo 饲喂管理接口 +type FeedPlanRepo interface { + + // ListAllPlanIntroduction 获取所有计划简介 + ListAllPlanIntroduction() ([]model.FeedingPlan, error) +} + +type feedPlanRepo struct { + db *gorm.DB +} + +func NewFeedPlanRepo(db *gorm.DB) FeedPlanRepo { + return &feedPlanRepo{ + db: db, + } +} + +// ListAllPlanIntroduction 获取所有计划简介 +func (f *feedPlanRepo) ListAllPlanIntroduction() ([]model.FeedingPlan, error) { + var plans []model.FeedingPlan + err := f.db.Model(&model.FeedingPlan{}). + Select("id, name, description, type, enabled, schedule_cron"). + Find(&plans).Error + return plans, err +}