Files
pig-farm-controller/internal/app/controller/device/device_controller.go
2025-10-13 14:15:38 +08:00

891 lines
40 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 (
"encoding/json"
"errors"
"strconv"
"strings"
"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/domain/device"
"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/gorm"
)
// Controller 设备控制器,封装了所有与设备和区域主控相关的业务逻辑
type Controller struct {
deviceRepo repository.DeviceRepository
areaControllerRepo repository.AreaControllerRepository
deviceTemplateRepo repository.DeviceTemplateRepository
deviceService device.Service
logger *logs.Logger
}
// NewController 创建一个新的设备控制器实例
func NewController(
deviceRepo repository.DeviceRepository,
areaControllerRepo repository.AreaControllerRepository,
deviceTemplateRepo repository.DeviceTemplateRepository,
deviceService device.Service,
logger *logs.Logger,
) *Controller {
return &Controller{
deviceRepo: deviceRepo,
areaControllerRepo: areaControllerRepo,
deviceTemplateRepo: deviceTemplateRepo,
deviceService: deviceService,
logger: logger,
}
}
// --- Controller Methods: Devices ---
// CreateDevice godoc
// @Summary 创建新设备
// @Description 根据提供的信息创建一个新设备
// @Tags 设备管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param device body dto.CreateDeviceRequest true "设备信息"
// @Success 200 {object} controller.Response{data=dto.DeviceResponse}
// @Router /api/v1/devices [post]
func (c *Controller) CreateDevice(ctx *gin.Context) {
const actionType = "创建设备"
var req dto.CreateDeviceRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return
}
propertiesJSON, err := json.Marshal(req.Properties)
if err != nil {
c.logger.Errorf("%s: 序列化属性失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "属性字段格式错误", actionType, "属性序列化失败", req.Properties)
return
}
device := &models.Device{
Name: req.Name,
DeviceTemplateID: req.DeviceTemplateID,
AreaControllerID: req.AreaControllerID,
Location: req.Location,
Properties: propertiesJSON,
}
if err := device.SelfCheck(); err != nil {
c.logger.Errorf("%s: 设备属性自检失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备属性不符合要求: "+err.Error(), actionType, "设备属性自检失败", device)
return
}
if err := c.deviceRepo.Create(device); err != nil {
c.logger.Errorf("%s: 数据库操作失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建设备失败: "+err.Error(), actionType, "数据库创建失败", device)
return
}
createdDevice, err := c.deviceRepo.FindByID(device.ID)
if err != nil {
c.logger.Errorf("%s: 重新加载创建的设备失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备创建成功,但重新加载设备失败", actionType, "重新加载设备失败", device)
return
}
resp, err := dto.NewDeviceResponse(createdDevice)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, createdDevice)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备创建成功,但响应生成失败", actionType, "响应序列化失败", createdDevice)
return
}
c.logger.Infof("%s: 设备创建成功, ID: %d", actionType, device.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "设备创建成功", resp, actionType, "设备创建成功", resp)
}
// GetDevice godoc
// @Summary 获取设备信息
// @Description 根据设备ID获取单个设备的详细信息
// @Tags 设备管理
// @Security BearerAuth
// @Produce json
// @Param id path string true "设备ID"
// @Success 200 {object} controller.Response{data=dto.DeviceResponse}
// @Router /api/v1/devices/{id} [get]
func (c *Controller) GetDevice(ctx *gin.Context) {
const actionType = "获取设备"
deviceID := ctx.Param("id")
if deviceID == "" {
c.logger.Errorf("%s: 设备ID为空", actionType)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备ID不能为空", actionType, "设备ID为空", nil)
return
}
device, err := c.deviceRepo.FindByIDString(deviceID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备不存在, ID: %s", actionType, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备未找到", actionType, "设备不存在", deviceID)
return
}
if strings.Contains(err.Error(), "无效的设备ID格式") {
c.logger.Errorf("%s: 设备ID格式错误: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "设备ID格式错误", deviceID)
return
}
c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备信息失败: "+err.Error(), actionType, "数据库查询失败", deviceID)
return
}
resp, err := dto.NewDeviceResponse(device)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, device)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备信息失败: 内部数据格式错误", actionType, "响应序列化失败", device)
return
}
c.logger.Infof("%s: 获取设备信息成功, ID: %d", actionType, device.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备信息成功", resp, actionType, "获取设备信息成功", resp)
}
// ListDevices godoc
// @Summary 获取设备列表
// @Description 获取系统中所有设备的列表
// @Tags 设备管理
// @Security BearerAuth
// @Produce json
// @Success 200 {object} controller.Response{data=[]dto.DeviceResponse}
// @Router /api/v1/devices [get]
func (c *Controller) ListDevices(ctx *gin.Context) {
const actionType = "获取设备列表"
devices, err := c.deviceRepo.ListAll()
if err != nil {
c.logger.Errorf("%s: 数据库查询失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备列表失败: "+err.Error(), actionType, "数据库查询失败", nil)
return
}
resp, err := dto.NewListDeviceResponse(devices)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, Devices: %+v", actionType, err, devices)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备列表失败: 内部数据格式错误", actionType, "响应序列化失败", devices)
return
}
c.logger.Infof("%s: 获取设备列表成功, 数量: %d", actionType, len(devices))
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备列表成功", resp, actionType, "获取设备列表成功", resp)
}
// UpdateDevice godoc
// @Summary 更新设备信息
// @Description 根据设备ID更新一个已存在的设备信息
// @Tags 设备管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param id path string true "设备ID"
// @Param device body dto.UpdateDeviceRequest true "要更新的设备信息"
// @Success 200 {object} controller.Response{data=dto.DeviceResponse}
// @Router /api/v1/devices/{id} [put]
func (c *Controller) UpdateDevice(ctx *gin.Context) {
const actionType = "更新设备"
deviceID := ctx.Param("id")
existingDevice, err := c.deviceRepo.FindByIDString(deviceID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备不存在, ID: %s", actionType, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备未找到", actionType, "设备不存在", deviceID)
return
}
if strings.Contains(err.Error(), "无效的设备ID格式") {
c.logger.Errorf("%s: 设备ID格式错误: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "设备ID格式错误", deviceID)
return
}
c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备失败: "+err.Error(), actionType, "数据库查询失败", deviceID)
return
}
var req dto.UpdateDeviceRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return
}
propertiesJSON, err := json.Marshal(req.Properties)
if err != nil {
c.logger.Errorf("%s: 序列化属性失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "属性字段格式错误", actionType, "属性序列化失败", req.Properties)
return
}
existingDevice.Name = req.Name
existingDevice.DeviceTemplateID = req.DeviceTemplateID
existingDevice.AreaControllerID = req.AreaControllerID
existingDevice.Location = req.Location
existingDevice.Properties = propertiesJSON
if err := existingDevice.SelfCheck(); err != nil {
c.logger.Errorf("%s: 设备属性自检失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备属性不符合要求: "+err.Error(), actionType, "设备属性自检失败", existingDevice)
return
}
if err := c.deviceRepo.Update(existingDevice); err != nil {
c.logger.Errorf("%s: 数据库更新失败: %v, Device: %+v", actionType, err, existingDevice)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备失败: "+err.Error(), actionType, "数据库更新失败", existingDevice)
return
}
updatedDevice, err := c.deviceRepo.FindByID(existingDevice.ID)
if err != nil {
c.logger.Errorf("%s: 重新加载更新的设备失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备更新成功,但重新加载设备失败", actionType, "重新加载设备失败", existingDevice)
return
}
resp, err := dto.NewDeviceResponse(updatedDevice)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, Device: %+v", actionType, err, updatedDevice)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备更新成功,但响应生成失败", actionType, "响应序列化失败", updatedDevice)
return
}
c.logger.Infof("%s: 设备更新成功, ID: %d", actionType, existingDevice.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备更新成功", resp, actionType, "设备更新成功", resp)
}
// DeleteDevice godoc
// @Summary 删除设备
// @Description 根据设备ID删除一个设备软删除
// @Tags 设备管理
// @Security BearerAuth
// @Produce json
// @Param id path string true "设备ID"
// @Success 200 {object} controller.Response
// @Router /api/v1/devices/{id} [delete]
func (c *Controller) DeleteDevice(ctx *gin.Context) {
const actionType = "删除设备"
deviceID := ctx.Param("id")
idUint, err := strconv.ParseUint(deviceID, 10, 64)
if err != nil {
c.logger.Errorf("%s: 设备ID格式错误: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的设备ID格式", actionType, "设备ID格式错误", deviceID)
return
}
_, err = c.deviceRepo.FindByIDString(deviceID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备不存在, ID: %s", actionType, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备未找到", actionType, "设备不存在", deviceID)
return
}
c.logger.Errorf("%s: 查找设备失败: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备失败: 查找设备时发生内部错误", actionType, "数据库查询失败", deviceID)
return
}
if err := c.deviceRepo.Delete(uint(idUint)); err != nil {
c.logger.Errorf("%s: 数据库删除失败: %v, ID: %d", actionType, err, idUint)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备失败: "+err.Error(), actionType, "数据库删除失败", deviceID)
return
}
c.logger.Infof("%s: 设备删除成功, ID: %d", actionType, idUint)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备删除成功", nil, actionType, "设备删除成功", deviceID)
}
// ManualControl godoc
// @Summary 手动控制设备
// @Description 根据设备ID和指定的动作开启或关闭来手动控制设备
// @Tags 设备管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param id path string true "设备ID"
// @Param manualControl body dto.ManualControlDeviceRequest true "手动控制指令"
// @Success 200 {object} controller.Response
// @Router /api/v1/devices/manual-control/{id} [post]
func (c *Controller) ManualControl(ctx *gin.Context) {
const actionType = "手动控制设备"
deviceID := ctx.Param("id")
var req dto.ManualControlDeviceRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return
}
dev, err := c.deviceRepo.FindByIDString(deviceID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备不存在, ID: %s", actionType, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备未找到", actionType, "设备不存在", deviceID)
return
}
if strings.Contains(err.Error(), "无效的设备ID格式") {
c.logger.Errorf("%s: 设备ID格式错误: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "设备ID格式错误", deviceID)
return
}
c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "手动控制失败: "+err.Error(), actionType, "数据库查询失败", deviceID)
return
}
c.logger.Infof("%s: 接收到指令, 设备ID: %s, 动作: %s", actionType, deviceID, req.Action)
if req.Action == nil {
err = c.deviceService.Collect(dev.AreaControllerID, []*models.Device{dev})
if err != nil {
c.logger.Errorf("%s: 获取设备状态失败: %v, 设备ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备状态失败: "+err.Error(), actionType, "获取设备状态失败", deviceID)
}
} else {
action := device.DeviceActionStart
if *req.Action == "off" {
action = device.DeviceActionStop
}
err = c.deviceService.Switch(dev, action)
if err != nil {
c.logger.Errorf("%s: 设备控制失败: %v, 设备ID: %s", actionType, err, deviceID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备控制失败: "+err.Error(), actionType, "设备控制失败", deviceID)
return
}
}
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "指令已发送", map[string]interface{}{"device_id": deviceID}, actionType, "指令发送成功", gin.H{"device_id": deviceID, "action": req.Action})
}
// --- Controller Methods: Area Controllers ---
// CreateAreaController godoc
// @Summary 创建新区域主控
// @Description 根据提供的信息创建一个新区域主控
// @Tags 区域主控管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param areaController body dto.CreateAreaControllerRequest true "区域主控信息"
// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse}
// @Router /api/v1/area-controllers [post]
func (c *Controller) CreateAreaController(ctx *gin.Context) {
const actionType = "创建区域主控"
var req dto.CreateAreaControllerRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return
}
propertiesJSON, err := json.Marshal(req.Properties)
if err != nil {
c.logger.Errorf("%s: 序列化属性失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "属性字段格式错误", actionType, "属性序列化失败", req.Properties)
return
}
ac := &models.AreaController{
Name: req.Name,
NetworkID: req.NetworkID,
Location: req.Location,
Properties: propertiesJSON,
}
if err := ac.SelfCheck(); err != nil {
c.logger.Errorf("%s: 区域主控自检失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "区域主控参数不符合要求: "+err.Error(), actionType, "区域主控自检失败", ac)
return
}
if err := c.areaControllerRepo.Create(ac); err != nil {
c.logger.Errorf("%s: 数据库操作失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建区域主控失败: "+err.Error(), actionType, "数据库创建失败", ac)
return
}
resp, err := dto.NewAreaControllerResponse(ac)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "区域主控创建成功,但响应生成失败", actionType, "响应序列化失败", ac)
return
}
c.logger.Infof("%s: 区域主控创建成功, ID: %d", actionType, ac.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "区域主控创建成功", resp, actionType, "区域主控创建成功", resp)
}
// GetAreaController godoc
// @Summary 获取区域主控信息
// @Description 根据ID获取单个区域主控的详细信息
// @Tags 区域主控管理
// @Security BearerAuth
// @Produce json
// @Param id path string true "区域主控ID"
// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse}
// @Router /api/v1/area-controllers/{id} [get]
func (c *Controller) GetAreaController(ctx *gin.Context) {
const actionType = "获取区域主控"
acID := ctx.Param("id")
idUint, err := strconv.ParseUint(acID, 10, 64)
if err != nil {
c.logger.Errorf("%s: 区域主控ID格式错误: %v, ID: %s", actionType, err, acID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的区域主控ID格式", actionType, "ID格式错误", acID)
return
}
ac, err := c.areaControllerRepo.FindByID(uint(idUint))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID)
return
}
c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, acID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控信息失败: "+err.Error(), actionType, "数据库查询失败", acID)
return
}
resp, err := dto.NewAreaControllerResponse(ac)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, AreaController: %+v", actionType, err, ac)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控信息失败: 内部数据格式错误", actionType, "响应序列化失败", ac)
return
}
c.logger.Infof("%s: 获取区域主控信息成功, ID: %d", actionType, ac.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取区域主控信息成功", resp, actionType, "获取区域主控信息成功", resp)
}
// ListAreaControllers godoc
// @Summary 获取所有区域主控列表
// @Description 获取系统中所有区域主控的列表
// @Tags 区域主控管理
// @Security BearerAuth
// @Produce json
// @Success 200 {object} controller.Response{data=[]dto.AreaControllerResponse}
// @Router /api/v1/area-controllers [get]
func (c *Controller) ListAreaControllers(ctx *gin.Context) {
const actionType = "获取区域主控列表"
acs, err := c.areaControllerRepo.ListAll()
if err != nil {
c.logger.Errorf("%s: 数据库查询失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控列表失败: "+err.Error(), actionType, "数据库查询失败", nil)
return
}
resp, err := dto.NewListAreaControllerResponse(acs)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, AreaControllers: %+v", actionType, err, acs)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取区域主控列表失败: 内部数据格式错误", actionType, "响应序列化失败", acs)
return
}
c.logger.Infof("%s: 获取区域主控列表成功, 数量: %d", actionType, len(acs))
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取区域主控列表成功", resp, actionType, "获取区域主控列表成功", resp)
}
// UpdateAreaController godoc
// @Summary 更新区域主控信息
// @Description 根据ID更新一个已存在的区域主控信息
// @Tags 区域主控管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param id path string true "区域主控ID"
// @Param areaController body dto.UpdateAreaControllerRequest true "要更新的区域主控信息"
// @Success 200 {object} controller.Response{data=dto.AreaControllerResponse}
// @Router /api/v1/area-controllers/{id} [put]
func (c *Controller) UpdateAreaController(ctx *gin.Context) {
const actionType = "更新区域主控"
acID := ctx.Param("id")
idUint, err := strconv.ParseUint(acID, 10, 64)
if err != nil {
c.logger.Errorf("%s: 区域主控ID格式错误: %v, ID: %s", actionType, err, acID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的区域主控ID格式", actionType, "ID格式错误", acID)
return
}
existingAC, err := c.areaControllerRepo.FindByID(uint(idUint))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID)
return
}
c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, acID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新区域主控失败: "+err.Error(), actionType, "数据库查询失败", acID)
return
}
var req dto.UpdateAreaControllerRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return
}
propertiesJSON, err := json.Marshal(req.Properties)
if err != nil {
c.logger.Errorf("%s: 序列化属性失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "属性字段格式错误", actionType, "属性序列化失败", req.Properties)
return
}
existingAC.Name = req.Name
existingAC.NetworkID = req.NetworkID
existingAC.Location = req.Location
existingAC.Properties = propertiesJSON
if err := existingAC.SelfCheck(); err != nil {
c.logger.Errorf("%s: 区域主控自检失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "区域主控参数不符合要求: "+err.Error(), actionType, "区域主控自检失败", existingAC)
return
}
if err := c.areaControllerRepo.Update(existingAC); err != nil {
c.logger.Errorf("%s: 数据库更新失败: %v, AreaController: %+v", actionType, err, existingAC)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新区域主控失败: "+err.Error(), actionType, "数据库更新失败", existingAC)
return
}
resp, err := dto.NewAreaControllerResponse(existingAC)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, AreaController: %+v", actionType, err, existingAC)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "区域主控更新成功,但响应生成失败", actionType, "响应序列化失败", existingAC)
return
}
c.logger.Infof("%s: 区域主控更新成功, ID: %d", actionType, existingAC.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "区域主控更新成功", resp, actionType, "区域主控更新成功", resp)
}
// DeleteAreaController godoc
// @Summary 删除区域主控
// @Description 根据ID删除一个区域主控软删除
// @Tags 区域主控管理
// @Security BearerAuth
// @Produce json
// @Param id path string true "区域主控ID"
// @Success 200 {object} controller.Response
// @Router /api/v1/area-controllers/{id} [delete]
func (c *Controller) DeleteAreaController(ctx *gin.Context) {
const actionType = "删除区域主控"
acID := ctx.Param("id")
idUint, err := strconv.ParseUint(acID, 10, 64)
if err != nil {
c.logger.Errorf("%s: 区域主控ID格式错误: %v, ID: %s", actionType, err, acID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的区域主控ID格式", actionType, "ID格式错误", acID)
return
}
_, err = c.areaControllerRepo.FindByID(uint(idUint))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 区域主控不存在, ID: %s", actionType, acID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "区域主控未找到", actionType, "区域主控不存在", acID)
return
}
c.logger.Errorf("%s: 查找区域主控失败: %v, ID: %s", actionType, err, acID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除区域主控失败: 查找时发生内部错误", actionType, "数据库查询失败", acID)
return
}
if err := c.areaControllerRepo.Delete(uint(idUint)); err != nil {
c.logger.Errorf("%s: 数据库删除失败: %v, ID: %d", actionType, err, idUint)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除区域主控失败: "+err.Error(), actionType, "数据库删除失败", acID)
return
}
c.logger.Infof("%s: 区域主控删除成功, ID: %d", actionType, idUint)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "区域主控删除成功", nil, actionType, "区域主控删除成功", acID)
}
// --- Controller Methods: Device Templates ---
// CreateDeviceTemplate godoc
// @Summary 创建新设备模板
// @Description 根据提供的信息创建一个新设备模板
// @Tags 设备模板管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param deviceTemplate body dto.CreateDeviceTemplateRequest true "设备模板信息"
// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse}
// @Router /api/v1/device-templates [post]
func (c *Controller) CreateDeviceTemplate(ctx *gin.Context) {
const actionType = "创建设备模板"
var req dto.CreateDeviceTemplateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return
}
commandsJSON, err := json.Marshal(req.Commands)
if err != nil {
c.logger.Errorf("%s: 序列化命令失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "命令字段格式错误", actionType, "命令序列化失败", req.Commands)
return
}
valuesJSON, err := json.Marshal(req.Values)
if err != nil {
c.logger.Errorf("%s: 序列化值描述符失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "值描述符字段格式错误", actionType, "值描述符序列化失败", req.Values)
return
}
deviceTemplate := &models.DeviceTemplate{
Name: req.Name,
Manufacturer: req.Manufacturer,
Description: req.Description,
Category: req.Category,
Commands: commandsJSON,
Values: valuesJSON,
}
if err := deviceTemplate.SelfCheck(); err != nil {
c.logger.Errorf("%s: 设备模板自检失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备模板参数不符合要求: "+err.Error(), actionType, "设备模板自检失败", deviceTemplate)
return
}
if err := c.deviceTemplateRepo.Create(deviceTemplate); err != nil {
c.logger.Errorf("%s: 数据库操作失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "创建设备模板失败: "+err.Error(), actionType, "数据库创建失败", deviceTemplate)
return
}
resp, err := dto.NewDeviceTemplateResponse(deviceTemplate)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备模板创建成功,但响应生成失败", actionType, "响应序列化失败", deviceTemplate)
return
}
c.logger.Infof("%s: 设备模板创建成功, ID: %d", actionType, deviceTemplate.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeCreated, "设备模板创建成功", resp, actionType, "设备模板创建成功", resp)
}
// GetDeviceTemplate godoc
// @Summary 获取设备模板信息
// @Description 根据设备模板ID获取单个设备模板的详细信息
// @Tags 设备模板管理
// @Security BearerAuth
// @Produce json
// @Param id path string true "设备模板ID"
// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse}
// @Router /api/v1/device-templates/{id} [get]
func (c *Controller) GetDeviceTemplate(ctx *gin.Context) {
const actionType = "获取设备模板"
dtID := ctx.Param("id")
idUint, err := strconv.ParseUint(dtID, 10, 64)
if err != nil {
c.logger.Errorf("%s: 设备模板ID格式错误: %v, ID: %s", actionType, err, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的设备模板ID格式", actionType, "ID格式错误", dtID)
return
}
deviceTemplate, err := c.deviceTemplateRepo.FindByID(uint(idUint))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID)
return
}
c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板信息失败: "+err.Error(), actionType, "数据库查询失败", dtID)
return
}
resp, err := dto.NewDeviceTemplateResponse(deviceTemplate)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplate: %+v", actionType, err, deviceTemplate)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板信息失败: 内部数据格式错误", actionType, "响应序列化失败", deviceTemplate)
return
}
c.logger.Infof("%s: 获取设备模板信息成功, ID: %d", actionType, deviceTemplate.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备模板信息成功", resp, actionType, "获取设备模板信息成功", resp)
}
// ListDeviceTemplates godoc
// @Summary 获取设备模板列表
// @Description 获取系统中所有设备模板的列表
// @Tags 设备模板管理
// @Security BearerAuth
// @Produce json
// @Success 200 {object} controller.Response{data=[]dto.DeviceTemplateResponse}
// @Router /api/v1/device-templates [get]
func (c *Controller) ListDeviceTemplates(ctx *gin.Context) {
const actionType = "获取设备模板列表"
deviceTemplates, err := c.deviceTemplateRepo.ListAll()
if err != nil {
c.logger.Errorf("%s: 数据库查询失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板列表失败: "+err.Error(), actionType, "数据库查询失败", nil)
return
}
resp, err := dto.NewListDeviceTemplateResponse(deviceTemplates)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplates: %+v", actionType, err, deviceTemplates)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "获取设备模板列表失败: 内部数据格式错误", actionType, "响应序列化失败", deviceTemplates)
return
}
c.logger.Infof("%s: 获取设备模板列表成功, 数量: %d", actionType, len(deviceTemplates))
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "获取设备模板列表成功", resp, actionType, "获取设备模板列表成功", resp)
}
// UpdateDeviceTemplate godoc
// @Summary 更新设备模板信息
// @Description 根据设备模板ID更新一个已存在的设备模板信息
// @Tags 设备模板管理
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param id path string true "设备模板ID"
// @Param deviceTemplate body dto.UpdateDeviceTemplateRequest true "要更新的设备模板信息"
// @Success 200 {object} controller.Response{data=dto.DeviceTemplateResponse}
// @Router /api/v1/device-templates/{id} [put]
func (c *Controller) UpdateDeviceTemplate(ctx *gin.Context) {
const actionType = "更新设备模板"
dtID := ctx.Param("id")
idUint, err := strconv.ParseUint(dtID, 10, 64)
if err != nil {
c.logger.Errorf("%s: 设备模板ID格式错误: %v, ID: %s", actionType, err, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的设备模板ID格式", actionType, "ID格式错误", dtID)
return
}
existingDeviceTemplate, err := c.deviceTemplateRepo.FindByID(uint(idUint))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID)
return
}
c.logger.Errorf("%s: 数据库查询失败: %v, ID: %s", actionType, err, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备模板失败: "+err.Error(), actionType, "数据库查询失败", dtID)
return
}
var req dto.UpdateDeviceTemplateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
c.logger.Errorf("%s: 参数绑定失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的请求体: "+err.Error(), actionType, "请求体绑定失败", req)
return
}
commandsJSON, err := json.Marshal(req.Commands)
if err != nil {
c.logger.Errorf("%s: 序列化命令失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "命令字段格式错误", actionType, "命令序列化失败", req.Commands)
return
}
valuesJSON, err := json.Marshal(req.Values)
if err != nil {
c.logger.Errorf("%s: 序列化值描述符失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "值描述符字段格式错误", actionType, "值描述符序列化失败", req.Values)
return
}
existingDeviceTemplate.Name = req.Name
existingDeviceTemplate.Manufacturer = req.Manufacturer
existingDeviceTemplate.Description = req.Description
existingDeviceTemplate.Category = req.Category
existingDeviceTemplate.Commands = commandsJSON
existingDeviceTemplate.Values = valuesJSON
if err := existingDeviceTemplate.SelfCheck(); err != nil {
c.logger.Errorf("%s: 设备模板自检失败: %v", actionType, err)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "设备模板参数不符合要求: "+err.Error(), actionType, "设备模板自检失败", existingDeviceTemplate)
return
}
if err := c.deviceTemplateRepo.Update(existingDeviceTemplate); err != nil {
c.logger.Errorf("%s: 数据库更新失败: %v, DeviceTemplate: %+v", actionType, err, existingDeviceTemplate)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "更新设备模板失败: "+err.Error(), actionType, "数据库更新失败", existingDeviceTemplate)
return
}
resp, err := dto.NewDeviceTemplateResponse(existingDeviceTemplate)
if err != nil {
c.logger.Errorf("%s: 序列化响应失败: %v, DeviceTemplate: %+v", actionType, err, existingDeviceTemplate)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "设备模板更新成功,但响应生成失败", actionType, "响应序列化失败", existingDeviceTemplate)
return
}
c.logger.Infof("%s: 设备模板更新成功, ID: %d", actionType, existingDeviceTemplate.ID)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备模板更新成功", resp, actionType, "设备模板更新成功", resp)
}
// DeleteDeviceTemplate godoc
// @Summary 删除设备模板
// @Description 根据设备模板ID删除一个设备模板软删除
// @Tags 设备模板管理
// @Security BearerAuth
// @Produce json
// @Param id path string true "设备模板ID"
// @Success 200 {object} controller.Response
// @Router /api/v1/device-templates/{id} [delete]
func (c *Controller) DeleteDeviceTemplate(ctx *gin.Context) {
const actionType = "删除设备模板"
dtID := ctx.Param("id")
idUint, err := strconv.ParseUint(dtID, 10, 64)
if err != nil {
c.logger.Errorf("%s: 设备模板ID格式错误: %v, ID: %s", actionType, err, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的设备模板ID格式", actionType, "ID格式错误", dtID)
return
}
// 在尝试删除之前,先检查设备模板是否存在
_, err = c.deviceTemplateRepo.FindByID(uint(idUint))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.logger.Warnf("%s: 设备模板不存在, ID: %s", actionType, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeNotFound, "设备模板未找到", actionType, "设备模板不存在", dtID)
return
}
c.logger.Errorf("%s: 查找设备模板失败: %v, ID: %s", actionType, err, dtID)
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备模板失败: 查找时发生内部错误", actionType, "数据库查询失败", dtID)
return
}
// 调用仓库层的删除方法,该方法会检查模板是否被使用
if err := c.deviceTemplateRepo.Delete(uint(idUint)); err != nil {
c.logger.Errorf("%s: 数据库删除失败: %v, ID: %d", actionType, err, idUint)
// 如果错误信息包含“设备模板正在被设备使用,无法删除”,则返回特定的错误码
if strings.Contains(err.Error(), "设备模板正在被设备使用,无法删除") {
controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, err.Error(), actionType, "设备模板正在使用", dtID)
} else {
// 其他数据库错误
controller.SendErrorWithAudit(ctx, controller.CodeInternalError, "删除设备模板失败: "+err.Error(), actionType, "数据库删除失败", dtID)
}
return
}
c.logger.Infof("%s: 设备模板删除成功, ID: %d", actionType, idUint)
controller.SendSuccessWithAudit(ctx, controller.CodeSuccess, "设备模板删除成功", nil, actionType, "设备模板删除成功", dtID)
}