ForcePilot/backend/server/routers/external_systems/dashboard_router.py
Kris 08617091dc refactor: 整理项目包结构与导入路径
- 新增多个业务域的__init__.py模块文件,规范包导出结构
- 调整多个DTO文件的导入路径,统一模块组织方式
- 移除测试文件中多余的空行与导入语句
- 优化部分业务模块的包层级划分
2026-07-18 02:04:03 +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.dto.dashboard import (
ActivitiesStatsOutput,
AlertsStatsOutput,
ExecutionsStatsOutput,
GetActivitiesStatsInput,
GetAlertsStatsInput,
GetExecutionsStatsInput,
GetHealthStatsInput,
GetOverviewInput,
GetQuotasStatsInput,
GetSystemsStatsInput,
GetTodosStatsInput,
HealthStatsOutput,
OverviewOutput,
QuotasStatsOutput,
SystemsStatsOutput,
TodosStatsOutput,
)
from yuxi.external_systems.use_cases.ports import DashboardServicePort
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)