PingWatch 网络设备离线监控系统

- FastAPI 后端 + Vue 3 前端
- Docker Compose 一键部署
- Casdoor OAuth 认证集成
- LogHive 集中式日志
- 设备批量 CSV 导入/导出
- WebSocket 实时状态推送
- 企业微信告警通知
- fping 高性能并发 Ping 检测

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-09 15:02:04 +08:00
commit 848f804169
55 changed files with 5941 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
"""告警记录 API"""
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.deps import get_db
from app.core.auth import get_current_user
from app.models.device import Device
from app.models.alert_event import AlertEvent, AlertTypeEnum
from app.models.user import User
from app.schemas.alert import AlertEventOut
router = APIRouter(prefix="/api/alerts", tags=["告警记录"])
@router.get("", response_model=dict)
async def list_alerts(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
device_id: Optional[int] = None,
alert_type: Optional[str] = None,
is_resolved: Optional[bool] = None,
start_time: Optional[datetime] = None,
end_time: Optional[datetime] = None,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""查询告警记录,支持分页和过滤"""
query = select(AlertEvent)
count_query = select(func.count(AlertEvent.id))
# 过滤条件
if device_id:
query = query.where(AlertEvent.device_id == device_id)
count_query = count_query.where(AlertEvent.device_id == device_id)
if alert_type:
query = query.where(AlertEvent.alert_type == alert_type)
count_query = count_query.where(AlertEvent.alert_type == alert_type)
if is_resolved is not None:
query = query.where(AlertEvent.is_resolved == is_resolved)
count_query = count_query.where(AlertEvent.is_resolved == is_resolved)
if start_time:
query = query.where(AlertEvent.created_at >= start_time)
count_query = count_query.where(AlertEvent.created_at >= start_time)
if end_time:
query = query.where(AlertEvent.created_at <= end_time)
count_query = count_query.where(AlertEvent.created_at <= end_time)
# 总数
total_result = await db.execute(count_query)
total = total_result.scalar() or 0
# 分页
offset = (page - 1) * page_size
query = query.order_by(AlertEvent.created_at.desc()).offset(offset).limit(page_size)
result = await db.execute(query)
events = list(result.scalars().all())
# 关联设备信息
device_ids = {e.device_id for e in events if e.device_id > 0}
devices_map = {}
if device_ids:
dev_result = await db.execute(select(Device).where(Device.id.in_(device_ids)))
devices_map = {d.id: d for d in dev_result.scalars().all()}
items = []
for e in events:
dev = devices_map.get(e.device_id)
items.append(AlertEventOut(
id=e.id,
device_id=e.device_id,
device_name=dev.name if dev else "系统",
device_ip=dev.ip if dev else "",
device_type=dev.device_type.value if dev else "",
location=dev.location if dev else "",
project_name=dev.project_name if dev else "",
alert_type=e.alert_type.value,
message=e.message,
start_at=e.start_at,
end_at=e.end_at,
duration_minutes=e.duration_minutes,
is_resolved=e.is_resolved,
notification_sent=e.notification_sent,
created_at=e.created_at,
))
return {"items": items, "total": total, "page": page, "page_size": page_size}
@router.get("/latest", response_model=list[AlertEventOut])
async def get_latest_alerts(
limit: int = Query(10, ge=1, le=50),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取最近的告警"""
result = await db.execute(
select(AlertEvent)
.order_by(AlertEvent.created_at.desc())
.limit(limit)
)
events = list(result.scalars().all())
device_ids = {e.device_id for e in events if e.device_id > 0}
devices_map = {}
if device_ids:
dev_result = await db.execute(select(Device).where(Device.id.in_(device_ids)))
devices_map = {d.id: d for d in dev_result.scalars().all()}
items = []
for e in events:
dev = devices_map.get(e.device_id)
items.append(AlertEventOut(
id=e.id,
device_id=e.device_id,
device_name=dev.name if dev else "系统",
device_ip=dev.ip if dev else "",
device_type=dev.device_type.value if dev else "",
location=dev.location if dev else "",
project_name=dev.project_name if dev else "",
alert_type=e.alert_type.value,
message=e.message,
start_at=e.start_at,
end_at=e.end_at,
duration_minutes=e.duration_minutes,
is_resolved=e.is_resolved,
notification_sent=e.notification_sent,
created_at=e.created_at,
))
return items