Files
H3ConuMS-v2/backend/app/tasks/alert_tasks.py
T

82 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""告警相关 Celery 任务"""
from datetime import datetime
from sqlalchemy import func, case
from app.core.celery_app import celery_app
from app.core.database import SessionLocal
@celery_app.task(queue='h3c_onu_ms', ignore_result=True)
def check_school_offline_alerts():
"""
检查是否有学校全部离线(在线率为 0%),如有则发送企业微信告警。
该任务在每次全量状态检查完成后异步调用。
"""
from app.models.device import ONUDevice, DeviceStatusHistory
from app.services.wechat_service import send_wechat_markdown
db = SessionLocal()
try:
# 每台设备最新状态的子查询
latest_subq = (
db.query(
DeviceStatusHistory.onu_device_id,
func.max(DeviceStatusHistory.checked_at).label("max_checked_at")
)
.group_by(DeviceStatusHistory.onu_device_id)
.subquery()
)
latest_status_subq = (
db.query(
DeviceStatusHistory.onu_device_id,
DeviceStatusHistory.status
)
.join(
latest_subq,
(DeviceStatusHistory.onu_device_id == latest_subq.c.onu_device_id) &
(DeviceStatusHistory.checked_at == latest_subq.c.max_checked_at)
)
.subquery()
)
# 按学校聚合在线率,只查询在线数为 0 的学校
rows = (
db.query(
ONUDevice.school_name,
ONUDevice.region,
func.count().label("total"),
func.sum(case((latest_status_subq.c.status == 'online', 1), else_=0)).label("online"),
)
.outerjoin(latest_status_subq, ONUDevice.id == latest_status_subq.c.onu_device_id)
.group_by(ONUDevice.school_name, ONUDevice.region)
.having(
func.sum(case((latest_status_subq.c.status == 'online', 1), else_=0)) == 0
)
.all()
)
if not rows:
return {"alerted": False, "reason": "没有全离线的学校"}
# 构造告警消息
now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines = [
"## <font color=\"warning\">[告警] 学校全部离线</font>",
f"> 检查时间:{now_str}",
"> 以下学校所有设备均处于离线状态:",
"",
]
for row in rows:
school = row.school_name or "未知"
region = row.region or "未知"
total = int(row.total or 0)
if total > 0:
lines.append(f"- **{school}**{region}: {total} 台设备全离线")
content = "\n".join(lines)
send_wechat_markdown(content)
return {"alerted": True, "schools": len(rows)}
except Exception as e:
return {"alerted": False, "error": "告警任务失败", "error_type": type(e).__name__}
finally:
db.close()