低空智控平台 后端go
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

200 lines
5.2 KiB

package service
import (
"errors"
"time"
"gorm.io/gorm"
"laic-backend/cache"
"laic-backend/common"
"laic-backend/logger"
"laic-backend/model"
"laic-backend/tool"
"laic-backend/vo"
)
type AlarmService struct{}
var DefaultAlarmService = &AlarmService{}
// GetPage 告警分页列表(按所属机巢过滤)
func (s *AlarmService) GetPage(userID int64, isAdmin bool, req *vo.AlarmPageReq) (*common.PageResponse[model.Alarm], *common.BusiError) {
db := common.DB.Model(&model.Alarm{}).Scopes(withDockFilter(userID, isAdmin))
if req.Status != "" {
db = db.Where("status = ?", req.Status)
}
if req.Level != "" {
db = db.Where("level = ?", req.Level)
}
if req.DeviceType != "" {
db = db.Where("device_type = ?", req.DeviceType)
}
if req.DockID != "" {
db = db.Where("dock_id = ?", req.DockID)
}
if req.Keyword != "" {
kw := "%" + req.Keyword + "%"
db = db.Where("code LIKE ? OR message_cn LIKE ?", kw, kw)
}
var total int64
if err := db.Count(&total).Error; err != nil {
logger.ERROR("统计告警失败", err)
return nil, common.ErrInternal
}
var list []model.Alarm
if err := db.Scopes(req.Paginate).Order("triggered_at DESC, id DESC").Find(&list).Error; err != nil {
logger.ERROR("查询告警列表失败", err)
return nil, common.ErrInternal
}
return common.Page(req.Pagination, total, list), nil
}
// Acknowledge 确认告警(active → acknowledged)
func (s *AlarmService) Acknowledge(userID int64, isAdmin bool, id int64) *common.BusiError {
var alarm model.Alarm
if err := common.DB.Scopes(withDockFilter(userID, isAdmin)).First(&alarm, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.ErrAlarmNotFound
}
return common.ErrInternal
}
if alarm.Status == "active" {
if err := common.DB.Model(&model.Alarm{}).Where("id = ?", id).Update("status", "acknowledged").Error; err != nil {
logger.ERROR("确认告警失败", err)
return common.ErrInternal
}
broadcast("alarm.acknowledged", map[string]any{
"id": id,
"dockId": alarm.DockID,
"code": alarm.Code,
})
}
return nil
}
// Resolve 关闭告警(active/acknowledged → resolved)
func (s *AlarmService) Resolve(userID int64, isAdmin bool, id int64) *common.BusiError {
var alarm model.Alarm
if err := common.DB.Scopes(withDockFilter(userID, isAdmin)).First(&alarm, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.ErrAlarmNotFound
}
return common.ErrInternal
}
if alarm.Status != "resolved" {
now := time.Now()
if err := common.DB.Model(&model.Alarm{}).Where("id = ?", id).
Updates(map[string]any{"status": "resolved", "resolved_at": now}).Error; err != nil {
logger.ERROR("关闭告警失败", err)
return common.ErrInternal
}
broadcast("alarm.resolved", map[string]any{
"id": id,
"dockId": alarm.DockID,
"code": alarm.Code,
"resolvedAt": now,
})
}
return nil
}
// SyncByState 根据设备最新 alarmCodes 做 diff,生成/关闭告警
// deviceType: dock / drone
func (s *AlarmService) SyncByState(dockID, deviceType string, codes []string) {
key := s.alarmKey(dockID, deviceType)
last, err := common.SetMembers(key)
if err != nil {
last = []string{}
}
lastSet := toSet(last)
newSet := toSet(codes)
// 新增告警 → active
for code := range newSet {
if !lastSet[code] {
s.createAlarm(dockID, deviceType, code)
}
}
// 移除告警 → resolved
for code := range lastSet {
if !newSet[code] {
s.resolveAlarm(dockID, code)
}
}
// 更新当前告警码集合
_ = common.Delete(key)
for _, code := range codes {
_ = common.SetAdd(key, code)
}
}
func (s *AlarmService) createAlarm(dockID, deviceType, code string) {
messageCn, level := code, "critical"
var ac model.AlarmCode
if err := common.DB.Where("code = ?", code).First(&ac).Error; err == nil {
messageCn, level = ac.MessageCn, ac.Level
}
id, err := tool.NextID()
if err != nil {
logger.ERROR("生成告警 ID 失败", err)
return
}
now := time.Now()
alarm := model.Alarm{
ID: id,
DockID: dockID,
Code: code,
DeviceType: deviceType,
MessageCn: messageCn,
Level: level,
Status: "active",
TriggeredAt: &now,
CreatedAt: now,
}
if err := common.DB.Create(&alarm).Error; err != nil {
logger.ERROR("写入告警失败", err)
return
}
broadcast("alarm.created", map[string]any{
"dockId": dockID,
"alarm": alarm,
})
}
func (s *AlarmService) resolveAlarm(dockID, code string) {
now := time.Now()
res := common.DB.Model(&model.Alarm{}).Where("dock_id = ? AND code = ? AND status = 'active'", dockID, code).
Updates(map[string]any{"status": "resolved", "resolved_at": now})
if res.Error != nil {
logger.ERROR("关闭告警失败", res.Error)
return
}
if res.RowsAffected > 0 {
broadcast("alarm.resolved", map[string]any{
"dockId": dockID,
"code": code,
"resolvedAt": now,
})
}
}
func (s *AlarmService) alarmKey(dockID, deviceType string) string {
if deviceType == "drone" {
return cache.DroneAlarmKeyOf(dockID)
}
return cache.DockAlarmKeyOf(dockID)
}
func toSet(items []string) map[string]bool {
set := make(map[string]bool, len(items))
for _, it := range items {
set[it] = true
}
return set
}