package device import ( "errors" "strconv" "strings" "time" "git.huangwc.com/pig/pig-farm-controller/internal/app/controller" "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" "gorm.io/datatypes" "gorm.io/gorm" ) // Controller 设备控制器,封装了所有与设备相关的业务逻辑 type Controller struct { repo repository.DeviceRepository logger *logs.Logger } // NewController 创建一个新的设备控制器实例 func NewController(repo repository.DeviceRepository, logger *logs.Logger) *Controller { return &Controller{ repo: repo, logger: logger, } } // --- Request DTOs --- // CreateDeviceRequest 定义了创建设备时需要传入的参数 type CreateDeviceRequest struct { Name string `json:"name" binding:"required"` Type models.DeviceType `json:"type" binding:"required"` SubType models.DeviceSubType `json:"sub_type,omitempty"` ParentID *uint `json:"parent_id,omitempty"` Location string `json:"location,omitempty"` Properties controller.Properties `json:"properties,omitempty"` } // UpdateDeviceRequest 定义了更新设备时需要传入的参数 type UpdateDeviceRequest struct { Name string `json:"name" binding:"required"` Type models.DeviceType `json:"type" binding:"required"` SubType models.DeviceSubType `json:"sub_type,omitempty"` ParentID *uint `json:"parent_id,omitempty"` Location string `json:"location,omitempty"` Properties controller.Properties `json:"properties,omitempty"` } // --- Response DTOs --- // DeviceResponse 定义了返回给客户端的单个设备信息的结构 type DeviceResponse struct { ID uint `json:"id"` Name string `json:"name"` Type models.DeviceType `json:"type"` SubType models.DeviceSubType `json:"sub_type"` ParentID *uint `json:"parent_id"` Location string `json:"location"` Properties controller.Properties `json:"properties"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } // --- DTO 转换函数 --- // newDeviceResponse 从数据库模型创建一个新的设备响应 DTO func newDeviceResponse(device *models.Device) *DeviceResponse { if device == nil { return nil } return &DeviceResponse{ ID: device.ID, Name: device.Name, Type: device.Type, SubType: device.SubType, ParentID: device.ParentID, Location: device.Location, Properties: controller.Properties(device.Properties), CreatedAt: device.CreatedAt.Format(time.RFC3339), UpdatedAt: device.UpdatedAt.Format(time.RFC3339), } } // newListDeviceResponse 从数据库模型切片创建一个新的设备列表响应 DTO 切片 func newListDeviceResponse(devices []*models.Device) []*DeviceResponse { list := make([]*DeviceResponse, 0, len(devices)) for _, device := range devices { list = append(list, newDeviceResponse(device)) } return list } // --- Controller Methods --- // CreateDevice godoc // @Summary 创建新设备 // @Description 根据提供的信息创建一个新设备 // @Tags 设备管理 // @Accept json // @Produce json // @Param device body CreateDeviceRequest true "设备信息" // @Success 200 {object} controller.Response{data=DeviceResponse} "业务码为201代表创建成功" // @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体" // @Router /devices [post] func (c *Controller) CreateDevice(ctx *gin.Context) { var req CreateDeviceRequest if err := ctx.ShouldBindJSON(&req); err != nil { c.logger.Errorf("创建设备: 参数绑定失败: %v", err) controller.SendErrorResponse(ctx, controller.CodeBadRequest, err.Error()) return } device := &models.Device{ Name: req.Name, Type: req.Type, SubType: req.SubType, ParentID: req.ParentID, Location: req.Location, Properties: datatypes.JSON(req.Properties), } if err := c.repo.Create(device); err != nil { c.logger.Errorf("创建设备: 数据库操作失败: %v", err) controller.SendErrorResponse(ctx, controller.CodeInternalError, "创建设备失败") return } controller.SendResponse(ctx, controller.CodeCreated, "设备创建成功", newDeviceResponse(device)) } // GetDevice godoc // @Summary 获取设备信息 // @Description 根据设备ID获取单个设备的详细信息 // @Tags 设备管理 // @Produce json // @Param id path string true "设备ID" // @Success 200 {object} controller.Response{data=DeviceResponse} "业务码为200代表获取成功" // @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体" // @Router /devices/{id} [get] func (c *Controller) GetDevice(ctx *gin.Context) { deviceID := ctx.Param("id") device, err := c.repo.FindByIDString(deviceID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { controller.SendErrorResponse(ctx, controller.CodeNotFound, "设备未找到") return } if strings.Contains(err.Error(), "无效的设备ID格式") { controller.SendErrorResponse(ctx, controller.CodeBadRequest, err.Error()) return } c.logger.Errorf("获取设备: 数据库操作失败: %v", err) controller.SendErrorResponse(ctx, controller.CodeInternalError, "获取设备信息失败") return } controller.SendResponse(ctx, controller.CodeSuccess, "获取设备信息成功", newDeviceResponse(device)) } // ListDevices godoc // @Summary 获取设备列表 // @Description 获取系统中所有设备的列表 // @Tags 设备管理 // @Produce json // @Success 200 {object} controller.Response{data=[]DeviceResponse} "业务码为200代表获取成功" // @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体" // @Router /devices [get] func (c *Controller) ListDevices(ctx *gin.Context) { devices, err := c.repo.ListAll() if err != nil { c.logger.Errorf("获取设备列表: 数据库操作失败: %v", err) controller.SendErrorResponse(ctx, controller.CodeInternalError, "获取设备列表失败") return } controller.SendResponse(ctx, controller.CodeSuccess, "获取设备列表成功", newListDeviceResponse(devices)) } // UpdateDevice godoc // @Summary 更新设备信息 // @Description 根据设备ID更新一个已存在的设备信息 // @Tags 设备管理 // @Accept json // @Produce json // @Param id path string true "设备ID" // @Param device body UpdateDeviceRequest true "要更新的设备信息" // @Success 200 {object} controller.Response{data=DeviceResponse} "业务码为200代表更新成功" // @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体" // @Router /devices/{id} [put] func (c *Controller) UpdateDevice(ctx *gin.Context) { deviceID := ctx.Param("id") // 1. 检查设备是否存在 existingDevice, err := c.repo.FindByIDString(deviceID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { controller.SendErrorResponse(ctx, controller.CodeNotFound, "设备未找到") return } if strings.Contains(err.Error(), "无效的设备ID格式") { controller.SendErrorResponse(ctx, controller.CodeBadRequest, err.Error()) return } c.logger.Errorf("更新设备: 查找设备失败: %v", err) controller.SendErrorResponse(ctx, controller.CodeInternalError, "更新设备失败") return } // 2. 绑定请求参数 var req UpdateDeviceRequest if err := ctx.ShouldBindJSON(&req); err != nil { c.logger.Errorf("更新设备: 参数绑定失败: %v", err) controller.SendErrorResponse(ctx, controller.CodeBadRequest, err.Error()) return } // 3. 更新从数据库中查出的现有设备对象的字段 existingDevice.Name = req.Name existingDevice.Type = req.Type existingDevice.SubType = req.SubType existingDevice.ParentID = req.ParentID existingDevice.Location = req.Location existingDevice.Properties = datatypes.JSON(req.Properties) // 4. 将修改后的 existingDevice 对象保存回数据库 if err := c.repo.Update(existingDevice); err != nil { c.logger.Errorf("更新设备: 数据库操作失败: %v", err) controller.SendErrorResponse(ctx, controller.CodeInternalError, "更新设备失败") return } controller.SendResponse(ctx, controller.CodeSuccess, "设备更新成功", newDeviceResponse(existingDevice)) } // DeleteDevice godoc // @Summary 删除设备 // @Description 根据设备ID删除一个设备(软删除) // @Tags 设备管理 // @Produce json // @Param id path string true "设备ID" // @Success 200 {object} controller.Response "业务码为200代表删除成功" // @Failure 200 {object} controller.Response "业务失败,具体错误码和信息见响应体" // @Router /devices/{id} [delete] func (c *Controller) DeleteDevice(ctx *gin.Context) { deviceID := ctx.Param("id") // 我们需要先将字符串ID转换为uint,因为Delete方法需要uint类型 idUint, err := strconv.ParseUint(deviceID, 10, 64) if err != nil { controller.SendErrorResponse(ctx, controller.CodeBadRequest, "无效的设备ID格式") return } if err := c.repo.Delete(uint(idUint)); err != nil { c.logger.Errorf("删除设备: 数据库操作失败: %v", err) controller.SendErrorResponse(ctx, controller.CodeInternalError, "删除设备失败") return } controller.SendResponse(ctx, controller.CodeSuccess, "设备删除成功", nil) }