|
|
|
|
|
# backend/app/api/v2/alert.py
|
|
|
|
|
|
"""
|
|
|
|
|
|
告警 API 接口
|
|
|
|
|
|
支持告警规则的 CRUD 操作
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from typing import Optional, List
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
from app.db.base import get_db
|
|
|
|
|
|
from app.middleware.auth import get_current_user
|
|
|
|
|
|
from app.models.user import User
|
|
|
|
|
|
from app.models.alert import (
|
|
|
|
|
|
AlertRule, AlertHistory,
|
|
|
|
|
|
AlertRuleCreate, AlertRuleUpdate, AlertRuleResponse,
|
|
|
|
|
|
AlertHistoryResponse, AlertListResponse, AlertHistoryListResponse,
|
|
|
|
|
|
AlertType, AlertOperator, NotifyChannel
|
|
|
|
|
|
)
|
|
|
|
|
|
from app.services.alert_engine import alert_engine
|
|
|
|
|
|
from app.services.alert_notification import alert_notification
|
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/api/v2/alert", tags=["告警服务"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/rules", response_model=AlertRuleResponse, summary="创建告警规则")
|
|
|
|
|
|
async def create_alert_rule(
|
|
|
|
|
|
rule_data: AlertRuleCreate,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
创建告警规则
|
|
|
|
|
|
|
|
|
|
|
|
- **name**: 告警名称
|
|
|
|
|
|
- **symbol**: 品种代码(如 IF2406)
|
|
|
|
|
|
- **type**: 告警类型(price/change_percent/technical/volume)
|
|
|
|
|
|
- **condition**: 触发条件(JSON 结构)
|
|
|
|
|
|
- **channels**: 通知渠道列表
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 创建规则
|
|
|
|
|
|
rule = AlertRule(
|
|
|
|
|
|
user_id=current_user.id,
|
|
|
|
|
|
name=rule_data.name,
|
|
|
|
|
|
symbol=rule_data.symbol,
|
|
|
|
|
|
type=rule_data.type.value if hasattr(rule_data.type, 'value') else rule_data.type,
|
|
|
|
|
|
condition=rule_data.condition.model_dump(),
|
|
|
|
|
|
channels=[c.value if hasattr(c, 'value') else c for c in rule_data.channels],
|
|
|
|
|
|
enabled=rule_data.enabled,
|
|
|
|
|
|
start_time=rule_data.start_time,
|
|
|
|
|
|
end_time=rule_data.end_time,
|
|
|
|
|
|
repeat_interval=rule_data.repeat_interval
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
db.add(rule)
|
|
|
|
|
|
db.commit()
|
|
|
|
|
|
db.refresh(rule)
|
|
|
|
|
|
|
|
|
|
|
|
# 加载到缓存
|
|
|
|
|
|
await alert_engine.load_user_rules(db, current_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
logger.info(f"✅ 用户 {current_user.id} 创建告警规则 {rule.id}: {rule.name}")
|
|
|
|
|
|
|
|
|
|
|
|
return AlertRuleResponse(
|
|
|
|
|
|
id=rule.id,
|
|
|
|
|
|
user_id=rule.user_id,
|
|
|
|
|
|
name=rule.name,
|
|
|
|
|
|
symbol=rule.symbol,
|
|
|
|
|
|
type=rule.type,
|
|
|
|
|
|
condition=rule.condition,
|
|
|
|
|
|
channels=rule.channels,
|
|
|
|
|
|
enabled=rule.enabled,
|
|
|
|
|
|
start_time=str(rule.start_time) if rule.start_time else None,
|
|
|
|
|
|
end_time=str(rule.end_time) if rule.end_time else None,
|
|
|
|
|
|
repeat_interval=rule.repeat_interval,
|
|
|
|
|
|
last_triggered_at=rule.last_triggered_at,
|
|
|
|
|
|
trigger_count=rule.trigger_count,
|
|
|
|
|
|
created_at=rule.created_at,
|
|
|
|
|
|
updated_at=rule.updated_at
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 创建告警规则失败: {e}")
|
|
|
|
|
|
db.rollback()
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"创建告警规则失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/rules", response_model=AlertListResponse, summary="查询告警规则列表")
|
|
|
|
|
|
async def get_alert_rules(
|
|
|
|
|
|
page: int = Query(1, ge=1, description="页码"),
|
|
|
|
|
|
page_size: int = Query(20, ge=1, le=100, description="每页数量"),
|
|
|
|
|
|
symbol: Optional[str] = Query(None, description="品种代码筛选"),
|
|
|
|
|
|
type: Optional[AlertType] = Query(None, description="告警类型筛选"),
|
|
|
|
|
|
enabled: Optional[bool] = Query(None, description="是否启用筛选"),
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
查询告警规则列表
|
|
|
|
|
|
|
|
|
|
|
|
支持按品种、类型、状态筛选
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 构造查询
|
|
|
|
|
|
query = db.query(AlertRule).filter(AlertRule.user_id == current_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
if symbol:
|
|
|
|
|
|
query = query.filter(AlertRule.symbol == symbol)
|
|
|
|
|
|
|
|
|
|
|
|
if type:
|
|
|
|
|
|
query = query.filter(AlertRule.type == type.value)
|
|
|
|
|
|
|
|
|
|
|
|
if enabled is not None:
|
|
|
|
|
|
query = query.filter(AlertRule.enabled == enabled)
|
|
|
|
|
|
|
|
|
|
|
|
# 总数
|
|
|
|
|
|
total = query.count()
|
|
|
|
|
|
|
|
|
|
|
|
# 分页
|
|
|
|
|
|
rules = query.order_by(AlertRule.created_at.desc()).offset((page - 1) * page_size).limit(page_size).all()
|
|
|
|
|
|
|
|
|
|
|
|
# 构造响应
|
|
|
|
|
|
items = [
|
|
|
|
|
|
AlertRuleResponse(
|
|
|
|
|
|
id=rule.id,
|
|
|
|
|
|
user_id=rule.user_id,
|
|
|
|
|
|
name=rule.name,
|
|
|
|
|
|
symbol=rule.symbol,
|
|
|
|
|
|
type=rule.type,
|
|
|
|
|
|
condition=rule.condition,
|
|
|
|
|
|
channels=rule.channels,
|
|
|
|
|
|
enabled=rule.enabled,
|
|
|
|
|
|
start_time=str(rule.start_time) if rule.start_time else None,
|
|
|
|
|
|
end_time=str(rule.end_time) if rule.end_time else None,
|
|
|
|
|
|
repeat_interval=rule.repeat_interval,
|
|
|
|
|
|
last_triggered_at=rule.last_triggered_at,
|
|
|
|
|
|
trigger_count=rule.trigger_count,
|
|
|
|
|
|
created_at=rule.created_at,
|
|
|
|
|
|
updated_at=rule.updated_at
|
|
|
|
|
|
)
|
|
|
|
|
|
for rule in rules
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
return AlertListResponse(
|
|
|
|
|
|
total=total,
|
|
|
|
|
|
page=page,
|
|
|
|
|
|
page_size=page_size,
|
|
|
|
|
|
items=items
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 查询告警规则列表失败: {e}")
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"查询告警规则列表失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/rules/{rule_id}", response_model=AlertRuleResponse, summary="查询告警规则详情")
|
|
|
|
|
|
async def get_alert_rule(
|
|
|
|
|
|
rule_id: int,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
查询告警规则详情
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
rule = db.query(AlertRule).filter(
|
|
|
|
|
|
AlertRule.id == rule_id,
|
|
|
|
|
|
AlertRule.user_id == current_user.id
|
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
|
|
if not rule:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail="告警规则不存在")
|
|
|
|
|
|
|
|
|
|
|
|
return AlertRuleResponse(
|
|
|
|
|
|
id=rule.id,
|
|
|
|
|
|
user_id=rule.user_id,
|
|
|
|
|
|
name=rule.name,
|
|
|
|
|
|
symbol=rule.symbol,
|
|
|
|
|
|
type=rule.type,
|
|
|
|
|
|
condition=rule.condition,
|
|
|
|
|
|
channels=rule.channels,
|
|
|
|
|
|
enabled=rule.enabled,
|
|
|
|
|
|
start_time=str(rule.start_time) if rule.start_time else None,
|
|
|
|
|
|
end_time=str(rule.end_time) if rule.end_time else None,
|
|
|
|
|
|
repeat_interval=rule.repeat_interval,
|
|
|
|
|
|
last_triggered_at=rule.last_triggered_at,
|
|
|
|
|
|
trigger_count=rule.trigger_count,
|
|
|
|
|
|
created_at=rule.created_at,
|
|
|
|
|
|
updated_at=rule.updated_at
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 查询告警规则详情失败: {e}")
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"查询告警规则详情失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.put("/rules/{rule_id}", response_model=AlertRuleResponse, summary="更新告警规则")
|
|
|
|
|
|
async def update_alert_rule(
|
|
|
|
|
|
rule_id: int,
|
|
|
|
|
|
rule_data: AlertRuleUpdate,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
更新告警规则
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
rule = db.query(AlertRule).filter(
|
|
|
|
|
|
AlertRule.id == rule_id,
|
|
|
|
|
|
AlertRule.user_id == current_user.id
|
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
|
|
if not rule:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail="告警规则不存在")
|
|
|
|
|
|
|
|
|
|
|
|
# 更新字段
|
|
|
|
|
|
if rule_data.name is not None:
|
|
|
|
|
|
rule.name = rule_data.name
|
|
|
|
|
|
|
|
|
|
|
|
if rule_data.symbol is not None:
|
|
|
|
|
|
rule.symbol = rule_data.symbol
|
|
|
|
|
|
|
|
|
|
|
|
if rule_data.type is not None:
|
|
|
|
|
|
rule.type = rule_data.type.value if hasattr(rule_data.type, 'value') else rule_data.type
|
|
|
|
|
|
|
|
|
|
|
|
if rule_data.condition is not None:
|
|
|
|
|
|
rule.condition = rule_data.condition.model_dump()
|
|
|
|
|
|
|
|
|
|
|
|
if rule_data.channels is not None:
|
|
|
|
|
|
rule.channels = [c.value if hasattr(c, 'value') else c for c in rule_data.channels]
|
|
|
|
|
|
|
|
|
|
|
|
if rule_data.enabled is not None:
|
|
|
|
|
|
rule.enabled = rule_data.enabled
|
|
|
|
|
|
|
|
|
|
|
|
if rule_data.start_time is not None:
|
|
|
|
|
|
rule.start_time = rule_data.start_time
|
|
|
|
|
|
|
|
|
|
|
|
if rule_data.end_time is not None:
|
|
|
|
|
|
rule.end_time = rule_data.end_time
|
|
|
|
|
|
|
|
|
|
|
|
if rule_data.repeat_interval is not None:
|
|
|
|
|
|
rule.repeat_interval = rule_data.repeat_interval
|
|
|
|
|
|
|
|
|
|
|
|
rule.updated_at = datetime.now()
|
|
|
|
|
|
|
|
|
|
|
|
db.commit()
|
|
|
|
|
|
db.refresh(rule)
|
|
|
|
|
|
|
|
|
|
|
|
# 更新缓存
|
|
|
|
|
|
await alert_engine.load_user_rules(db, current_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
logger.info(f"✅ 用户 {current_user.id} 更新告警规则 {rule_id}")
|
|
|
|
|
|
|
|
|
|
|
|
return AlertRuleResponse(
|
|
|
|
|
|
id=rule.id,
|
|
|
|
|
|
user_id=rule.user_id,
|
|
|
|
|
|
name=rule.name,
|
|
|
|
|
|
symbol=rule.symbol,
|
|
|
|
|
|
type=rule.type,
|
|
|
|
|
|
condition=rule.condition,
|
|
|
|
|
|
channels=rule.channels,
|
|
|
|
|
|
enabled=rule.enabled,
|
|
|
|
|
|
start_time=str(rule.start_time) if rule.start_time else None,
|
|
|
|
|
|
end_time=str(rule.end_time) if rule.end_time else None,
|
|
|
|
|
|
repeat_interval=rule.repeat_interval,
|
|
|
|
|
|
last_triggered_at=rule.last_triggered_at,
|
|
|
|
|
|
trigger_count=rule.trigger_count,
|
|
|
|
|
|
created_at=rule.created_at,
|
|
|
|
|
|
updated_at=rule.updated_at
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 更新告警规则失败: {e}")
|
|
|
|
|
|
db.rollback()
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"更新告警规则失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete("/rules/{rule_id}", summary="删除告警规则")
|
|
|
|
|
|
async def delete_alert_rule(
|
|
|
|
|
|
rule_id: int,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
删除告警规则
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
rule = db.query(AlertRule).filter(
|
|
|
|
|
|
AlertRule.id == rule_id,
|
|
|
|
|
|
AlertRule.user_id == current_user.id
|
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
|
|
if not rule:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail="告警规则不存在")
|
|
|
|
|
|
|
|
|
|
|
|
db.delete(rule)
|
|
|
|
|
|
db.commit()
|
|
|
|
|
|
|
|
|
|
|
|
# 更新缓存
|
|
|
|
|
|
await alert_engine.load_user_rules(db, current_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
logger.info(f"✅ 用户 {current_user.id} 删除告警规则 {rule_id}")
|
|
|
|
|
|
|
|
|
|
|
|
return {"status": "success", "message": "告警规则已删除"}
|
|
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 删除告警规则失败: {e}")
|
|
|
|
|
|
db.rollback()
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"删除告警规则失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/rules/{rule_id}/enable", summary="启用告警规则")
|
|
|
|
|
|
async def enable_alert_rule(
|
|
|
|
|
|
rule_id: int,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
启用告警规则
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
rule = db.query(AlertRule).filter(
|
|
|
|
|
|
AlertRule.id == rule_id,
|
|
|
|
|
|
AlertRule.user_id == current_user.id
|
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
|
|
if not rule:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail="告警规则不存在")
|
|
|
|
|
|
|
|
|
|
|
|
rule.enabled = True
|
|
|
|
|
|
rule.updated_at = datetime.now()
|
|
|
|
|
|
|
|
|
|
|
|
db.commit()
|
|
|
|
|
|
|
|
|
|
|
|
# 更新缓存
|
|
|
|
|
|
await alert_engine.load_user_rules(db, current_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
logger.info(f"✅ 用户 {current_user.id} 启用告警规则 {rule_id}")
|
|
|
|
|
|
|
|
|
|
|
|
return {"status": "success", "message": "告警规则已启用"}
|
|
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 启用告警规则失败: {e}")
|
|
|
|
|
|
db.rollback()
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"启用告警规则失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/rules/{rule_id}/disable", summary="禁用告警规则")
|
|
|
|
|
|
async def disable_alert_rule(
|
|
|
|
|
|
rule_id: int,
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
禁用告警规则
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
rule = db.query(AlertRule).filter(
|
|
|
|
|
|
AlertRule.id == rule_id,
|
|
|
|
|
|
AlertRule.user_id == current_user.id
|
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
|
|
if not rule:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail="告警规则不存在")
|
|
|
|
|
|
|
|
|
|
|
|
rule.enabled = False
|
|
|
|
|
|
rule.updated_at = datetime.now()
|
|
|
|
|
|
|
|
|
|
|
|
db.commit()
|
|
|
|
|
|
|
|
|
|
|
|
# 更新缓存
|
|
|
|
|
|
await alert_engine.load_user_rules(db, current_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
logger.info(f"✅ 用户 {current_user.id} 禁用告警规则 {rule_id}")
|
|
|
|
|
|
|
|
|
|
|
|
return {"status": "success", "message": "告警规则已禁用"}
|
|
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 禁用告警规则失败: {e}")
|
|
|
|
|
|
db.rollback()
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"禁用告警规则失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/history", response_model=AlertHistoryListResponse, summary="查询告警历史")
|
|
|
|
|
|
async def get_alert_history(
|
|
|
|
|
|
page: int = Query(1, ge=1, description="页码"),
|
|
|
|
|
|
page_size: int = Query(20, ge=1, le=100, description="每页数量"),
|
|
|
|
|
|
rule_id: Optional[int] = Query(None, description="规则 ID 筛选"),
|
|
|
|
|
|
symbol: Optional[str] = Query(None, description="品种代码筛选"),
|
|
|
|
|
|
start_date: Optional[datetime] = Query(None, description="开始日期"),
|
|
|
|
|
|
end_date: Optional[datetime] = Query(None, description="结束日期"),
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
查询告警历史
|
|
|
|
|
|
|
|
|
|
|
|
支持按规则、品种、时间范围筛选
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 构造查询
|
|
|
|
|
|
query = db.query(AlertHistory).filter(AlertHistory.user_id == current_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
if rule_id:
|
|
|
|
|
|
query = query.filter(AlertHistory.rule_id == rule_id)
|
|
|
|
|
|
|
|
|
|
|
|
if symbol:
|
|
|
|
|
|
query = query.filter(AlertHistory.symbol == symbol)
|
|
|
|
|
|
|
|
|
|
|
|
if start_date:
|
|
|
|
|
|
query = query.filter(AlertHistory.trigger_time >= start_date)
|
|
|
|
|
|
|
|
|
|
|
|
if end_date:
|
|
|
|
|
|
query = query.filter(AlertHistory.trigger_time <= end_date)
|
|
|
|
|
|
|
|
|
|
|
|
# 总数
|
|
|
|
|
|
total = query.count()
|
|
|
|
|
|
|
|
|
|
|
|
# 分页
|
|
|
|
|
|
histories = query.order_by(AlertHistory.trigger_time.desc()).offset((page - 1) * page_size).limit(page_size).all()
|
|
|
|
|
|
|
|
|
|
|
|
# 构造响应
|
|
|
|
|
|
items = [
|
|
|
|
|
|
AlertHistoryResponse(
|
|
|
|
|
|
id=h.id,
|
|
|
|
|
|
rule_id=h.rule_id,
|
|
|
|
|
|
user_id=h.user_id,
|
|
|
|
|
|
symbol=h.symbol,
|
|
|
|
|
|
trigger_value=float(h.trigger_value) if h.trigger_value else None,
|
|
|
|
|
|
trigger_condition=h.trigger_condition,
|
|
|
|
|
|
notified=h.notified,
|
|
|
|
|
|
notify_channels=h.notify_channels,
|
|
|
|
|
|
notify_time=h.notify_time,
|
|
|
|
|
|
trigger_time=h.trigger_time,
|
|
|
|
|
|
created_at=h.created_at
|
|
|
|
|
|
)
|
|
|
|
|
|
for h in histories
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
return AlertHistoryListResponse(
|
|
|
|
|
|
total=total,
|
|
|
|
|
|
page=page,
|
|
|
|
|
|
page_size=page_size,
|
|
|
|
|
|
items=items
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 查询告警历史失败: {e}")
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"查询告警历史失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/statistics", summary="查询告警统计")
|
|
|
|
|
|
async def get_alert_statistics(
|
|
|
|
|
|
db: Session = Depends(get_db),
|
|
|
|
|
|
current_user: User = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
查询告警统计
|
|
|
|
|
|
|
|
|
|
|
|
包括规则数量、触发次数、通知统计等
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 规则统计
|
|
|
|
|
|
total_rules = db.query(AlertRule).filter(AlertRule.user_id == current_user.id).count()
|
|
|
|
|
|
enabled_rules = db.query(AlertRule).filter(
|
|
|
|
|
|
AlertRule.user_id == current_user.id,
|
|
|
|
|
|
AlertRule.enabled == True
|
|
|
|
|
|
).count()
|
|
|
|
|
|
|
|
|
|
|
|
# 历史统计
|
|
|
|
|
|
total_triggers = db.query(AlertHistory).filter(AlertHistory.user_id == current_user.id).count()
|
|
|
|
|
|
|
|
|
|
|
|
# 今日触发
|
|
|
|
|
|
today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
|
|
|
|
today_triggers = db.query(AlertHistory).filter(
|
|
|
|
|
|
AlertHistory.user_id == current_user.id,
|
|
|
|
|
|
AlertHistory.trigger_time >= today_start
|
|
|
|
|
|
).count()
|
|
|
|
|
|
|
|
|
|
|
|
# 引擎统计
|
|
|
|
|
|
engine_stats = alert_engine.get_statistics()
|
|
|
|
|
|
|
|
|
|
|
|
# 通知统计
|
|
|
|
|
|
notification_stats = alert_notification.get_statistics()
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
"rules": {
|
|
|
|
|
|
"total": total_rules,
|
|
|
|
|
|
"enabled": enabled_rules,
|
|
|
|
|
|
"disabled": total_rules - enabled_rules
|
|
|
|
|
|
},
|
|
|
|
|
|
"history": {
|
|
|
|
|
|
"total_triggers": total_triggers,
|
|
|
|
|
|
"today_triggers": today_triggers
|
|
|
|
|
|
},
|
|
|
|
|
|
"engine": engine_stats,
|
|
|
|
|
|
"notification": notification_stats
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"❌ 查询告警统计失败: {e}")
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"查询告警统计失败: {str(e)}")
|