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.
191 lines
6.5 KiB
191 lines
6.5 KiB
|
2 months ago
|
# backend/app/models/alert.py
|
||
|
|
"""
|
||
|
|
告警规则模型
|
||
|
|
"""
|
||
|
|
|
||
|
|
from sqlalchemy import Column, Integer, String, Boolean, Time, TIMESTAMP, Numeric, Text, ForeignKey
|
||
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
||
|
|
from sqlalchemy.orm import relationship
|
||
|
|
from sqlalchemy.sql import func
|
||
|
|
from app.db.base import Base
|
||
|
|
from datetime import datetime, time as dt_time
|
||
|
|
from typing import Optional, List
|
||
|
|
from pydantic import BaseModel, Field
|
||
|
|
from enum import Enum
|
||
|
|
|
||
|
|
|
||
|
|
class AlertType(str, Enum):
|
||
|
|
"""告警类型"""
|
||
|
|
PRICE = "price" # 价格告警
|
||
|
|
CHANGE_PERCENT = "change_percent" # 涨跌幅告警
|
||
|
|
TECHNICAL = "technical" # 技术指标告警
|
||
|
|
VOLUME = "volume" # 成交量告警
|
||
|
|
|
||
|
|
|
||
|
|
class AlertOperator(str, Enum):
|
||
|
|
"""告警操作符"""
|
||
|
|
GT = "gt" # 大于
|
||
|
|
LT = "lt" # 小于
|
||
|
|
EQ = "eq" # 等于
|
||
|
|
GE = "ge" # 大于等于
|
||
|
|
LE = "le" # 小于等于
|
||
|
|
NE = "ne" # 不等于
|
||
|
|
|
||
|
|
|
||
|
|
class NotifyChannel(str, Enum):
|
||
|
|
"""通知渠道"""
|
||
|
|
IN_APP = "站内消息"
|
||
|
|
EMAIL = "邮件"
|
||
|
|
SMS = "短信"
|
||
|
|
WECHAT = "企业微信"
|
||
|
|
DINGTALK = "钉钉"
|
||
|
|
|
||
|
|
|
||
|
|
class AlertRule(Base):
|
||
|
|
"""告警规则表"""
|
||
|
|
__tablename__ = "alert_rule"
|
||
|
|
|
||
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
|
|
user_id = Column(Integer, nullable=False, index=True)
|
||
|
|
name = Column(String(100), nullable=False, comment="告警名称")
|
||
|
|
symbol = Column(String(20), nullable=False, index=True, comment="品种代码")
|
||
|
|
type = Column(String(20), nullable=False, index=True, comment="告警类型")
|
||
|
|
condition = Column(JSONB, nullable=False, comment="触发条件")
|
||
|
|
channels = Column(JSONB, nullable=False, comment="通知渠道")
|
||
|
|
enabled = Column(Boolean, default=True, index=True, comment="是否启用")
|
||
|
|
start_time = Column(Time, nullable=True, comment="生效开始时间")
|
||
|
|
end_time = Column(Time, nullable=True, comment="生效结束时间")
|
||
|
|
repeat_interval = Column(Integer, default=0, comment="重复间隔(秒)")
|
||
|
|
last_triggered_at = Column(TIMESTAMP(timezone=True), nullable=True, comment="上次触发时间")
|
||
|
|
trigger_count = Column(Integer, default=0, comment="触发次数")
|
||
|
|
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
|
||
|
|
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
|
||
|
|
|
||
|
|
# 关系
|
||
|
|
history = relationship("AlertHistory", back_populates="rule", cascade="all, delete-orphan")
|
||
|
|
|
||
|
|
def __repr__(self):
|
||
|
|
return f"<AlertRule(id={self.id}, name='{self.name}', symbol='{self.symbol}', type='{self.type}')>"
|
||
|
|
|
||
|
|
|
||
|
|
class AlertHistory(Base):
|
||
|
|
"""告警历史表"""
|
||
|
|
__tablename__ = "alert_history"
|
||
|
|
|
||
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
|
|
rule_id = Column(Integer, ForeignKey("alert_rule.id", ondelete="CASCADE"), nullable=False, index=True)
|
||
|
|
user_id = Column(Integer, nullable=False, index=True)
|
||
|
|
symbol = Column(String(20), nullable=False, index=True)
|
||
|
|
trigger_value = Column(Numeric(20, 4), nullable=True, comment="触发时的值")
|
||
|
|
trigger_condition = Column(Text, nullable=True, comment="触发条件描述")
|
||
|
|
notified = Column(Boolean, default=False, comment="是否已发送通知")
|
||
|
|
notify_channels = Column(JSONB, nullable=True, comment="已发送的通知渠道")
|
||
|
|
notify_time = Column(TIMESTAMP(timezone=True), nullable=True, comment="通知发送时间")
|
||
|
|
trigger_time = Column(TIMESTAMP(timezone=True), server_default=func.now(), index=True, comment="触发时间")
|
||
|
|
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
|
||
|
|
|
||
|
|
# 关系
|
||
|
|
rule = relationship("AlertRule", back_populates="history")
|
||
|
|
|
||
|
|
def __repr__(self):
|
||
|
|
return f"<AlertHistory(id={self.id}, rule_id={self.rule_id}, trigger_time='{self.trigger_time}')>"
|
||
|
|
|
||
|
|
|
||
|
|
# ============== Pydantic Schema ==============
|
||
|
|
|
||
|
|
class AlertConditionSchema(BaseModel):
|
||
|
|
"""告警条件 Schema"""
|
||
|
|
field: str = Field(..., description="字段名: price/change_percent/volume")
|
||
|
|
operator: AlertOperator = Field(..., description="操作符: gt/lt/eq/ge/le/ne")
|
||
|
|
value: float = Field(..., description="阈值")
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
use_enum_values = True
|
||
|
|
|
||
|
|
|
||
|
|
class AlertRuleCreate(BaseModel):
|
||
|
|
"""创建告警规则"""
|
||
|
|
name: str = Field(..., min_length=1, max_length=100, description="告警名称")
|
||
|
|
symbol: str = Field(..., min_length=1, max_length=20, description="品种代码")
|
||
|
|
type: AlertType = Field(..., description="告警类型")
|
||
|
|
condition: AlertConditionSchema = Field(..., description="触发条件")
|
||
|
|
channels: List[NotifyChannel] = Field(..., min_items=1, description="通知渠道")
|
||
|
|
enabled: bool = Field(default=True, description="是否启用")
|
||
|
|
start_time: Optional[dt_time] = Field(None, description="生效开始时间")
|
||
|
|
end_time: Optional[dt_time] = Field(None, description="生效结束时间")
|
||
|
|
repeat_interval: int = Field(default=0, ge=0, description="重复间隔(秒)")
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
use_enum_values = True
|
||
|
|
|
||
|
|
|
||
|
|
class AlertRuleUpdate(BaseModel):
|
||
|
|
"""更新告警规则"""
|
||
|
|
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
||
|
|
symbol: Optional[str] = Field(None, min_length=1, max_length=20)
|
||
|
|
type: Optional[AlertType] = None
|
||
|
|
condition: Optional[AlertConditionSchema] = None
|
||
|
|
channels: Optional[List[NotifyChannel]] = None
|
||
|
|
enabled: Optional[bool] = None
|
||
|
|
start_time: Optional[dt_time] = None
|
||
|
|
end_time: Optional[dt_time] = None
|
||
|
|
repeat_interval: Optional[int] = Field(None, ge=0)
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
use_enum_values = True
|
||
|
|
|
||
|
|
|
||
|
|
class AlertRuleResponse(BaseModel):
|
||
|
|
"""告警规则响应"""
|
||
|
|
id: int
|
||
|
|
user_id: int
|
||
|
|
name: str
|
||
|
|
symbol: str
|
||
|
|
type: str
|
||
|
|
condition: dict
|
||
|
|
channels: list
|
||
|
|
enabled: bool
|
||
|
|
start_time: Optional[str]
|
||
|
|
end_time: Optional[str]
|
||
|
|
repeat_interval: int
|
||
|
|
last_triggered_at: Optional[datetime]
|
||
|
|
trigger_count: int
|
||
|
|
created_at: datetime
|
||
|
|
updated_at: datetime
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
|
||
|
|
class AlertHistoryResponse(BaseModel):
|
||
|
|
"""告警历史响应"""
|
||
|
|
id: int
|
||
|
|
rule_id: int
|
||
|
|
user_id: int
|
||
|
|
symbol: str
|
||
|
|
trigger_value: Optional[float]
|
||
|
|
trigger_condition: Optional[str]
|
||
|
|
notified: bool
|
||
|
|
notify_channels: Optional[list]
|
||
|
|
notify_time: Optional[datetime]
|
||
|
|
trigger_time: datetime
|
||
|
|
created_at: datetime
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
|
||
|
|
class AlertListResponse(BaseModel):
|
||
|
|
"""告警列表响应"""
|
||
|
|
total: int
|
||
|
|
page: int
|
||
|
|
page_size: int
|
||
|
|
items: List[AlertRuleResponse]
|
||
|
|
|
||
|
|
|
||
|
|
class AlertHistoryListResponse(BaseModel):
|
||
|
|
"""告警历史列表响应"""
|
||
|
|
total: int
|
||
|
|
page: int
|
||
|
|
page_size: int
|
||
|
|
items: List[AlertHistoryResponse]
|