实现设备阈值检查任务

This commit is contained in:
2025-11-09 21:37:35 +08:00
parent a35a9a1038
commit e54c1bbc97
6 changed files with 340 additions and 20 deletions

View File

@@ -15,28 +15,51 @@ const (
AlarmSourceTypeSystem AlarmSourceType = "系统"
)
// AlarmCode 定义了标准化的告警类型标识
type AlarmCode string
const (
// --- 设备相关告警 ---
AlarmCodeTemperature AlarmCode = "温度阈值"
AlarmCodeHumidity AlarmCode = "湿度阈值"
AlarmCodeWeight AlarmCode = "重量阈值"
AlarmCodeBatteryLevel AlarmCode = "电池电量阈值"
AlarmCodeSignalMetrics AlarmCode = "信号强度阈值"
AlarmCodeDeviceOffline AlarmCode = "设备离线"
// --- 区域主控相关告警 ---
AlarmCodeAreaControllerOffline AlarmCode = "区域主控离线"
// --- 系统相关告警 ---
// (可在此处预留或添加)
)
// ActiveAlarm 活跃告警
// 活跃告警会被更新(如忽略状态),因此保留 gorm.Model 以包含所有标准字段。
type ActiveAlarm struct {
gorm.Model
SourceType AlarmSourceType `gorm:"type:varchar(50);not null;index;comment:告警来源类型" json:"source_type"`
SourceType AlarmSourceType `gorm:"type:varchar(50);not null;index:idx_alarm_uniqueness;comment:告警来源类型" json:"source_type"`
// SourceID 告警来源ID其具体含义取决于 SourceType 字段 (例如设备ID, 区域主控ID, 猪栏ID)。
SourceID uint `gorm:"not null;index;comment:告警来源ID" json:"source_id"`
SourceID uint `gorm:"not null;index:idx_alarm_uniqueness;comment:告警来源ID" json:"source_id"`
// AlarmCode 是一个机器可读的、标准化的告警类型标识。
// 它与 SourceType 和 SourceID 共同构成一个活跃告警的唯一标识。
AlarmCode AlarmCode `gorm:"type:varchar(100);not null;index:idx_alarm_uniqueness;comment:告警代码" json:"alarm_code"`
AlarmSummary string `gorm:"comment:告警简述" json:"alarm_summary"`
Level SeverityLevel `gorm:"type:varchar(10);not null;comment:严重性等级" json:"level"`
Level SeverityLevel `gorm:"type:varchar(10);not null;index:idx_notification_query;comment:严重性等级" json:"level"`
AlarmDetails string `gorm:"comment:告警详细内容" json:"alarm_details"`
TriggerTime time.Time `gorm:"not null;comment:告警触发时间" json:"trigger_time"`
// IsIgnored 是否被手动忽略 (Snooze)
IsIgnored bool `gorm:"default:false;comment:是否被手动忽略" json:"is_ignored"`
// IsIgnored 字段加入到专为通知查询优化的复合索引中
IsIgnored bool `gorm:"default:false;index:idx_notification_query;comment:是否被手动忽略" json:"is_ignored"`
// IgnoredUntil 忽略截止时间。在此时间之前,即使告警持续,也不会发送通知。
// 使用指针类型 *time.Time 来表示可为空的时间。
IgnoredUntil *time.Time `gorm:"comment:忽略截止时间" json:"ignored_until"`
// LastNotifiedAt 上次发送通知的时间。用于控制重复通知的频率。
LastNotifiedAt *time.Time `gorm:"comment:上次发送通知时间" json:"last_notified_at"`
// LastNotifiedAt 字段加入到专为通知查询优化的复合索引中
LastNotifiedAt *time.Time `gorm:"index:idx_notification_query;comment:上次发送通知时间" json:"last_notified_at"`
}
// TableName 指定 ActiveAlarm 结构体对应的数据库表名
@@ -55,6 +78,9 @@ type HistoricalAlarm struct {
// SourceID 告警来源ID其具体含义取决于 SourceType 字段 (例如设备ID, 区域主控ID, 猪栏ID)。
SourceID uint `gorm:"not null;index;comment:告警来源ID" json:"source_id"`
// AlarmCode 是一个机器可读的、标准化的告警类型标识。
AlarmCode AlarmCode `gorm:"type:varchar(100);not null;index;comment:告警代码" json:"alarm_code"`
AlarmSummary string `gorm:"comment:告警简述" json:"alarm_summary"`
Level SeverityLevel `gorm:"type:varchar(10);not null;comment:严重性等级" json:"level"`
AlarmDetails string `gorm:"comment:告警详细内容" json:"alarm_details"`

View File

@@ -1,6 +1,8 @@
package models
import (
"encoding/json"
"errors"
"time"
"gorm.io/datatypes"
@@ -68,3 +70,12 @@ type SensorData struct {
func (SensorData) TableName() string {
return "sensor_data"
}
// ParseData 解析 JSON 数据到一个具体的结构体中。
// 调用方需要传入一个指向目标结构体实例的指针。
func (s *SensorData) ParseData(v interface{}) error {
if s.Data == nil {
return errors.New("传感器数据为空,无法解析")
}
return json.Unmarshal(s.Data, v)
}

View File

@@ -25,6 +25,21 @@ type ActiveAlarmListOptions struct {
// AlarmRepository 定义了对告警模型的数据库操作接口
type AlarmRepository interface {
// CreateActiveAlarm 创建一条新的活跃告警记录
CreateActiveAlarm(ctx context.Context, alarm *models.ActiveAlarm) error
// IsAlarmActiveInUse 检查具有相同来源和告警代码的告警当前是否处于活跃表中
IsAlarmActiveInUse(ctx context.Context, sourceType models.AlarmSourceType, sourceID uint, alarmCode models.AlarmCode) (bool, error)
// GetActiveAlarmByUniqueFieldsTx 在指定事务中根据唯一业务键获取一个活跃告警
GetActiveAlarmByUniqueFieldsTx(ctx context.Context, tx *gorm.DB, sourceType models.AlarmSourceType, sourceID uint, alarmCode models.AlarmCode) (*models.ActiveAlarm, error)
// CreateHistoricalAlarmTx 在指定事务中创建一条历史告警记录
CreateHistoricalAlarmTx(ctx context.Context, tx *gorm.DB, alarm *models.HistoricalAlarm) error
// DeleteActiveAlarmTx 在指定事务中根据主键 ID 删除一个活跃告警
DeleteActiveAlarmTx(ctx context.Context, tx *gorm.DB, id uint) error
// ListActiveAlarms 支持分页和过滤的活跃告警列表查询。
// 返回活跃告警列表、总记录数和错误。
ListActiveAlarms(ctx context.Context, opts ActiveAlarmListOptions, page, pageSize int) ([]models.ActiveAlarm, int64, error)
@@ -59,6 +74,48 @@ func NewGormAlarmRepository(ctx context.Context, db *gorm.DB) AlarmRepository {
}
}
// CreateActiveAlarm 创建一条新的活跃告警记录
func (r *gormAlarmRepository) CreateActiveAlarm(ctx context.Context, alarm *models.ActiveAlarm) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateActiveAlarm")
return r.db.WithContext(repoCtx).Create(alarm).Error
}
// IsAlarmActive 检查具有相同来源和告警代码的告警当前是否处于活跃状态
func (r *gormAlarmRepository) IsAlarmActiveInUse(ctx context.Context, sourceType models.AlarmSourceType, sourceID uint, alarmCode models.AlarmCode) (bool, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "IsAlarmActiveInUse")
var count int64
err := r.db.WithContext(repoCtx).Model(&models.ActiveAlarm{}).
Where("source_type = ? AND source_id = ? AND alarm_code = ?", sourceType, sourceID, alarmCode).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
// GetActiveAlarmByUniqueFieldsTx 在指定事务中根据唯一业务键获取一个活跃告警
func (r *gormAlarmRepository) GetActiveAlarmByUniqueFieldsTx(ctx context.Context, tx *gorm.DB, sourceType models.AlarmSourceType, sourceID uint, alarmCode models.AlarmCode) (*models.ActiveAlarm, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "GetActiveAlarmByUniqueFieldsTx")
var alarm models.ActiveAlarm
err := tx.WithContext(repoCtx).
Where("source_type = ? AND source_id = ? AND alarm_code = ?", sourceType, sourceID, alarmCode).
First(&alarm).Error
return &alarm, err
}
// CreateHistoricalAlarmTx 在指定事务中创建一条历史告警记录
func (r *gormAlarmRepository) CreateHistoricalAlarmTx(ctx context.Context, tx *gorm.DB, alarm *models.HistoricalAlarm) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateHistoricalAlarmTx")
return tx.WithContext(repoCtx).Create(alarm).Error
}
// DeleteActiveAlarmTx 在指定事务中根据主键 ID 删除一个活跃告警
func (r *gormAlarmRepository) DeleteActiveAlarmTx(ctx context.Context, tx *gorm.DB, id uint) error {
repoCtx := logs.AddFuncName(ctx, r.ctx, "DeleteActiveAlarmTx")
// 使用 Unscoped() 确保执行物理删除,而不是软删除
return tx.WithContext(repoCtx).Unscoped().Delete(&models.ActiveAlarm{}, id).Error
}
// ListActiveAlarms 实现了分页和过滤查询活跃告警记录的功能
func (r *gormAlarmRepository) ListActiveAlarms(ctx context.Context, opts ActiveAlarmListOptions, page, pageSize int) ([]models.ActiveAlarm, int64, error) {
repoCtx := logs.AddFuncName(ctx, r.ctx, "ListActiveAlarms")