@@ -20,6 +20,7 @@ import (
type Controller struct {
deviceRepo repository . DeviceRepository
areaControllerRepo repository . AreaControllerRepository
deviceTemplateRepo repository . DeviceTemplateRepository // 设备模板仓库
logger * logs . Logger
}
@@ -27,11 +28,13 @@ type Controller struct {
func NewController (
deviceRepo repository . DeviceRepository ,
areaControllerRepo repository . AreaControllerRepository ,
deviceTemplateRepo repository . DeviceTemplateRepository , // 注入设备模板仓库
logger * logs . Logger ,
) * Controller {
return & Controller {
deviceRepo : deviceRepo ,
areaControllerRepo : areaControllerRepo ,
deviceTemplateRepo : deviceTemplateRepo , // 初始化设备模板仓库
logger : logger ,
}
}
@@ -72,6 +75,26 @@ type UpdateAreaControllerRequest struct {
Properties map [ string ] interface { } ` json:"properties,omitempty" `
}
// CreateDeviceTemplateRequest 定义了创建设备模板时需要传入的参数
type CreateDeviceTemplateRequest struct {
Name string ` json:"name" binding:"required" `
Manufacturer string ` json:"manufacturer,omitempty" `
Description string ` json:"description,omitempty" `
Category models . DeviceCategory ` json:"category" binding:"required" `
Commands map [ string ] interface { } ` json:"commands" binding:"required" `
Values [ ] models . ValueDescriptor ` json:"values,omitempty" `
}
// UpdateDeviceTemplateRequest 定义了更新设备模板时需要传入的参数
type UpdateDeviceTemplateRequest struct {
Name string ` json:"name" binding:"required" `
Manufacturer string ` json:"manufacturer,omitempty" `
Description string ` json:"description,omitempty" `
Category models . DeviceCategory ` json:"category" binding:"required" `
Commands map [ string ] interface { } ` json:"commands" binding:"required" `
Values [ ] models . ValueDescriptor ` json:"values,omitempty" `
}
// --- Response DTOs ---
// DeviceResponse 定义了返回给客户端的单个设备信息的结构
@@ -100,6 +123,19 @@ type AreaControllerResponse struct {
UpdatedAt string ` json:"updated_at" `
}
// DeviceTemplateResponse 定义了返回给客户端的单个设备模板信息的结构
type DeviceTemplateResponse struct {
ID uint ` json:"id" `
Name string ` json:"name" `
Manufacturer string ` json:"manufacturer" `
Description string ` json:"description" `
Category models . DeviceCategory ` json:"category" `
Commands map [ string ] interface { } ` json:"commands" `
Values [ ] models . ValueDescriptor ` json:"values" `
CreatedAt string ` json:"created_at" `
UpdatedAt string ` json:"updated_at" `
}
// --- DTO 转换函数 ---
// newDeviceResponse 从数据库模型创建一个新的设备响应 DTO
@@ -191,6 +227,50 @@ func newListAreaControllerResponse(acs []*models.AreaController) ([]*AreaControl
return list , nil
}
// newDeviceTemplateResponse 从数据库模型创建一个新的设备模板响应 DTO
func newDeviceTemplateResponse ( dt * models . DeviceTemplate ) ( * DeviceTemplateResponse , error ) {
if dt == nil {
return nil , nil
}
var commands map [ string ] interface { }
if err := dt . ParseCommands ( & commands ) ; err != nil {
return nil , fmt . Errorf ( "解析设备模板命令失败 (ID: %d): %w" , dt . ID , err )
}
var values [ ] models . ValueDescriptor
if dt . Category == models . CategorySensor {
if err := dt . ParseValues ( & values ) ; err != nil {
return nil , fmt . Errorf ( "解析设备模板值描述符失败 (ID: %d): %w" , dt . ID , err )
}
}
return & DeviceTemplateResponse {
ID : dt . ID ,
Name : dt . Name ,
Manufacturer : dt . Manufacturer ,
Description : dt . Description ,
Category : dt . Category ,
Commands : commands ,
Values : values ,
CreatedAt : dt . CreatedAt . Format ( time . RFC3339 ) ,
UpdatedAt : dt . UpdatedAt . Format ( time . RFC3339 ) ,
} , nil
}
// newListDeviceTemplateResponse 从数据库模型切片创建一个新的设备模板列表响应 DTO 切片
func newListDeviceTemplateResponse ( dts [ ] * models . DeviceTemplate ) ( [ ] * DeviceTemplateResponse , error ) {
list := make ( [ ] * DeviceTemplateResponse , 0 , len ( dts ) )
for _ , dt := range dts {
resp , err := newDeviceTemplateResponse ( dt )
if err != nil {
return nil , err
}
list = append ( list , resp )
}
return list , nil
}
// --- Controller Methods: Devices ---
// CreateDevice godoc
@@ -692,3 +772,271 @@ func (c *Controller) DeleteAreaController(ctx *gin.Context) {
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 设备模板管理
// @Accept json
// @Produce json
// @Param deviceTemplate body CreateDeviceTemplateRequest true "设备模板信息"
// @Success 200 {object} controller.Response{data=DeviceTemplateResponse}
// @Router /api/v1/device-templates [post]
func ( c * Controller ) CreateDeviceTemplate ( ctx * gin . Context ) {
const actionType = "创建设备模板"
var req 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 := 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 设备模板管理
// @Produce json
// @Param id path string true "设备模板ID"
// @Success 200 {object} controller.Response{data=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 := 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 设备模板管理
// @Produce json
// @Success 200 {object} controller.Response{data=[]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 := 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 设备模板管理
// @Accept json
// @Produce json
// @Param id path string true "设备模板ID"
// @Param deviceTemplate body UpdateDeviceTemplateRequest true "要更新的设备模板信息"
// @Success 200 {object} controller.Response{data=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 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 := 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 设备模板管理
// @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 )
}