增加设备模板列表
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user