- 新增多个业务域的__init__.py模块文件,规范包导出结构 - 调整多个DTO文件的导入路径,统一模块组织方式 - 移除测试文件中多余的空行与导入语句 - 优化部分业务模块的包层级划分
230 lines
10 KiB
Python
230 lines
10 KiB
Python
"""Dashboard 聚合视图域 Router(DSB-01~DSB-04 + DSB-DELIVERY + DSB-TODOS + DSB-REALTIME)。
|
||
|
||
统一采用模板 A(控制面端口路由),鉴权依赖 get_admin_user,调用
|
||
use_cases.dashboard_view.<method>,raiseOnControlFailure 转译失败,
|
||
serialize_control_data 序列化响应。
|
||
|
||
路径设计:7 个端点均为静态路径(/dashboard/overview / /dashboard/accounts
|
||
/dashboard/messages / /dashboard/sessions / /dashboard/delivery
|
||
/dashboard/todos / /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
|
||
- GET /dashboard/delivery DSB-DELIVERY getDashboardDelivery
|
||
- GET /dashboard/todos DSB-TODOS getDashboardTodos
|
||
- GET /dashboard/realtime DSB-REALTIME getRealtimeMetrics
|
||
|
||
DSB-01~04 对应《16-聚合视图域-dashboard-router-设计方案》(仅覆盖 4 个
|
||
KPI 快照端点)。DSB-DELIVERY / DSB-TODOS / DSB-REALTIME 为后续扩展端点,
|
||
分别提供投递 KPI 总览、工作台待办计数与实时监控指标,不在上述设计方案范围内。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from fastapi import APIRouter, Depends, Query, Request
|
||
from yuxi.channels.contract.dtos.messaging.channel import ChannelType
|
||
from yuxi.channels.contract.dtos.shared.dashboard import (
|
||
DEFAULT_WINDOW_SECONDS,
|
||
MAX_WINDOW_SECONDS,
|
||
MIN_WINDOW_SECONDS,
|
||
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)。非法格式抛
|
||
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.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=DEFAULT_WINDOW_SECONDS,
|
||
ge=MIN_WINDOW_SECONDS,
|
||
le=MAX_WINDOW_SECONDS,
|
||
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)}
|