Files
IOT_API/Service/Implement/Inspection/AlertCenterService.cs
T

116 lines
5.2 KiB
C#

using Model;
using Model.Dto.Inspection;
using Model.Entity.Inspection;
using Model.Mapper;
using ORM;
using Service.Interface;
using SqlSugar;
using System;
using System.Threading.Tasks;
namespace Service.Implement
{
/// <summary>
/// 告警中心服务实现(全系统统一告警上报入口)
/// 职责:默认内容生成 → 去重合并(防告警风暴)→ 落库 → 规则触发计数 → 通知队列入队
/// </summary>
public class AlertCenterService : IAlertCenterService
{
/// <summary>
/// 合并窗口(分钟):窗口内同设备+同测点+同类型的活跃告警不新建记录,只累加触发次数。
/// 后续可迁移到系统参数配置模块动态调整
/// </summary>
private const int MergeWindowMinutes = 5;
/// <summary>
/// 上报一条告警
/// </summary>
public async Task<Result<string>> RaiseAsync(AlertRaiseDto dto)
{
if (dto == null)
{
return Result<string>.Error("告警上报参数为空");
}
if (string.IsNullOrWhiteSpace(dto.DeviceCode) && string.IsNullOrWhiteSpace(dto.Content))
{
return Result<string>.Error("设备编号与告警内容不能同时为空");
}
try
{
var now = DateTime.Now;
// ===== 去重合并:窗口内相同 设备+测点+类型 的活跃告警(未确认/已确认)只累加不新建 =====
var active = await SqlSugarContext.DbContext.Queryable<AlertMessageEntity>()
.Where(x => x.IsDel == 0)
.Where(x => x.DeviceCode == dto.DeviceCode && x.Metric == dto.Metric && x.AlertType == dto.AlertType)
.Where(x => x.AlertStatus == AlertStatusEnum.Unacknowledged || x.AlertStatus == AlertStatusEnum.Acknowledged)
.Where(x => x.LastAlertTime >= now.AddMinutes(-MergeWindowMinutes))
.FirstAsync();
if (active != null)
{
// 累加触发次数、刷新最近告警时间与当前值;级别取更高者(告警升级)
var mergedLevel = active.AlertLevel > dto.AlertLevel ? active.AlertLevel : dto.AlertLevel;
await SqlSugarContext.DbContext.Updateable<AlertMessageEntity>()
.SetColumns(x => new AlertMessageEntity
{
TriggerCount = x.TriggerCount + 1,
LastAlertTime = now,
CurrentValue = dto.CurrentValue ?? x.CurrentValue,
AlertLevel = mergedLevel
})
.Where(x => x.Id == active.Id)
.ExecuteCommandAsync();
// 合并告警也入队(是否真正推送由 Worker 按通知规则静默期决定)
AlertNotifyBus.Publish(active.Id, false);
return Result<string>.Success(active.Id.ToString());
}
// ===== 新告警:生成默认内容 → 落库 → 规则计数 → 入队 =====
var entity = dto.ToEntity();
if (string.IsNullOrWhiteSpace(entity.Content))
{
entity.Content = BuildDefaultContent(entity);
}
entity.CreateTime = now;
entity.FirstAlertTime = now;
entity.LastAlertTime = now;
entity.TriggerCount = 1;
entity.AlertStatus = AlertStatusEnum.Unacknowledged;
await SqlSugarContext.DbContext.Insertable(entity).ExecuteCommandAsync();
// 来源为规则引擎时,回写规则触发统计(与黄的规则引擎模块联动)
if (entity.RuleId > 0)
{
await SqlSugarContext.DbContext.Updateable<AlertRuleEntity>()
.SetColumns(x => new AlertRuleEntity { TriggerCount = x.TriggerCount + 1, LastAlarmTime = now })
.Where(x => x.Id == entity.RuleId)
.ExecuteCommandAsync();
}
AlertNotifyBus.Publish(entity.Id, true);
return Result<string>.Success(entity.Id.ToString());
}
catch (Exception ex)
{
return Result<string>.Error("告警上报失败", ex);
}
}
/// <summary>
/// 按内置模板生成默认告警内容(后续可由通知模板模块接管,见 TemplateCode 预留字段)
/// </summary>
private static string BuildDefaultContent(AlertMessageEntity e)
{
string device = string.IsNullOrWhiteSpace(e.DeviceName) ? e.DeviceCode ?? "未知设备" : $"{e.DeviceName}({e.DeviceCode})";
string metric = string.IsNullOrWhiteSpace(e.Metric) ? "" : $" 测点[{e.Metric}]";
string value = e.CurrentValue.HasValue ? $" 当前值 {e.CurrentValue.Value}" : "";
string threshold = e.ThresholdValue.HasValue ? $",阈值 {e.ThresholdValue.Value}" : "";
return $"{device}{metric} 触发告警:{value}{threshold}";
}
}
}