Files
v6ole b2c20ec43d feat(alerts): 添加微信告警、WebSocket实时推送和地图功能
- 新增微信告警服务(wechat_service)和告警任务(alert_tasks)
- 新增 WebSocket 实时推送端点
- 新增监控管理模块(monitor)
- 增强统计仪表板:趋势图、区域分布、光功率历史
- 设备管理:添加坐标信息、标签系统、复合索引优化
- 前端:重构Dashboard/Charts页面,新增业务组件
- 新增5个数据库迁移(坐标、复合索引、标签、光功率历史、显示名)
- 更新部署配置和脚本
- 新增测试框架基础结构
2026-06-11 15:16:29 +08:00

83 lines
2.9 KiB
Python
Raw Permalink 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 任务"""
import traceback
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": str(e), "traceback": traceback.format_exc()}
finally:
db.close()