Files
pig-farm-controller/internal/app/controller/device/device_controller.go
2025-09-14 13:23:16 +08:00

268 lines
9.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}