feat: 新增告警、成本、配额、微信模块及缓存服务
All checks were successful
continuous-integration/drone/push Build is passing

- 新增告警模块 (alerts): 告警规则配置与触发
- 新增成本管理模块 (cost): 成本统计与分析
- 新增配额模块 (quota): 配额管理与限制
- 新增微信模块 (wechat): 微信相关功能接口
- 新增缓存服务 (cache): Redis 缓存封装
- 新增请求日志中间件 (request_logger)
- 新增异常处理和链路追踪中间件
- 更新 dashboard 前端展示
- 更新 SDK stats_client 功能
This commit is contained in:
111
2026-01-24 16:53:47 +08:00
parent eab2533c36
commit 6c6c48cf71
29 changed files with 4607 additions and 41 deletions

View File

@@ -5,6 +5,8 @@ from .tenant_wechat_app import TenantWechatApp
from .app import App
from .stats import AICallEvent, TenantUsageDaily
from .logs import PlatformLog
from .alert import AlertRule, AlertRecord, NotificationChannel
from .pricing import ModelPricing, TenantBilling
__all__ = [
"Tenant",
@@ -15,5 +17,10 @@ __all__ = [
"App",
"AICallEvent",
"TenantUsageDaily",
"PlatformLog"
"PlatformLog",
"AlertRule",
"AlertRecord",
"NotificationChannel",
"ModelPricing",
"TenantBilling"
]

108
backend/app/models/alert.py Normal file
View File

@@ -0,0 +1,108 @@
"""告警相关模型"""
from datetime import datetime
from sqlalchemy import Column, Integer, BigInteger, String, Text, Enum, SmallInteger, JSON, TIMESTAMP
from ..database import Base
class AlertRule(Base):
"""告警规则表"""
__tablename__ = "platform_alert_rules"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False) # 规则名称
description = Column(Text) # 规则描述
# 规则类型
rule_type = Column(Enum(
'error_rate', # 错误率告警
'call_count', # 调用次数告警
'token_usage', # Token使用量告警
'cost_threshold', # 费用阈值告警
'latency', # 延迟告警
'custom' # 自定义告警
), nullable=False)
# 作用范围
scope_type = Column(Enum('global', 'tenant', 'app'), default='global') # 作用范围类型
scope_value = Column(String(100)) # 作用范围值如租户ID或应用代码
# 告警条件
condition = Column(JSON, nullable=False) # 告警条件配置
# 示例: {"metric": "error_count", "operator": ">", "threshold": 10, "window": "5m"}
# 通知配置
notification_channels = Column(JSON) # 通知渠道列表
# 示例: [{"type": "wechat_bot", "webhook": "https://..."}, {"type": "email", "to": ["a@b.com"]}]
# 告警限制
cooldown_minutes = Column(Integer, default=30) # 冷却时间(分钟),避免重复告警
max_alerts_per_day = Column(Integer, default=10) # 每天最大告警次数
# 状态
status = Column(SmallInteger, default=1) # 0-禁用 1-启用
priority = Column(Enum('low', 'medium', 'high', 'critical'), default='medium') # 优先级
created_at = Column(TIMESTAMP, default=datetime.now)
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)
class AlertRecord(Base):
"""告警记录表"""
__tablename__ = "platform_alert_records"
id = Column(BigInteger, primary_key=True, autoincrement=True)
rule_id = Column(Integer, nullable=False, index=True) # 关联的规则ID
rule_name = Column(String(100)) # 规则名称(冗余,便于查询)
# 告警信息
alert_type = Column(String(50), nullable=False) # 告警类型
severity = Column(Enum('info', 'warning', 'error', 'critical'), default='warning') # 严重程度
title = Column(String(200), nullable=False) # 告警标题
message = Column(Text) # 告警详情
# 上下文
tenant_id = Column(String(50), index=True) # 相关租户
app_code = Column(String(50)) # 相关应用
metric_value = Column(String(100)) # 触发告警的指标值
threshold_value = Column(String(100)) # 阈值
# 通知状态
notification_status = Column(Enum('pending', 'sent', 'failed', 'skipped'), default='pending')
notification_result = Column(JSON) # 通知结果
notified_at = Column(TIMESTAMP) # 通知时间
# 处理状态
status = Column(Enum('active', 'acknowledged', 'resolved', 'ignored'), default='active')
acknowledged_by = Column(String(100)) # 确认人
acknowledged_at = Column(TIMESTAMP) # 确认时间
resolved_at = Column(TIMESTAMP) # 解决时间
created_at = Column(TIMESTAMP, default=datetime.now, index=True)
class NotificationChannel(Base):
"""通知渠道配置表"""
__tablename__ = "platform_notification_channels"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False) # 渠道名称
channel_type = Column(Enum(
'wechat_bot', # 企微机器人
'email', # 邮件
'sms', # 短信
'webhook', # Webhook
'dingtalk' # 钉钉
), nullable=False)
# 渠道配置
config = Column(JSON, nullable=False)
# wechat_bot: {"webhook": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx"}
# email: {"smtp_host": "...", "smtp_port": 465, "username": "...", "password_encrypted": "..."}
# webhook: {"url": "https://...", "method": "POST", "headers": {...}}
# 状态
status = Column(SmallInteger, default=1) # 0-禁用 1-启用
created_at = Column(TIMESTAMP, default=datetime.now)
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)

View File

@@ -0,0 +1,70 @@
"""费用计算相关模型"""
from datetime import datetime
from decimal import Decimal
from sqlalchemy import Column, Integer, String, Text, DECIMAL, SmallInteger, JSON, TIMESTAMP
from ..database import Base
class ModelPricing(Base):
"""模型价格配置表"""
__tablename__ = "platform_model_pricing"
id = Column(Integer, primary_key=True, autoincrement=True)
# 模型标识
model_name = Column(String(100), nullable=False, unique=True) # 模型名称,如 gpt-4, claude-3-opus
provider = Column(String(50)) # 提供商,如 openai, anthropic, 4sapi
display_name = Column(String(100)) # 显示名称
# 价格配置(单位:元/1K tokens
input_price_per_1k = Column(DECIMAL(10, 6), default=0) # 输入价格
output_price_per_1k = Column(DECIMAL(10, 6), default=0) # 输出价格
# 或固定价格(每次调用)
fixed_price_per_call = Column(DECIMAL(10, 6), default=0)
# 计费方式
pricing_type = Column(String(20), default='token') # token / call / hybrid
# 备注
description = Column(Text)
status = Column(SmallInteger, default=1) # 0-禁用 1-启用
created_at = Column(TIMESTAMP, default=datetime.now)
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)
class TenantBilling(Base):
"""租户账单表(月度汇总)"""
__tablename__ = "platform_tenant_billing"
id = Column(Integer, primary_key=True, autoincrement=True)
tenant_id = Column(String(50), nullable=False, index=True)
billing_month = Column(String(7), nullable=False) # 格式: YYYY-MM
# 使用量统计
total_calls = Column(Integer, default=0) # 总调用次数
total_input_tokens = Column(Integer, default=0) # 总输入token
total_output_tokens = Column(Integer, default=0) # 总输出token
# 费用统计
total_cost = Column(DECIMAL(12, 4), default=0) # 总费用
# 按模型分类的费用明细
cost_by_model = Column(JSON) # {"gpt-4": 10.5, "claude-3": 5.2}
# 按应用分类的费用明细
cost_by_app = Column(JSON) # {"tools": 8.0, "interview": 7.7}
# 状态
status = Column(String(20), default='pending') # pending / confirmed / paid
created_at = Column(TIMESTAMP, default=datetime.now)
updated_at = Column(TIMESTAMP, default=datetime.now, onupdate=datetime.now)
class Config:
# 联合唯一索引
__table_args__ = (
{'mysql_charset': 'utf8mb4'}
)