docs: 采集阶段B设计文档——零网站改动业务事件体系(含两轮自审17项修正)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# 采集阶段B:业务事件体系(零网站改动)— 设计文档
|
||||
|
||||
日期:2026-09-18
|
||||
分支:m1
|
||||
状态:设计已获用户批准(含两轮自审 17 项修正)
|
||||
依据: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)、悬浮聊天组件
|
||||
|
||||
## 核心约束与决策
|
||||
|
||||
1. **零网站改动**:hesucar WordPress 不加任何代码。表单服务端确认通道**只做契约预留**(阶段C 接入)。
|
||||
2. SDK 自动监听(内置埋点),不用 track 手动调用承载业务事件。
|
||||
3. 事件全部携带阶段A 归属四元组(PageSession.attribution())。
|
||||
4. 范围:contact_click / form_*(CF7 全过程)/ product_view(URL 推导)/ track 通道修复 / D06 归因扩展(paid_social + utm_medium)/ admin 时间线新事件 / 询盘漏斗最小视图。
|
||||
5. 排除:服务端确认实现(阶段C)、次批事件(scroll_depth/section_view/media_progress 等)、sdk_diag、capabilities、完整漏斗报表、导出对账。
|
||||
|
||||
## 1. SDK(0.2.0 → 0.2.1):新模块 `sdk/src/business.ts`
|
||||
|
||||
与 page.ts/stay.ts 同模式,init 时安装;所有回调 try/catch 静默(红线)。
|
||||
|
||||
### 1.1 contact_click
|
||||
|
||||
- document 级点击委托(capture):命中链接 `wa.me / api.whatsapp.com / whatsapp://` → `channel:"whatsapp"`;`tel:` → `"phone"`;`mailto:` → `"email"`。属性仅 `{channel}`(枚举;placement 已砍——YAGNI)。
|
||||
- 点击后立即 `transport.flush(true)`(beacon)——跳转窗口内保证发出。
|
||||
- **known-limitation**:非链接按钮(如悬浮组件 Get Quotation)无标准特征,不识别不硬造。
|
||||
- 语义:只证明点击意图,不宣称联系成功(OT09)。
|
||||
|
||||
### 1.2 form_*(Contact Form 7 全过程)
|
||||
|
||||
监听 CF7 DOM 事件(冒泡至 document)。**事件名/载荷以实测与源码为准**(见 §6 验证路径),设计按 CF7 5.x/6.x 已知形态:`wpcf7submit / wpcf7invalid / wpcf7spam / wpcf7mailsent / wpcf7mailfailed`。
|
||||
|
||||
| 事件 | 触发 | 属性 |
|
||||
|---|---|---|
|
||||
| form_start | .wpcf7-form 内首次 isTrusted focusin/input(每表单实例一次) | form_id |
|
||||
| form_submit | wpcf7submit(**每次尝试**) | form_id, submission_id(当次新生成 uuid) |
|
||||
| form_error | wpcf7invalid / wpcf7spam | form_id, submission_id, error_code(CF7 status 规整枚举), field_count |
|
||||
| form_submit_result | wpcf7mailsent → result_code:"mail_sent";wpcf7mailfailed → "mail_failed" | form_id, submission_id, result_code |
|
||||
|
||||
- **submission_id 语义**:一次提交尝试 = 一个 ID(form_submit 时生成;result 引用最近一次的 ID)。form_start 不生成。
|
||||
- **form_id 规整**:从 CF7 unit_tag/action 解析表单编号 → `cf7-{ID}`(如 cf7-156),不暴露完整内部串。
|
||||
- **不采任何输入值**(OT10);error_code 只取状态码 + 字段数,不采 invalid_fields 明细。
|
||||
- form_submit_result 是**客户端声明**(事件名本身表达,不升级为可信确认)。
|
||||
|
||||
### 1.3 product_view
|
||||
|
||||
- 规则:path 匹配 `/product/{slug}/`(pattern 内置默认,init 可覆盖——为未来站点预留,hesucar 用默认)。
|
||||
- 属性:`{product_id: slug, inferred: true, rule_version: "v1"}`。多语言同产品无法关联——诚实标注(inferred 降级路径,gpt/14 §5.1)。
|
||||
- **触发时机**:init 时同步判定 + SPA 切段回调。PageSession 新增 `onSegmentChange(fn)` 钩子(与 onClose 对称;**单点 hook history**,business.ts 注册回调判定新段 path,不重复 wrap)。
|
||||
- PV 不双计:服务端 PV 统计已有 event_name 过滤(87261ab)。
|
||||
|
||||
### 1.4 track 通道修复(阶段A 终审 I-1)
|
||||
|
||||
- `ctk('track', name, props)`:transport.push 前用 `getPageSession()?.attribution()` 补归属顶层字段;事件名必须在服务端白名单内(白名单扩后合法名可通过)。
|
||||
- **约定**:自动监听的事件(contact_click/form_*/product_view)勿手动 track,否则双发。
|
||||
|
||||
### 1.5 隐藏字段 `_ctk_sid`(服务端确认契约预留的浏览器侧半边)
|
||||
|
||||
- form_start 时向表单注入 `<input type="hidden" name="_ctk_sid" value="{submission_id}">`——**注意**:submission_id 在 form_submit 才生成,故注入策略:focusin 时注入占位(空值或预生成 ID),form_submit 事件触发时**更新**该字段值为当次 submission_id(CF7 的 AJAX FormData 构造在 wpcf7submit 事件之后的同步流程内,更新可见性以实测为准,不可靠则该次对齐缺失)。
|
||||
- 作用:CF7 提交 POST 天然携带,阶段C 的 WP mu-plugin 从 `$_POST['_ctk_sid']` 读取对齐。CF7 忽略未注册字段、正常传输——对现有表单行为无害。
|
||||
- known-limitation:无任何交互直接提交的极端场景,submission_id 可能为空(事件与漏斗不受损,仅确认对齐缺失)。
|
||||
|
||||
## 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 或空串;`product_id` slug 格式(复用清洗 `_SAFE_VALUE` 同款正则);`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)。
|
||||
- **`ATTRIBUTION_RULE_VERSION` bump v1→v2** + 历史重算:**计划第一步先验证 recompute 管理命令是否存在**(constants.py 注释提及约定但命令本体未核验)——不存在则本任务含实现(按 site 幂等重算 SourceClassification)。
|
||||
- admin 中文标签同步:付费社交(渠道徽章/来源分布,grep 全部渠道显示点统一改)。
|
||||
|
||||
## 4. Admin 前端
|
||||
|
||||
### 4.1 时间线新事件展示
|
||||
|
||||
- EventTimeline/访客详情的事件类型映射扩展:contact_click(咨询点击 💬+channel)、form_start/submit/error/result(表单各阶段 📋+关键属性)、product_view(产品查看 🚗+product_id)——具体图标按现有组件风格。
|
||||
- 新事件自动进时间线(timeline API 原样返回),仅前端徽章/文案工作。
|
||||
|
||||
### 4.2 询盘漏斗卡(数据总览)
|
||||
|
||||
- 五级:浏览(page_view) → 咨询点击 → 表单开始 → 提交 → 邮件已发(**显示措辞"邮件已发(未验证)"**——客户端声明的诚实边界)。
|
||||
- **每级按 distinct session 计数**(Count("session", distinct=True),同会话多次点击算一次到达——不做则漏斗被刷新灌水无意义)。
|
||||
- 当日/近 7 天切换;新 API `GET /api/admin/funnel-summary?days=N`(site 权限沿既有)。
|
||||
- 卡片位置随 dashboard 现有排版(趋势/渠道卡附近),实现时看布局。
|
||||
|
||||
## 5. 服务端确认——契约预留(不实现)
|
||||
|
||||
spec 附录写死(阶段C 直接照做,客迹侧零改动):
|
||||
|
||||
1. submission_id:uuid v4,form_submit 生成,`_ctk_sid` 隐藏字段随 POST 传输(§1.5)。
|
||||
2. 认证端点:`POST /api/integrations/form-confirm`,站点级密钥(HMAC 签名 + 时间窗 ±5min + nonce 防重放 + submission_id 幂等);事件 form_submission_confirmed(白名单届时加入)。
|
||||
3. WordPress mu-plugin 示例(约 20 行 PHP):hook `wpcf7_mail_sent` → 读 `$_POST['_ctk_sid']` + 表单摘要 → 签名转发。
|
||||
4. 浏览器事件缺失时服务器确认仍成立(业务事实保留,浏览轨迹标缺);同 ID 重试不重复计数,内容变化报冲突。
|
||||
|
||||
## 6. 验证与部署
|
||||
|
||||
- **CF7 事实核验(实现第一步)**:读 CF7 6.1.6 公开源码(wp-content/plugins/contact-form-7/includes/js/index.js)确认事件 dispatch 名/payload/FormData 时序;Playwright 在 hesucar 空表单提交实测 invalid+submit 路径(无副作用)。**不真提交验证 mailsent**(会给商家发邮件)——按源码实现,上线后以第一封真实询盘闭环验证。
|
||||
- SDK 单测:点击委托命中/漏判、CF7 事件模拟、submission_id 每次尝试独立、form_start 去重、pattern 匹配、onSegmentChange、track 补归属。
|
||||
- server 测试:6 新事件校验(枚举/格式)、漏斗 API distinct session 口径、归因 paid_social/utm_medium 分支、规则 v2 重算幂等。
|
||||
- admin:时间线渲染新事件、漏斗卡数据正确。
|
||||
- **部署顺序**:server → admin → SDK 0.2.1(SDK 先上会被白名单拒);**整体排在 9/20 观察窗 D02 确认之后**(开发期自然覆盖,非阻塞开发)。
|
||||
- 线上全链路:Playwright 点 hesucar WhatsApp 链接/tel/mailto → 事件落库;空表单提交 → form_start+submit+error 落库。
|
||||
|
||||
## 7. 风险与边界
|
||||
|
||||
- CF7 事件形态按公开源码+invalid 实测锚定,mailsent 分支上线后以真询盘验证(诚实标注)。
|
||||
- `_ctk_sid` 注入对表单无行为影响(CF7 忽略未注册字段);submit 时更新值的可见性受 CF7 内部时序影响,不可靠场景该次对齐缺失(known-limitation)。
|
||||
- product_view 为 inferred URL 推导:跨语言同产品不关联、URL 结构变化需 bump rule_version。
|
||||
- track 与自动监听双发:靠文档约定(不强制拦截),RawBatch 可诊断。
|
||||
- 询盘漏斗末级为客户端声明(未验证措辞),阶段C 接确认通道后升级为"已验证询盘"。
|
||||
Reference in New Issue
Block a user