feat(analytics): 管理层指标呈现——首响回复率上墙/客户资产总览/自定义日期/排行五新列
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""GET /api/analytics/* —— 数据分析板块端组(前缀分组=未来角色权限落点)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
from typing import Annotated, Any, Literal
|
||||
|
||||
import sqlalchemy as sa
|
||||
@@ -21,7 +22,11 @@ ChannelParam = Annotated[Literal["wechat", "whatsapp"], Query()]
|
||||
# 看板 v2(2026-09-07):overview 默认全平台——"all" 在 router 层翻译成
|
||||
# metrics 的 channel=None,不把哨兵字符串传进 SQL
|
||||
AllChannelParam = Annotated[Literal["all", "wechat", "whatsapp"], Query()]
|
||||
ScopeParam = Annotated[Literal["today", "week", "month", "last30"], Query()]
|
||||
# C1(批2):custom=自定义日期范围,配合 start/end yyyymmdd
|
||||
ScopeParam = Annotated[
|
||||
Literal["today", "week", "month", "last30", "custom"], Query()
|
||||
]
|
||||
YmdParam = Annotated[int, Query(ge=20000101, le=29991231)]
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
@@ -36,6 +41,8 @@ async def analytics_overview(
|
||||
session: SessionDep,
|
||||
channel: AllChannelParam = "all",
|
||||
scope: ScopeParam = "today",
|
||||
start: YmdParam | None = None,
|
||||
end: YmdParam | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""A1 经营总览:卡片+趋势+异常警示一次返回(单往返)。
|
||||
|
||||
@@ -43,9 +50,27 @@ async def analytics_overview(
|
||||
双渠道趋势序列;单渠道行为不变。
|
||||
cards 不含 data_as_of——数据截至走 /health(data_as_of 为 ms-epoch int
|
||||
与 entities[].last_seen 为 ISO 字符串的双格式)。
|
||||
|
||||
scope=custom(C1):start/end=yyyymmdd(缺一/日历非法/Start>End/跨度
|
||||
>366 天皆 422);环比=等长紧邻前窗滚动(不做同期对齐)。
|
||||
"""
|
||||
s = e = None
|
||||
if scope == "custom":
|
||||
if start is None or end is None:
|
||||
raise HTTPException(422, "scope=custom requires start and end")
|
||||
try:
|
||||
sd = dt.date(start // 10000, (start // 100) % 100, start % 100)
|
||||
ed = dt.date(end // 10000, (end // 100) % 100, end % 100)
|
||||
except ValueError as exc: # 如 20260230——Query 只拦范围不拦日历
|
||||
raise HTTPException(422, "invalid start/end calendar date") from exc
|
||||
if sd > ed:
|
||||
raise HTTPException(422, "start must be <= end")
|
||||
if (ed - sd).days > 365: # 含端点共 366 天
|
||||
raise HTTPException(422, "custom span must be <= 366 days")
|
||||
s, e = start, end
|
||||
return await metrics_svc.overview(
|
||||
session, channel=None if channel == "all" else channel, scope=scope
|
||||
session, channel=None if channel == "all" else channel, scope=scope,
|
||||
start=s, end=e,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user