"""聚合视图域 analytics 子域 Router。 实现 9 个 GET 端点,统一采用模板 A(控制面端口路由),通过 ``use_cases.analytics_view.(...)`` 调用类型化方法,经控制面管道 五阶段(鉴权 → 权限 → 限流 → dispatch → 审计)处理。 端点清单: - ANL-01: GET /analytics/messages 消息深度分析 - ANL-02: GET /analytics/messages/distribution 消息类型分布 - ANL-04: GET /analytics/sessions 会话分析 - ANL-05: GET /analytics/delivery 投递链路分析 - ANL-06: GET /analytics/delivery/latency 投递延迟分布 - ANL-07: GET /analytics/delivery/funnel 投递漏斗分析 - ANL-ACCOUNTS: GET /analytics/accounts 账户活跃度分析 - ANL-PEERS: GET /analytics/peers 对端活跃度分析 - ANL-CR: GET /analytics/content-review 内容审核分析 设计依据:《18-聚合视图域-analytics-router-设计方案.md》v1.2(前 6 端点), ANL-ACCOUNTS/PEERS/CR 为后续扩展端点。 """ 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.storage.postgres.models_business import User from server.routers.channels import ( Granularity, build_operator, get_channel_use_cases, parse_datetime, raiseOnControlFailure, serialize_control_data, ) from server.utils.auth_middleware import get_admin_user analytics_router = APIRouter(tags=["channels-analytics"]) @analytics_router.get("/analytics/messages", response_model=dict) async def analyze_messages( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), granularity: Granularity = Query(default="day", description="时间粒度"), 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]: """消息深度分析(ANL-01)。""" 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.analytics_view.analyzeMessages( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), granularity=granularity, operator=operator, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)} @analytics_router.get("/analytics/messages/distribution", response_model=dict) async def get_message_distribution( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), 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]: """消息类型分布分析(ANL-02)。""" 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.analytics_view.getMessageDistribution( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), operator=operator, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)} @analytics_router.get("/analytics/sessions", response_model=dict) async def analyze_sessions( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), granularity: Granularity = Query(default="day", description="时间粒度"), 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]: """会话分析(ANL-04)。""" 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.analytics_view.analyzeSessions( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), granularity=granularity, operator=operator, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)} @analytics_router.get("/analytics/delivery", response_model=dict) async def analyze_delivery( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), 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]: """投递链路分析(ANL-05)。""" 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.analytics_view.analyzeDelivery( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), operator=operator, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)} @analytics_router.get("/analytics/delivery/latency", response_model=dict) async def get_delivery_latency_distribution( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), 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]: """投递延迟分布(ANL-06)。""" 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.analytics_view.getDeliveryLatencyDistribution( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), operator=operator, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)} @analytics_router.get("/analytics/delivery/funnel", response_model=dict) async def get_delivery_funnel( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), 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]: """投递漏斗分析(ANL-07)。""" 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.analytics_view.getDeliveryFunnel( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), operator=operator, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)} @analytics_router.get("/analytics/accounts", response_model=dict) async def analyze_accounts( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), granularity: Granularity = Query(default="day", description="时间粒度"), 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]: """账户活跃度分析(ANL-ACCOUNTS)。""" 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.analytics_view.analyzeAccounts( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), granularity=granularity, operator=operator, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)} @analytics_router.get("/analytics/peers", response_model=dict) async def analyze_peers( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), channel_type: ChannelType | None = Query(default=None, description="渠道类型过滤"), account_id: str | None = Query(default=None, description="账户 ID 过滤"), limit: int = Query(default=100, ge=1, le=500, description="Top N 上限(1-500)"), use_cases=Depends(get_channel_use_cases), current_user: User = Depends(get_admin_user), ) -> dict[str, Any]: """对端活跃度分析(ANL-PEERS)。""" 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.analytics_view.analyzePeers( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), operator=operator, account_id=account_id, limit=limit, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)} @analytics_router.get("/analytics/content-review", response_model=dict) async def analyze_content_review( request: Request, start_time: str = Query(..., description="开始时间(ISO 8601)"), end_time: str = Query(..., description="结束时间(ISO 8601)"), granularity: Granularity = Query(default="day", description="时间粒度"), 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]: """内容审核分析(ANL-CR)。""" 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.analytics_view.analyzeContentReview( channel_type=channel_type, start_time=start_time_dt.isoformat(), end_time=end_time_dt.isoformat(), granularity=granularity, operator=operator, ) raiseOnControlFailure(result) return {"success": True, "data": serialize_control_data(result.data)}