@@ -16,17 +16,23 @@ import (
"gorm.io/gorm"
)
// Controller 设备控制器,封装了所有与设备相关的业务逻辑
// Controller 设备控制器,封装了所有与设备和区域主控 相关的业务逻辑
type Controller struct {
repo repository . DeviceRepository
logger * logs . Logger
deviceRepo repository . DeviceRepository
areaControllerRepo repository . AreaControllerRepository
logger * logs . Logger
}
// NewController 创建一个新的设备控制器实例
func NewController ( repo repository . DeviceRepository , logger * logs . Logger ) * Controller {
func NewController (
deviceRepo repository . DeviceRepository ,
areaControllerRepo repository . AreaControllerRepository ,
logger * logs . Logger ,
) * Controller {
return & Controller {
r epo: r epo,
logger : logger ,
deviceR epo: deviceR epo ,
areaControllerRepo : areaControllerRepo ,
logger : logger ,
}
}
@@ -50,6 +56,22 @@ type UpdateDeviceRequest struct {
Properties map [ string ] interface { } ` json:"properties,omitempty" `
}
// CreateAreaControllerRequest 定义了创建区域主控时需要传入的参数
type CreateAreaControllerRequest struct {
Name string ` json:"name" binding:"required" `
NetworkID string ` json:"network_id" binding:"required" `
Location string ` json:"location,omitempty" `
Properties map [ string ] interface { } ` json:"properties,omitempty" `
}
// UpdateAreaControllerRequest 定义了更新区域主控时需要传入的参数
type UpdateAreaControllerRequest struct {
Name string ` json:"name" binding:"required" `
NetworkID string ` json:"network_id" binding:"required" `
Location string ` json:"location,omitempty" `
Properties map [ string ] interface { } ` json:"properties,omitempty" `
}
// --- Response DTOs ---
// DeviceResponse 定义了返回给客户端的单个设备信息的结构
@@ -66,6 +88,18 @@ type DeviceResponse struct {
UpdatedAt string ` json:"updated_at" `
}
// AreaControllerResponse 定义了返回给客户端的单个区域主控信息的结构
type AreaControllerResponse struct {
ID uint ` json:"id" `
Name string ` json:"name" `
NetworkID string ` json:"network_id" `
Location string ` json:"location" `
Status string ` json:"status" `
Properties map [ string ] interface { } ` json:"properties" `
CreatedAt string ` json:"created_at" `
UpdatedAt string ` json:"updated_at" `
}
// --- DTO 转换函数 ---
// newDeviceResponse 从数据库模型创建一个新的设备响应 DTO
@@ -119,7 +153,45 @@ func newListDeviceResponse(devices []*models.Device) ([]*DeviceResponse, error)
return list , nil
}
// --- Controller Methods ---
// newArea ControllerResponse 从数据库模型创建一个新的区域主控响应 DTO
func newAreaControllerResponse ( ac * models . AreaController ) ( * AreaControllerResponse , error ) {
if ac == nil {
return nil , nil
}
var props map [ string ] interface { }
if len ( ac . Properties ) > 0 && string ( ac . Properties ) != "null" {
if err := json . Unmarshal ( ac . Properties , & props ) ; err != nil {
return nil , fmt . Errorf ( "解析区域主控属性失败 (ID: %d): %w" , ac . ID , err )
}
}
return & AreaControllerResponse {
ID : ac . ID ,
Name : ac . Name ,
NetworkID : ac . NetworkID ,
Location : ac . Location ,
Status : ac . Status ,
Properties : props ,
CreatedAt : ac . CreatedAt . Format ( time . RFC3339 ) ,
UpdatedAt : ac . UpdatedAt . Format ( time . RFC3339 ) ,
} , nil
}
// newListAreaControllerResponse 从数据库模型切片创建一个新的区域主控列表响应 DTO 切片
func newListAreaControllerResponse ( acs [ ] * models . AreaController ) ( [ ] * AreaControllerResponse , error ) {
list := make ( [ ] * AreaControllerResponse , 0 , len ( acs ) )
for _ , ac := range acs {
resp , err := newAreaControllerResponse ( ac )
if err != nil {
return nil , err
}
list = append ( list , resp )
}
return list , nil
}
// --- Controller Methods: Devices ---
// CreateDevice godoc
// @Summary 创建新设备
@@ -154,25 +226,19 @@ func (c *Controller) CreateDevice(ctx *gin.Context) {
Properties : propertiesJSON ,
}
// 在创建设备前进行自检
// 注意:这里的 SelfCheck 依赖于 DeviceTemplate 和 AreaController 字段,
// 但在创建时这些关联对象可能尚未完全加载。如果 SelfCheck 内部需要这些关联对象,
// 则需要在调用 SelfCheck 之前手动加载或调整 SelfCheck 逻辑。
// 目前假设 SelfCheck 仅检查 ID 和 Properties。
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 . r epo. Create ( device ) ; err != nil {
if err := c . deviceR epo. Create ( device ) ; err != nil {
c . logger . Errorf ( "%s: 数据库操作失败: %v" , actionType , err )
controller . SendErrorWithAudit ( ctx , controller . CodeInternalError , "创建设备失败: " + err . Error ( ) , actionType , "数据库创建失败" , device )
return
}
// 为了在响应中包含 DeviceTemplateName 和 AreaControllerName, 需要重新从数据库加载设备, 并预加载关联。
createdDevice , err := c . repo . FindByID ( device . ID )
createdDevice , err := c . deviceRepo . FindByID ( device . ID )
if err != nil {
c . logger . Errorf ( "%s: 重新加载创建的设备失败: %v" , actionType , err )
controller . SendErrorWithAudit ( ctx , controller . CodeInternalError , "设备创建成功,但重新加载设备失败" , actionType , "重新加载设备失败" , device )
@@ -208,8 +274,7 @@ func (c *Controller) GetDevice(ctx *gin.Context) {
return
}
// 假设 FindByIDString 方法会预加载 DeviceTemplate 和 AreaController
device , err := c . repo . FindByIDString ( deviceID )
device , err := c . deviceRepo . FindByIDString ( deviceID )
if err != nil {
if errors . Is ( err , gorm . ErrRecordNotFound ) {
c . logger . Warnf ( "%s: 设备不存在, ID: %s" , actionType , deviceID )
@@ -246,8 +311,7 @@ func (c *Controller) GetDevice(ctx *gin.Context) {
// @Router /api/v1/devices [get]
func ( c * Controller ) ListDevices ( ctx * gin . Context ) {
const actionType = "获取设备列表"
// 假设 ListAll 方法会预加载 DeviceTemplate 和 AreaController
devices , err := c . repo . ListAll ( )
devices , err := c . deviceRepo . ListAll ( )
if err != nil {
c . logger . Errorf ( "%s: 数据库查询失败: %v" , actionType , err )
controller . SendErrorWithAudit ( ctx , controller . CodeInternalError , "获取设备列表失败: " + err . Error ( ) , actionType , "数据库查询失败" , nil )
@@ -279,9 +343,7 @@ func (c *Controller) UpdateDevice(ctx *gin.Context) {
const actionType = "更新设备"
deviceID := ctx . Param ( "id" )
// 1. 检查设备是否存在
// 假设 FindByIDString 方法会预加载 DeviceTemplate 和 AreaController
existingDevice , err := c . repo . FindByIDString ( deviceID )
existingDevice , err := c . deviceRepo . FindByIDString ( deviceID )
if err != nil {
if errors . Is ( err , gorm . ErrRecordNotFound ) {
c . logger . Warnf ( "%s: 设备不存在, ID: %s" , actionType , deviceID )
@@ -298,7 +360,6 @@ func (c *Controller) UpdateDevice(ctx *gin.Context) {
return
}
// 2. 绑定请求参数
var req UpdateDeviceRequest
if err := ctx . ShouldBindJSON ( & req ) ; err != nil {
c . logger . Errorf ( "%s: 参数绑定失败: %v" , actionType , err )
@@ -313,33 +374,25 @@ func (c *Controller) UpdateDevice(ctx *gin.Context) {
return
}
// 3. 更新从数据库中查出的现有设备对象的字段
existingDevice . Name = req . Name
existingDevice . DeviceTemplateID = req . DeviceTemplateID
existingDevice . AreaControllerID = req . AreaControllerID
existingDevice . Location = req . Location
existingDevice . Properties = propertiesJSON
// 在更新设备前进行自检
// 注意:这里的 SelfCheck 依赖于 DeviceTemplate 和 AreaController 字段,
// 但在更新时这些关联对象可能尚未完全加载。如果 SelfCheck 内部需要这些关联对象,
// 则需要在调用 SelfCheck 之前手动加载或调整 SelfCheck 逻辑。
// 目前假设 SelfCheck 仅检查 ID 和 Properties。
if err := existingDevice . SelfCheck ( ) ; err != nil {
c . logger . Errorf ( "%s: 设备属性自检失败: %v" , actionType , err )
controller . SendErrorWithAudit ( ctx , controller . CodeBadRequest , "设备属性不符合要求: " + err . Error ( ) , actionType , "设备属性自检失败" , existingDevice )
return
}
// 4. 将修改后的 existingDevice 对象保存回数据库
if err := c . repo . Update ( existingDevice ) ; err != nil {
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
}
// 为了在响应中包含 DeviceTemplateName 和 AreaControllerName, 需要重新从数据库加载设备, 并预加载关联。
updatedDevice , err := c . repo . FindByID ( existingDevice . ID )
updatedDevice , err := c . deviceRepo . FindByID ( existingDevice . ID )
if err != nil {
c . logger . Errorf ( "%s: 重新加载更新的设备失败: %v" , actionType , err )
controller . SendErrorWithAudit ( ctx , controller . CodeInternalError , "设备更新成功,但重新加载设备失败" , actionType , "重新加载设备失败" , existingDevice )
@@ -369,7 +422,6 @@ func (c *Controller) DeleteDevice(ctx *gin.Context) {
const actionType = "删除设备"
deviceID := ctx . Param ( "id" )
// 我们需要先将字符串ID转换为uint, 因为Delete方法需要uint类型
idUint , err := strconv . ParseUint ( deviceID , 10 , 64 )
if err != nil {
c . logger . Errorf ( "%s: 设备ID格式错误: %v, ID: %s" , actionType , err , deviceID )
@@ -377,8 +429,7 @@ func (c *Controller) DeleteDevice(ctx *gin.Context) {
return
}
// 检查设备是否存在(可选,但通常在删除前会检查)
_ , err = c . repo . FindByIDString ( deviceID )
_ , err = c . deviceRepo . FindByIDString ( deviceID )
if err != nil {
if errors . Is ( err , gorm . ErrRecordNotFound ) {
c . logger . Warnf ( "%s: 设备不存在, ID: %s" , actionType , deviceID )
@@ -390,7 +441,7 @@ func (c *Controller) DeleteDevice(ctx *gin.Context) {
return
}
if err := c . r epo. Delete ( uint ( idUint ) ) ; err != nil {
if err := c . deviceR epo. 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
@@ -399,3 +450,245 @@ func (c *Controller) DeleteDevice(ctx *gin.Context) {
c . logger . Infof ( "%s: 设备删除成功, ID: %d" , actionType , idUint )
controller . SendSuccessWithAudit ( ctx , controller . CodeSuccess , "设备删除成功" , nil , actionType , "设备删除成功" , deviceID )
}
// --- Controller Methods: Area Controllers ---
// CreateAreaController godoc
// @Summary 创建新区域主控
// @Description 根据提供的信息创建一个新区域主控
// @Tags 区域主控管理
// @Accept json
// @Produce json
// @Param areaController body CreateAreaControllerRequest true "区域主控信息"
// @Success 200 {object} controller.Response{data=AreaControllerResponse}
// @Router /api/v1/area-controllers [post]
func ( c * Controller ) CreateAreaController ( ctx * gin . Context ) {
const actionType = "创建区域主控"
var req 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 := 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 区域主控管理
// @Produce json
// @Param id path string true "区域主控ID"
// @Success 200 {object} controller.Response{data=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 := 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 区域主控管理
// @Produce json
// @Success 200 {object} controller.Response{data=[]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 := 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 区域主控管理
// @Accept json
// @Produce json
// @Param id path string true "区域主控ID"
// @Param areaController body UpdateAreaControllerRequest true "要更新的区域主控信息"
// @Success 200 {object} controller.Response{data=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 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 := 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 区域主控管理
// @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 )
}