feat(admin): 来源归因展示——渠道徽章/筛选/来源识别卡/渠道构成图

- 类型:VisitorRow 补 channel/platform/variant/search_term/resolution,新增
  AttributionInfo/AttributionBlock,详情响应带 attribution 块;dashboard
  响应补 channels(ChannelItem)
- 工具:CHANNEL_LABELS 八渠道元数据 + channelBadge 徽章串(搜索词/标签/
  冲突后缀,未归因回 —)
- 访客列表:首触来源列升级渠道徽章(first_source 进 tooltip 兜底),
  筛选栏加渠道下拉(channel= 枚举)
- 访客详情:首触来源升级来源识别卡(渠道徽章/平台/国家站/搜索词显著
  展示,label_only/conflict 诚实标注,冲突并列两路证据文本渲染),
  无归因旧数据降级原 first_source 口径
- 总览:来源饼图改渠道构成(标签/颜色按渠道绑定 CHANNEL_LABELS),
  下方保留平台明细小表(原 sources 域名口径)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-16 17:19:31 +08:00
parent 64d5daf921
commit f9c8c354ae
6 changed files with 319 additions and 34 deletions
+9
View File
@@ -31,6 +31,13 @@ export interface SourceItem {
sessions: number
}
/** 今日渠道构成 TOP8(session_entry 分类聚合,sessions 去重计数;
* channel 为 CHANNELS 枚举,无分类行不计入——未重算的历史会话不在此口径内) */
export interface ChannelItem {
channel: string
sessions: number
}
/** 今日热门页面 TOP8(page_view 按 path 聚合) */
export interface PageItem {
path: string
@@ -42,6 +49,8 @@ export interface OverviewResp {
today: TodayCounts
trend: TrendPoint[]
sources: SourceItem[]
/** 来源渠道聚合(sources 的归因升级版,原始域名口径保留在 sources) */
channels: ChannelItem[]
top_pages: PageItem[]
}
+37
View File
@@ -30,6 +30,12 @@ export interface VisitorRow {
total_stay_ms: number
first_source: string
last_source: string
/** 首触来源归因(first_touch 分类;无分类行/未重算的历史访客为 '',前端回落 first_source 展示) */
channel: string
platform: string
variant: string
search_term: string
resolution: string
online: boolean
}
@@ -45,6 +51,35 @@ export interface SessionSummary {
stay_ms: number
}
/** 单条来源识别(visitors.py `_classification`:classify() 结果 + 规则版本/计算时间) */
export interface AttributionInfo {
/** CHANNELS 枚举:natural_search/paid_search/ai_referral/social/in_app/referral/direct/unknown */
channel: string
/** 平台显示名(Google/ChatGPT/微信…;referral 渠道为来源域名) */
platform: string
/** 国家站变体(如 google.ru 的 "ru") */
variant: string
/** 搜索词(仅自然搜索可能有值) */
search_term: string
/** observed_referrer / utm_label / combined / observed_ua */
evidence_kind: string
/** resolved / label_only / conflict / unknown */
resolution: string
/** 规则侧附加证据(conflict 时含 referrer_evidence/utm_evidence 并列)——只做文本渲染,禁 v-html */
detail: Record<string, unknown>
/** 生成该分类的规则版本(attribution.py RULE_VERSION) */
rule_version: string
computed_at: string
/** 仅 session_entries 携带(first_touch 无会话归属,不带该键) */
session_id?: string | null
}
/** 详情归因块:first_touch 主口径 + 近几次会话入口识别(旧数据未重算时 first_touch 为 null) */
export interface AttributionBlock {
first_touch: AttributionInfo | null
session_entries: AttributionInfo[]
}
/** 访客详情(`_row` + 详情扩展字段 + 全量会话摘要,M1 不分页) */
export interface VisitorDetail extends VisitorRow {
screen: string
@@ -54,6 +89,8 @@ export interface VisitorDetail extends VisitorRow {
first_utm: Record<string, string>
first_landing_path: string
first_referrer_domain: string
/** 来源归因块(first_touch=null 时前端回落 first_source 展示) */
attribution: AttributionBlock
sessions: SessionSummary[]
}