ForcePilot/backend/server/routers/external_systems/dashboard_router.py
Kris f9f08221fc chore: 整理代码风格与优化细节
本次提交包含多类代码优化:
1. 修复多处单行代码换行格式,统一代码排版
2. 为外部系统模块新增快捷菜单配置
3. 优化前端API请求参数命名一致性
4. 补充媒体下载超限错误码与领域异常类
5. 完善仓储层更新逻辑,支持显式清空字段
6. 优化部分测试用例与工具函数代码结构
7. 为微信插件白名单缓存增加过期时间
2026-07-14 15:13:29 +08:00

191 lines
7.8 KiB
Python
Raw 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.

"""Dashboard 子域 Router。
外部系统限界上下文的监控仪表板 API覆盖全局总览 / 系统维度 /
执行维度 / 告警维度 / 配额维度 / 健康维度 / 活动维度的跨子域聚合统计。
所有端点通过 ``create_dashboard_service_from_db`` 装配轻量级 dashboard_service
经 ``DashboardServicePort`` 端口调用用例(纯只读聚合,无需完整 use_cases 容器)。
路径顺序约束:本 router 所有端点均为静态路径(无动态路径参数),无路由匹配歧义。
端点按业务维度组织(总览 → 系统 → 执行 → 告警 → 配额 → 健康 → 活动),
与上文子域覆盖顺序保持一致。
认证策略:
- 所有端点均为只读查询,使用 get_required_user支持 JWT + API Key 双模认证)
"""
from __future__ import annotations
from typing import Any, Literal
from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel
from yuxi.external_systems.use_cases.ports import DashboardServicePort
from yuxi.external_systems.use_cases.dto.dashboard import (
ActivitiesStatsOutput,
AlertsStatsOutput,
ExecutionsStatsOutput,
GetActivitiesStatsInput,
GetAlertsStatsInput,
GetExecutionsStatsInput,
GetHealthStatsInput,
GetOverviewInput,
GetQuotasStatsInput,
GetSystemsStatsInput,
GetTodosStatsInput,
HealthStatsOutput,
OverviewOutput,
QuotasStatsOutput,
SystemsStatsOutput,
TodosStatsOutput,
)
from yuxi.storage.postgres.models_business import User
from server.routers.external_systems import get_dashboard_service, parse_and_naive, validate_time_range
from server.utils.auth_middleware import get_required_user
dashboard_router = APIRouter(prefix="/dashboard", tags=["external-systems-dashboard"])
# ---------------- Response Helpers ----------------
def _build_response(output: BaseModel) -> dict[str, Any]:
"""构建统一成功响应,使用 JSON 模式序列化确保日期/枚举值可安全输出。"""
return {"success": True, "data": output.model_dump(mode="json")}
# ---------------- Endpoints ----------------
@dashboard_router.get("/overview", response_model=dict)
async def get_overview(
start_time: str | None = Query(None, description="执行统计起始时间ISO 8601"),
end_time: str | None = Query(None, description="执行统计结束时间ISO 8601"),
dashboard_service: DashboardServicePort = Depends(get_dashboard_service),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""全局总览。聚合 5 个子域的关键指标。"""
start_dt = parse_and_naive("start_time", start_time)
end_dt = parse_and_naive("end_time", end_time)
validate_time_range(start_dt, end_dt)
input_dto = GetOverviewInput(
start=start_dt,
end=end_dt,
)
output: OverviewOutput = await dashboard_service.get_overview(input_dto)
return _build_response(output)
@dashboard_router.get("/systems", response_model=dict)
async def get_systems_stats(
dashboard_service: DashboardServicePort = Depends(get_dashboard_service),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""系统维度统计。含启用状态/适配器类型/分类分组 + 熔断器状态。"""
input_dto = GetSystemsStatsInput()
output: SystemsStatsOutput = await dashboard_service.get_systems_stats(input_dto)
return _build_response(output)
@dashboard_router.get("/executions", response_model=dict)
async def get_executions_stats(
start_time: str | None = Query(None, description="执行统计起始时间ISO 8601"),
end_time: str | None = Query(None, description="执行统计结束时间ISO 8601"),
tool_limit: int = Query(20, ge=1, le=100, description="Top 工具数量"),
dashboard_service: DashboardServicePort = Depends(get_dashboard_service),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""执行维度统计。含按状态/工具分组。"""
start_dt = parse_and_naive("start_time", start_time)
end_dt = parse_and_naive("end_time", end_time)
validate_time_range(start_dt, end_dt)
input_dto = GetExecutionsStatsInput(
start=start_dt,
end=end_dt,
tool_limit=tool_limit,
)
output: ExecutionsStatsOutput = await dashboard_service.get_executions_stats(input_dto)
return _build_response(output)
@dashboard_router.get("/alerts", response_model=dict)
async def get_alerts_stats(
severity_status: Literal["firing", "acknowledged", "resolved", "suppressed"] = Query(
"firing",
description="count_by_severity 的状态过滤告警状态firing/acknowledged/resolved/suppressed",
),
top_limit: int = Query(10, ge=1, le=50, description="Top 告警去重键数量"),
dashboard_service: DashboardServicePort = Depends(get_dashboard_service),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""告警维度统计。含按状态/严重程度分组 + Top 告警去重键。"""
input_dto = GetAlertsStatsInput(
severity_status=severity_status,
top_limit=top_limit,
)
output: AlertsStatsOutput = await dashboard_service.get_alerts_stats(input_dto)
return _build_response(output)
@dashboard_router.get("/quotas", response_model=dict)
async def get_quotas_stats(
warning_limit: int = Query(20, ge=1, le=100, description="预警配额明细数量"),
critical_limit: int = Query(20, ge=1, le=100, description="临界配额明细数量"),
dashboard_service: DashboardServicePort = Depends(get_dashboard_service),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""配额维度统计。含按窗口分组 + 预警/临界配额明细。"""
input_dto = GetQuotasStatsInput(
warning_limit=warning_limit,
critical_limit=critical_limit,
)
output: QuotasStatsOutput = await dashboard_service.get_quotas_stats(input_dto)
return _build_response(output)
@dashboard_router.get("/health", response_model=dict)
async def get_health_stats(
dashboard_service: DashboardServicePort = Depends(get_dashboard_service),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""健康维度统计。含按健康状态分组。"""
input_dto = GetHealthStatsInput()
output: HealthStatsOutput = await dashboard_service.get_health_stats(input_dto)
return _build_response(output)
@dashboard_router.get("/activities", response_model=dict)
async def get_activities_stats(
start_time: str | None = Query(None, description="活动统计起始时间ISO 8601"),
end_time: str | None = Query(None, description="活动统计结束时间ISO 8601"),
dashboard_service: DashboardServicePort = Depends(get_dashboard_service),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""活动维度统计。含按操作类型分组。"""
start_dt = parse_and_naive("start_time", start_time)
end_dt = parse_and_naive("end_time", end_time)
validate_time_range(start_dt, end_dt)
input_dto = GetActivitiesStatsInput(
start=start_dt,
end=end_dt,
)
output: ActivitiesStatsOutput = await dashboard_service.get_activities_stats(input_dto)
return _build_response(output)
@dashboard_router.get("/todos", response_model=dict)
async def get_todos_stats(
expiring_token_days: int = Query(
7,
ge=1,
le=90,
description="Token 即将过期阈值(天)",
),
dashboard_service: DashboardServicePort = Depends(get_dashboard_service),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""待办统计。跨子域聚合影响导航健康灯/角标的关键计数。"""
input_dto = GetTodosStatsInput(expiring_token_days=expiring_token_days)
output: TodosStatsOutput = await dashboard_service.get_todos_stats(input_dto)
return _build_response(output)