f640889f22
- 仪表盘视图:超管=平台总览四指标卡(/api/platform/stats 合并视图)+
租户卡片墙(逐租户 ?tenant= 下钻,含停用租户);租户管理员=自己
指标卡(/tenants/{slug}/stats,slug 取登录态);口径文案照
stats.py docstring(线索=留资三选一/成功率分母0显—等)
- 时间范围快捷切换(今天/近7天/近30天)→ from/to 本地日期串;
utils/statsRange.ts 纯函数 + epoch 防快速切换过期响应竞写
- 空数据态:无租户 ElEmpty / 单租户零数据自然显 0 与 —
- api/stats.ts 补 fetchPlatformStats/fetchTenantStats;tenants.ts 补
fetchTenantList(卡片墙枚举,其余仍留 Task 6)
- vitest +11(statsRange 6 + stats API 5,总 36)
- 顺手1:test_auth must_change 409 测试补 detail 断言,锁死前后端
契约文案(client.ts MUST_CHANGE_DETAIL)
- 顺手2:删除 beforeEach.ts 的 isUnauthorizedError 死分支与 ApiStatus
import(模板 isHttpError 判自建 ApiError 恒 false,分支不可达)
Co-Authored-By: Claude Code <noreply@anthropic.com>
100 lines
3.1 KiB
TypeScript
100 lines
3.1 KiB
TypeScript
/**
|
||
* 统计分析 API(Phase 3 Task 4 实装调用函数;类型与端点 Task 3 建立)
|
||
*
|
||
* 端点事实源:app/platform/routes/stats_routes.py + app/platform/stats.py
|
||
* (平台合并统计 / 单租户统计,?from/&to= 闭区间)。
|
||
*
|
||
* 指标口径事实源:app/platform/stats.py 模块 docstring 的口径表——
|
||
* 会话数不要求有消息 / 线索=留资三选一(phone/whatsapp/email)任一非空 /
|
||
* 高意向=评估 interested / 推送成功率 success/(success+failed)(分母 0 →
|
||
* null,前端展示"—")。Task 8(统计分析页)复用本组函数。
|
||
*/
|
||
import { http } from './client'
|
||
|
||
/** 指标卡(None 项为 null——如无推送时成功率无定义) */
|
||
export interface StatsCards {
|
||
conversations: number
|
||
leads: number
|
||
high_intent: number
|
||
push_success_rate: number | null
|
||
}
|
||
|
||
/** 漏斗计数 */
|
||
export interface StatsFunnel {
|
||
conversations: number
|
||
leads: number
|
||
evaluated: number
|
||
push_succeeded: number
|
||
}
|
||
|
||
/** 按日趋势行 */
|
||
export interface StatsTrendPoint {
|
||
date: string
|
||
conversations: number
|
||
leads: number
|
||
}
|
||
|
||
/** 分布视图(intent 按枚举键、source/site 计数表、country Top 数组) */
|
||
export interface StatsDistributions {
|
||
intent: Record<string, number>
|
||
source: Record<string, number>
|
||
site: Record<string, number>
|
||
country: Array<{ country: string; count: number }>
|
||
}
|
||
|
||
/** 推送健康计数 */
|
||
export interface PushHealth {
|
||
pending: number
|
||
success: number
|
||
failed: number
|
||
skipped: number
|
||
duplicated: number
|
||
}
|
||
|
||
/** 统计响应(平台合并与单租户同构) */
|
||
export interface StatsResponse {
|
||
cards: StatsCards
|
||
funnel: StatsFunnel
|
||
trend: StatsTrendPoint[]
|
||
distributions: StatsDistributions
|
||
push_health: PushHealth
|
||
}
|
||
|
||
/** 统计查询参数(from/to 闭区间,ISO 日期) */
|
||
export interface StatsParams {
|
||
from?: string
|
||
to?: string
|
||
}
|
||
|
||
/** 统计端点(platform 合并版仅超管;单租户版超管或本租户管理员) */
|
||
export const STATS_ENDPOINTS = {
|
||
platform: '/api/platform/stats',
|
||
tenant: (slug: string) => `/api/platform/tenants/${slug}/stats`
|
||
} as const
|
||
|
||
/** 平台合并统计查询参数(?tenant= 单租户下钻,未知 slug 404) */
|
||
export interface PlatformStatsParams extends StatsParams {
|
||
tenant?: string
|
||
}
|
||
|
||
/**
|
||
* 平台统计(仅超管):缺省全部租户合并(逐库查询应用层合并);
|
||
* tenant=slug 单租户下钻——仪表盘租户卡片墙逐租户调用即此形态。
|
||
* options.showErrorMessage: false 关全局错误提示(卡片墙 N 连发的失败
|
||
* toast 改由调用方逐卡内联展示)
|
||
*/
|
||
export function fetchPlatformStats(
|
||
params?: PlatformStatsParams,
|
||
options?: { showErrorMessage?: boolean }
|
||
): Promise<StatsResponse> {
|
||
return http.get<StatsResponse>(STATS_ENDPOINTS.platform, { params, ...options })
|
||
}
|
||
|
||
/**
|
||
* 单租户统计(本租户管理员或超管):租户管理员的"自己指标卡"走这里,
|
||
* slug 取登录态 tenant_slug(auth store)
|
||
*/
|
||
export function fetchTenantStats(slug: string, params?: StatsParams): Promise<StatsResponse> {
|
||
return http.get<StatsResponse>(STATS_ENDPOINTS.tenant(slug), { params })
|
||
}
|