docs: 阶段B spec独立复审修正——fb+paid优先规则(实测主导模式)/_ctk_sid原生submit捕获/产品段匹配非锚定/校验补全

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-18 18:06:59 +08:00
parent f4af77d261
commit fd3ffc8bc2
@@ -2,7 +2,7 @@
日期:2026-09-18
分支:m1
状态:设计已获用户批准(含三轮自审 21 项修正)
状态:设计已获用户批准(四轮自审+独立复审,共 28 项修正)
依据:gpt/14 §5/§6.2;前置阶段A 已上线(2026-09-18,归属冻结/区间停留/清洗三轨制/契约 v2)
实测基础:hesucar.com 载体盘点(2026-09-18 Playwright)——tel/mailto/wa.me 链接、Contact Form 7 v6.1.6 留言表单(Name/Tel/Message→Inquiry Now)、悬浮聊天组件
@@ -43,7 +43,7 @@
### 1.3 product_view
- 规则:path 匹配 `/product/{slug}/`(pattern 内置默认,init 可覆盖——为未来站点预留,hesucar 用默认)。
- 规则:path **段匹配** `product/{slug}`(**非锚定**——覆盖 `/{lang}/product/{slug}/` 多语言前缀;实测样本 9 种语言前缀 ar/de/es/fr/it/pt/ro/ru/th,锚定匹配会静默丢失约半数产品页);pattern 内置默认,init 可覆盖——为未来站点预留,hesucar 用默认。
- 属性:`{product_id: slug, inferred: true, rule_version: "v1"}`。多语言同产品无法关联——诚实标注(inferred 降级路径,gpt/14 §5.1)。
- **触发时机**:init 时同步判定 + SPA 切段回调。PageSession 新增 `onSegmentChange(fn)` 钩子——**触发点钉死在 `onRouteChange` 内新段创建之后**(模块级注册表,不在 `close()` 内触发——close 还覆盖 pagehide/重复 init,放在那里会在卸载时重复发 product_view);单点 hook history,business.ts 注册回调判定新段 path,不重复 wrap。
- PV 不双计:服务端 PV 统计已有 event_name 过滤(87261ab)。
@@ -55,21 +55,22 @@
### 1.5 隐藏字段 `_ctk_sid`(服务端确认契约预留的浏览器侧半边)
- form_start 时向表单注入 `<input type="hidden" name="_ctk_sid" value="">`(**空占位**——不用预生成 ID:若 submit 时更新对 CF7 FormData 不可见,空值=干净缺失,预生成 ID 则阶段C 会收到匹配不到任何事件的孤儿确认,更糟);form_submit 事件触发时更新该字段为当次 submission_id(更新可见性以 CF7 源码核验为准,不可靠则该次对齐缺失)。
- **submission_id 写入时序(结构性修正)**:`wpcf7submit` 在 AJAX 响应到达后才触发——POST 已发出,届时更新字段必然晚。因此改为:**capture 阶段监听表单原生 `submit` 事件**(document 级 capture 委托,先于 CF7 的 handler 构造 FormData 执行)——原生 submit 时生成当次 submission_id 并写入隐藏字段;wpcf7submit 触发的 form_submit 事件引用同一 ID。
- form_start 时注入 `<input type="hidden" name="_ctk_sid" value="">` 空占位(提交前字段恒存在;值由上述 submit 捕获填充)。
- 作用:CF7 提交 POST 天然携带,阶段C 的 WP mu-plugin 从 `$_POST['_ctk_sid']` 读取对齐。CF7 忽略未注册字段、正常传输——对现有表单行为无害。
- known-limitation:无任何交互直接提交的极端场景,submission_id 可能为空(事件与漏斗不受损,仅确认对齐缺失)。
- known-limitation:`_ctk_sid` 空值仍可能出现(capture submit 未及触发的极端时序),阶段C 按 §5.4 兜底(浏览器侧缺失不影响服务器事实)。
## 2. Server:契约扩展
- `EVENT_NAMES` +6:contact_click / form_start / form_error / form_submit / form_submit_result / product_view。
- `EVENT_PROP_WHITELIST` 严格枚举(§1 表格所列键;无自由文本字段)。
- validate_event 新键校验:`channel` ∈ {whatsapp, phone, email};`result_code` ∈ {mail_sent, mail_failed};`error_code` 短枚举(invalid/spam/other);`submission_id` uuid 或空串;`form_id` 格式 `cf7-\d+`;`product_id` slug 格式(复用清洗 `_SAFE_VALUE` 同款正则);`inferred` 布尔;`rule_version` 短 slug。
- validate_event 新键校验:`channel` ∈ {whatsapp, phone, email};`result_code` ∈ {mail_sent, mail_failed};`error_code` 短枚举(invalid/spam/other);`submission_id` uuid 或空串;`form_id` 格式 `cf7-\d+`;`product_id` slug 格式(**≤100 字符**——独立于清洗 _SAFE_VALUE 的 31 上限,产品 slug 可较长);`field_count` 非负整数(≤99);`inferred` 布尔;`rule_version` 短 slug。
- 幂等沿 uq_site_event;归属走既有 v2 路径,零新机制。
## 3. D06 归因扩展(attribution.py + constants.py)
- **优先级重排**:点击参数决定平台性质——fbclid → **paid_social**(Meta 平台本质),gclid/msclkid/yclid → paid_search;utm_medium 仅在无点击参数时参与判定(label_only 证据)。
- `CHANNELS` +`paid_social`;utm_medium 规范化映射(写死):paid_search 强化集 {cpc, ppc, paidsearch, paid}、paid_social 判定集 {social, paid_social, social_ads, paidsocial}(需同时有 fb 家族 utm_source)。
- `CHANNELS` +`paid_social`;utm_medium 规范化映射(写死):**fb 家族 utm_source + 任意付费类 medium(cpc/ppc/paidsearch/paid/social/paid_social/social_ads/paidsocial)→ paid_social,此规则优先于强化集**(实测样本主导模式即 `utm_medium=paid&utm_source=fb`,gpt/14 §6.1 的 28 条 fb/paid——不放本规则则它们仍全落 paid_search,违背本节目的);其余付费 medium(无 fb source)→ paid_search 强化(label_only 证据)。
- **`ATTRIBUTION_RULE_VERSION` bump v1→v2** + 历史重算:`recompute_attribution` 管理命令**已存在**(management/commands/recompute_attribution.py,含 --site 参数与既有幂等测试 test_attribution.py:226 可参照),本任务只需 bump 版本 + 新渠道分支测试 + 线上重算执行。
- fb 家族 utm_source 判定复用既有广告家族归一表(132e2fe 引入的家族映射)。
- admin 中文标签同步:付费社交(渠道徽章/来源分布,grep 全部渠道显示点统一改)。
@@ -83,7 +84,7 @@
### 4.2 询盘漏斗卡(数据总览)
- 五级:浏览(page_view) → 咨询点击 → 表单开始 → 提交 → 邮件已发(**显示措辞"邮件已发(未验证)"**——客户端声明的诚实边界)。
- 五级:浏览(page_view) → 咨询点击 → 表单开始 → 提交 → 邮件已发(**显示措辞"邮件已发(未验证)"**——客户端声明的诚实边界)。首级用 page_view 而非上游文档的 product_view:product_view 为 inferred 推导会低漏计,page_view 是全量基线(偏离理由留档)。
- **口径:逐级独立 distinct session 计数**(各级 Count("session", distinct=True)),**非严格嵌套漏斗**——允许跳级真实路径(gpt/14 §5.4"允许直接进入表单");各级数字不嵌套,**UI 不显示级间百分比转化率**(避免嵌套误读),只显示各级计数。
- **全级过滤 `is_bot=False`**(Event.is_bot 既有标记——爬虫浏览/垃圾提交不灌漏斗)。
- 当日/近 7 天切换;新 API `GET /api/admin/funnel-summary?days=N`(site 权限沿既有)。