ForcePilot/backend/server/routers/channels/dashboard_router.py

220 lines
9.7 KiB
Python
Raw Normal View History

"""Dashboard 聚合视图域 RouterDSB-01~DSB-04 + DSB-DELIVERY + DSB-REALTIME
统一采用模板 A控制面端口路由鉴权依赖 get_admin_user调用
use_cases.dashboard_view.<method>raiseOnControlFailure 转译失败
serialize_control_data 序列化响应
路径设计6 个端点均为静态路径/dashboard/overview / /dashboard/accounts
/dashboard/messages / /dashboard/sessions / /dashboard/delivery
/dashboard/realtime router 自身不设置 prefix根前缀 ``/channels``
``channels_router`` 聚合 router 统一追加
端点清单
- GET /dashboard/overview DSB-01 getDashboardOverview
- GET /dashboard/accounts DSB-02 getAccountStats
- GET /dashboard/messages DSB-03 getMessageStats
- GET /dashboard/sessions DSB-04 getDashboardSessionStats
DSB-01~04 对应16-聚合视图域-dashboard-router-设计方案仅覆盖 4
KPI 快照端点DSB-DELIVERY / DSB-REALTIME 为后续扩展端点分别提供
投递 KPI 总览与实时监控指标不在上述设计方案范围内
- GET /dashboard/delivery DSB-DELIVERY getDashboardDelivery
- GET /dashboard/realtime DSB-REALTIME getRealtimeMetrics
"""
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Depends, Query, Request
from yuxi.channels.contract.dtos.channel import ChannelType
from yuxi.channels.contract.dtos.dashboard import RealtimeQuery
from yuxi.storage.postgres.models_business import User
from server.routers.channels import (
build_operator,
get_channel_use_cases,
parse_datetime,
raiseOnControlFailure,
serialize_control_data,
)
from server.utils.auth_middleware import get_admin_user
dashboard_router = APIRouter(tags=["channels-dashboard"])
@dashboard_router.get("/dashboard/overview", response_model=dict)
async def get_dashboard_overview(
request: Request,
channel_type: ChannelType | None = Query(default=None, description="按渠道类型过滤"),
start_time: str | None = Query(default=None, description="消息统计起始时间ISO 8601仅 messages 子域受影响"),
end_time: str | None = Query(default=None, description="消息统计截止时间ISO 8601仅 messages 子域受影响"),
use_cases=Depends(get_channel_use_cases),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""查询全局总览DSB-01。对应控制面操作 dashboard/overview。
聚合账户 / 消息 / 会话 / 投递四类子域 KPI 计数
``start_time`` / ``end_time`` 仅影响消息统计账户会话投递为当前
快照不受时间范围影响
"""
operator = build_operator(current_user, request)
start_time_dt = parse_datetime("start_time", start_time)
end_time_dt = parse_datetime("end_time", end_time)
result = await use_cases.dashboard_view.getDashboardOverview(
channel_type=channel_type,
start_time=start_time_dt.isoformat() if start_time_dt else None,
end_time=end_time_dt.isoformat() if end_time_dt else None,
operator=operator,
)
raiseOnControlFailure(result)
return {"success": True, "data": serialize_control_data(result.data)}
@dashboard_router.get("/dashboard/accounts", response_model=dict)
async def get_dashboard_accounts(
request: Request,
channel_type: ChannelType | None = Query(default=None, description="按渠道类型过滤"),
use_cases=Depends(get_channel_use_cases),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""查询账户总览DSB-02。对应控制面操作 dashboard/accounts。
返回账户按渠道 / 状态分组的计数快照
"""
operator = build_operator(current_user, request)
result = await use_cases.dashboard_view.getAccountStats(
channel_type=channel_type,
operator=operator,
)
raiseOnControlFailure(result)
return {"success": True, "data": serialize_control_data(result.data)}
@dashboard_router.get("/dashboard/messages", response_model=dict)
async def get_dashboard_messages(
request: Request,
channel_type: ChannelType | None = Query(default=None, description="按渠道类型过滤"),
start_time: str | None = Query(default=None, description="起始时间ISO 8601"),
end_time: str | None = Query(default=None, description="截止时间ISO 8601"),
use_cases=Depends(get_channel_use_cases),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""查询消息总览DSB-03。对应控制面操作 dashboard/messages。
返回指定时间范围内的消息按渠道 / 角色 / 投递状态分组的计数快照
时间参数由 parse_datetime Router 层翻译为 UTC datetime再转回
ISO 8601 字符串传给端口端口签名为 str非法格式抛
ValidationError400
"""
operator = build_operator(current_user, request)
start_time_dt = parse_datetime("start_time", start_time)
end_time_dt = parse_datetime("end_time", end_time)
result = await use_cases.dashboard_view.getMessageStats(
channel_type=channel_type,
start_time=start_time_dt.isoformat() if start_time_dt else None,
end_time=end_time_dt.isoformat() if end_time_dt else None,
operator=operator,
)
raiseOnControlFailure(result)
return {"success": True, "data": serialize_control_data(result.data)}
@dashboard_router.get("/dashboard/sessions", response_model=dict)
async def get_dashboard_sessions(
request: Request,
channel_type: ChannelType | None = Query(default=None, description="按渠道类型过滤"),
use_cases=Depends(get_channel_use_cases),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""查询会话总览DSB-04。对应控制面操作 dashboard/sessions。
返回未软删除会话按渠道 / 是否临时会话分组的计数快照
"""
operator = build_operator(current_user, request)
result = await use_cases.dashboard_view.getDashboardSessionStats(
channel_type=channel_type,
operator=operator,
)
raiseOnControlFailure(result)
return {"success": True, "data": serialize_control_data(result.data)}
@dashboard_router.get("/dashboard/delivery", response_model=dict)
async def get_dashboard_delivery(
request: Request,
channel_type: ChannelType | None = Query(default=None, description="按渠道类型过滤"),
start_time: str | None = Query(default=None, description="起始时间ISO 8601"),
end_time: str | None = Query(default=None, description="截止时间ISO 8601"),
use_cases=Depends(get_channel_use_cases),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""查询投递总览DSB-DELIVERY。对应控制面操作 dashboard/delivery。
聚合投递 KPI 指标总数成功率延迟分位数死信数按渠道切片
时间参数由 parse_datetime Router 层翻译为 UTC datetime再转回
ISO 8601 字符串传给端口端口签名为 str非法格式抛 ValidationError
400
"""
operator = build_operator(current_user, request)
start_time_dt = parse_datetime("start_time", start_time)
end_time_dt = parse_datetime("end_time", end_time)
result = await use_cases.dashboard_view.getDashboardDelivery(
channel_type=channel_type,
start_time=start_time_dt.isoformat() if start_time_dt else None,
end_time=end_time_dt.isoformat() if end_time_dt else None,
operator=operator,
)
raiseOnControlFailure(result)
return {"success": True, "data": serialize_control_data(result.data)}
@dashboard_router.get("/dashboard/todos", response_model=dict)
async def get_dashboard_todos(
request: Request,
channel_type: ChannelType | None = Query(default=None, description="按渠道类型过滤"),
use_cases=Depends(get_channel_use_cases),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""查询工作台待办计数DSB-TODOS。对应控制面操作 dashboard/todos。
聚合待审批配对数待复核内容审核数与当前死信积压数按渠道类型过滤
"""
operator = build_operator(current_user, request)
result = await use_cases.dashboard_view.getDashboardTodos(
channel_type=channel_type,
operator=operator,
)
raiseOnControlFailure(result)
return {"success": True, "data": serialize_control_data(result.data)}
@dashboard_router.get("/dashboard/realtime", response_model=dict)
async def get_dashboard_realtime(
request: Request,
channel_type: ChannelType | None = Query(default=None, description="按渠道类型过滤"),
window_seconds: int = Query(default=60, ge=10, le=300, description="滑动窗口秒数10-300默认 60"),
use_cases=Depends(get_channel_use_cases),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""查询实时监控指标DSB-REALTIME。对应控制面操作 dashboard/realtime。
纯内存态聚合业务不开启 DB 事务``db_tx_required=False``高频查询
场景使用审计走 INDEPENDENT 独立事务 best-effort 写入每请求一次
审计记录不阻断响应
``channel_type`` 为空全局查询``by_channel`` 返回全部已注册
渠道的切片非空时仅返回该渠道一条
"""
operator = build_operator(current_user, request)
query = RealtimeQuery(
channel_type=channel_type,
window_seconds=window_seconds,
)
result = await use_cases.dashboard_view.getRealtimeMetrics(query, operator=operator)
raiseOnControlFailure(result)
return {"success": True, "data": serialize_control_data(result.data)}