d2867826d3
CI / docs-build (push) Successful in 55s
- iteration-3 报告 01-08(PM 拆解/后端/Flutter/RC 首个 CERTIFIED/UI/埋点/证据基线/Git) - ADR-016 自托管 MinIO 起步预留迁云(用户确认现无云存储)、ADR-017 patbond-community :8084 + media 归 user + 作者信息跨 schema 只读、ADR-018 范围裁剪(话题剪出/单层评论)、 ADR-019 幂等按域(PUT/DELETE + request_hash)、ADR-020 聚合 feed_viewed/字典 v3/ 北极星不变/队列三项升第一波、ADR-021 Git 修订(main 更正/PR 情形触发/首发布重建 main/ 防泄漏 grep 先行) - mkdocs 挂「第三迭代」导航,build --strict 通过 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
468 lines
39 KiB
Markdown
468 lines
39 KiB
Markdown
# 第三迭代埋点与实验规划(社区)
|
||
|
||
> 角色:Experiment Tracker
|
||
> 日期:2026-09-08
|
||
> 前序:iteration-2 `06-experiment-tracking-plan.md`(字典 v2、北极星定义式、H1~H4、A/B 八项前置)、`15-analytics-persistent-queue.md`(分段持久化队列实况)、`24-event-whitelist-v2.md`、`29-m2-summary.md` §4(遗留与 M3 方向)、`30-device-verification-checklist.md`(真机补验挂起)
|
||
> 依据:`development-plan.md` 第 4 节 community 域、第 7 节 M3 验收、第 9 节「可观测性与产品验证」;`patbond-api` `EventDictionary.java` 现行白名单(v2,21 事件);`patbond-flutter` `lib/analytics/` 现状(SessionTracker / RouteObserver / 分段队列均已落地)
|
||
> 范围:M3 社区纵切(Feed、帖子、草稿/发布、媒体、评论、点赞、收藏、关注、话题);AI 创作、本地服务不在本轮定义
|
||
> 性质:纯规划文档,供 M3 开发工单直接引用;不含任何代码改动
|
||
|
||
**速览(五个核心结论)**:
|
||
|
||
1. 事件字典 v3 增量 **19 个新事件**(post 域 8 + feed 域 2 + 互动 8 + 实验基建 `experiment_exposed` 1),命名沿 v1/v2 惯例,结果编码进事件名,见 §1。
|
||
2. **Feed 曝光采用「浏览段聚合」设计,逐卡曝光事件被本角色否决**——量级重测表明:逐卡设计下 M2 的「零扩容」结论**不再成立**(1,000 DAU 约 7~14 个月击穿 5,000 万行分区阈值,且接收端限流实际未实现、快速滑动会形成无背压直写),聚合设计下**零扩容结论继续成立**,见 §2。
|
||
3. 新增 **4 条可证伪假设 H5~H8**(发布渗透率 / 发布漏斗完成率 / 社区-记录协同 / Feed 消费深度),H1~H4 出数日历与责任人落定(判定日 10-06 / 10-13 / 10-20),见 §3。
|
||
4. 北极星**建议 M3 保持「7 日回访记录率」不变**,社区复合指标不在本迭代引入;复评点设在 M3 收官、以 H7 读数为依据(**待拍板**),见 §4。
|
||
5. A/B 八项前置的 M3 推进计划:**6 项本迭代变绿 + 1 项部分变绿**(#7 的 feature flag 回滚随社区发布开关顺带落地、监控留 M4),维持「M3 末基本全绿、M4 首实验」路线,见 §5。
|
||
|
||
---
|
||
|
||
## 0. 基线现状(开工前核对)
|
||
|
||
M2 收官把 v2 规划的绝大部分落成了现实,本节只记与 M3 规划直接相关的事实。
|
||
|
||
| 项 | 现状 | 出处 |
|
||
| --- | --- | --- |
|
||
| 后端字典 | v2 共 21 事件:auth 11 + `page_viewed` 正稿 + pet 3 + health_record 6;`health_record_action` 已移除(ADR-013) | `EventDictionary.java` 实读 |
|
||
| 接收端 | `POST /api/v1/events` 批量 1–50、202 逐条、eventId 幂等、白名单剥离、红线拒绝;**64KB 上限与 429 限流均未实现**(契约据实未写,09 号 §出入 1/2) | 09 号报告 |
|
||
| 存储 | `platform.product_events` 不分区,分区阈值约 5,000 万行 | v1 报告 13 §2.3 |
|
||
| 客户端会话 | `session_tracker.dart` 已落地(冷启动/30 分钟规则);`analytics_route_observer.dart` + `page_viewed` 已挂全 | 15 号 / 24 号报告 |
|
||
| 客户端队列 | 分段持久化(500 条 / 25 段、at-least-once、批 ≤50);冲刷触发点 3/4:满 20 条、退后台、冷启动恢复——**30 秒定时器未做** | 15 号 §2.3/§4 |
|
||
| 队列遗留三项 | 30s 定时冲刷、退避/429(依赖后端先有限流)、anonymousId 持久化 | 29 号 §4 遗留 3 |
|
||
| 真机补验 | Android 落库观察 + SessionTracker 30min 手测挂起(0.5 天清单在 30 号报告)——**这是 A/B 前置 #1「数据质量验收」的拦路项** | 30 号报告 |
|
||
| pageName 实况 | 客户端枚举 13 个:字典 v2 初始 9 个 + 客户端自行补充 4 个(`create`/`pet_archive`/`services`/`post_detail`),后者**尚未同步进字典正稿** | `analytics_page_name.dart` 实读 |
|
||
| 假设与北极星 | H1~H4 判定线已冻结(观察窗自 2026-09-08 起算);北极星 SQL 与 §6 对账 SQL 已入档待巡检 | M2 06 号 §2/§3 |
|
||
|
||
**开工前必须知道的一件事**:M2 06 号 §7.1 曾以「限流 60 请求/5 分钟余量十几倍」论证零扩容,但 09 号契约回填核实该限流**从未实现**——接收端目前对客户端写入没有任何背压。这不改变 M2 量级下的结论(量太小),但 M3 引入首个高频事件后,**保护必须内建在事件设计里而不能指望限流兜底**,这是 §2 裁定聚合方案的硬前提之一。
|
||
|
||
---
|
||
|
||
## 1. 事件字典 v3 增量(community 域族)
|
||
|
||
### 1.1 沿用原则与域划分
|
||
|
||
命名 `<域>_<动作>_<结果>` snake_case、结果编码进事件名(`_succeeded`/`_failed`)、单义事件不设结果后缀(沿 `health_record_viewed`/`health_record_deleted` 先例)、`eventVersion` 起始 1、公共属性十项全带、属性 camelCase。v3 新增域前缀:
|
||
|
||
- **`post`**:帖子生命周期(创建、媒体、草稿、发布、删除)
|
||
- **`feed`**:Feed 消费(曝光聚合、加载失败)
|
||
- **`comment`**:评论
|
||
- **`user`**:关注关系(`user_followed`——关注的对象是用户,域按实体归 user;话题关注见 §1.6 缺口 3)
|
||
- 点赞/收藏归 **`post`** 域(作用对象是帖子)
|
||
|
||
设计纪律沿 v2 §1.2 的教训:**不设** `community_action(actionType)` 式多路复用事件——like/unlike/favorite/unfavorite 是四个语义独立的动作,各自独立成名,任何一个的枚举扩充不污染其他指标口径。
|
||
|
||
### 1.2 核心裁定:Feed 曝光用「浏览段聚合」,不做逐卡事件
|
||
|
||
这是 v3 最重要的一个设计决策,先给结论再给依据(量级数字在 §2 展开):
|
||
|
||
**`feed_viewed` 定义为「一个 Feed 浏览段」的聚合事件**:用户进入 Feed 页起累计计数,**离开时(路由跳走 / 退后台)发一条**,携带该段的曝光卡片数、翻页数、刷新数与停留时长。卡片「曝光」的客户端判定:卡片可见面积 ≥ 50% 且持续 ≥ 500ms,**同一浏览段内按 postId 去重**(postId 只在客户端内存里做去重键,**绝不上报**,上报的只有计数)。
|
||
|
||
否决逐卡方案(每张卡片可见发一条 `post_impression(postId)`)的四条理由:
|
||
|
||
1. **存储击穿**(§2.2):逐卡设计使「零扩容」结论失效,M3 就要启动分区改造——为一个当前没有消费方的数据形态提前付基建成本,不成立。
|
||
2. **无背压直写**:接收端限流未实现(§0),快速滑动可产生 5–10 卡/秒,客户端满 20 条即冲刷 ≈ 每 2–4 秒一个 HTTP 请求,无任何机制拦截这种放大。
|
||
3. **队列容量反噬**:500 条队列按 M2 量级可容两周离线积压,逐卡设计下缩水到 2~4 天,离线场景开始真实丢数据(丢最旧整段),反而伤害其他低频高价值事件。
|
||
4. **当前无消费方**:逐卡曝光的唯一刚需是「按帖子算曝光-点击率」供推荐排序实验用。M3 的 Feed 是游标分页的时序流、没有排序算法;等 M4+ 真做排序实验时,逐帖曝光的正确采集点是**服务端 Feed 下发日志**(server-side,天然全量、无客户端丢失率问题),而不是客户端埋点。此路线记入 backlog(§8 拍板 6),届时按需再评估分区与采样。
|
||
|
||
聚合方案的代价是丢失「单帖曝光→点进」归因,保留的是本迭代真正要回答的问题:**人们刷不刷、刷多深、刷完动不动手**(H8、H5 的数据源)——按需采集,不为想象中的分析囤数据。
|
||
|
||
### 1.3 隐私红线增量(社区内容是重灾区,在 v2 五条之上追加)
|
||
|
||
社区域的埋点只记**行为**不记**内容**,且社区首次引入「用户生成内容 + 用户间关系」,红线从严:
|
||
|
||
1. **帖子/评论正文**:任何自由文本禁止上报;文本规模用 `textLengthBucket` 枚举(`empty` / `short`(≤50) / `medium`(51–500) / `long`(>500)),不报精确字数。
|
||
2. **内容 ID 与用户 ID**:postId、commentId、topicId、被关注/被赞用户的 userId 一律不进 props(公共属性里的 userId 是**行为主体**自己,这是既有契约;**行为客体**的任何标识不上报)。逐卡曝光被否决后,v3 全部事件无一需要内容 ID。
|
||
3. **话题名**:话题是公开分类词但仍不上报名称(自建话题可能含用户自由文本),只报 `topicCount`;话题维度的内容分析走服务端事实表。
|
||
4. **媒体线索**:文件名、本地路径、URL 禁止;只允许 `mediaType` 枚举与 `sizeBucket` 枚举(`lt_1mb` / `mb_1_5` / `mb_5_20` / `gte_20mb`),不报精确字节数。
|
||
5. **pageName 归一化**(红线 5 延伸):`post_detail`、`topic_detail`、`user_profile` 等带参数路由,参数一律剥离,UUID 出现在 pageName/referrer 即验收失败。
|
||
|
||
红线正则本轮仍不扩(理由同 v2 §1.3);值级巡检(v2 §6.4 长度 >64 扫描)天然覆盖「正文塞进合法字段」的泄漏形态,继续每日跑。
|
||
|
||
### 1.4 新事件清单
|
||
|
||
失败枚举基底(v2 七项之上按 M3 验收新增):
|
||
|
||
- `content_rejected` — 内容审核/敏感词拒绝(**待拍板**:M3 是否有审核环节,无则删)
|
||
- `media_too_large` / `unsupported_format` — 媒体上传专用
|
||
- `not_found` 复用 — 目标帖子/评论已被删除(对应验收「删除内容不可继续出现」的客户端时序窗口)
|
||
|
||
#### 发布漏斗(post 域)
|
||
|
||
| 事件名 | 触发时机 | 专有属性 |
|
||
| --- | --- | --- |
|
||
| `post_create_started` | 进入发帖编辑器并产生**首次输入**(含首次选媒体),每次进入记一次 | `entryPoint`(`create_tab` / `feed` / `topic_detail` / `pet_detail`,待 UI 定稿收敛) |
|
||
| `post_draft_saved` | 草稿保存成功响应后;**仅手动保存与离开时保存**,若产品做打字自动保存,自动保存不埋(防高频) | `trigger`(`manual` / `on_exit`)、`mediaCount` |
|
||
| `post_publish_succeeded` | 发布接口成功响应后(**漏斗事件**,H5/H6 核心数据源) | `durationMs`(started→publish)、`mediaCount`、`topicCount`、`textLengthBucket`、`fromDraft`(bool) |
|
||
| `post_publish_failed` | 发布失败 / 超时 / 本地校验拦截 | `failureReason`、`errorCode`、`httpStatus`、`attemptSeq` |
|
||
| `post_deleted` | 删帖成功响应后(单事件风格,失败靠服务端错误率观测) | 无专有属性 |
|
||
|
||
`post_publish_failed.failureReason`:`validation_error`、`content_rejected`(待拍板)、`media_upload_incomplete`(有媒体未传完即点发布)、`rate_limited`、`network_error`、`server_error`。
|
||
|
||
#### 媒体上传漏斗(post 域,逐文件)
|
||
|
||
| 事件名 | 触发时机 | 专有属性 |
|
||
| --- | --- | --- |
|
||
| `post_media_upload_started` | 单个媒体文件开始上传 | `mediaType`(`image` / `video`)、`sizeBucket` |
|
||
| `post_media_upload_succeeded` | 单文件上传成功 | `mediaType`、`sizeBucket`、`durationMs` |
|
||
| `post_media_upload_failed` | 单文件失败 / 超时 / 用户取消 | `mediaType`、`sizeBucket`、`failureReason`、`errorCode`、`httpStatus`、`attemptSeq` |
|
||
|
||
逐文件(而非逐帖聚合)的理由:上传是发布漏斗预判的最大流失段(H6),失败归因需要文件粒度的 `sizeBucket × mediaType × failureReason` 交叉;量级无忧——单帖媒体数有产品上限(九宫格类,≤9),非高频。`failureReason`:`media_too_large`、`unsupported_format`、`network_error`、`server_error`、`cancelled`。
|
||
|
||
#### Feed 消费(feed 域)
|
||
|
||
| 事件名 | 触发时机 | 专有属性 |
|
||
| --- | --- | --- |
|
||
| `feed_viewed` | **离开 Feed**(路由跳走 / 退后台)时发一条,聚合本浏览段(§1.2 裁定) | `feedTab`(`home` / `topic` / `user_posts` / `favorites`,待 UI 定稿收敛)、`durationMs`、`impressionCount`(≥50% 可见 ≥500ms、段内按帖去重)、`loadMoreCount`(翻页次数)、`refreshCount`(下拉刷新次数) |
|
||
| `feed_load_failed` | 刷新或翻页请求失败(M3 验收「分页不丢失不重复」的客户端观测点) | `feedTab`、`loadType`(`refresh` / `load_more`)、`failureReason`、`errorCode`、`httpStatus` |
|
||
|
||
实现注意:`impressionCount` 去重集合只存活于浏览段内存中,段结束即弃;`durationMs` 用前台时长(退后台暂停计时),上限截断 30 分钟(防止挂机污染 H8)。
|
||
|
||
帖子详情**浏览**不设 `post_viewed`——由 `page_viewed(pageName=post_detail)` 覆盖(沿 v2 `pet_viewed` 不设的同一先例,防双事件重复计数)。
|
||
|
||
#### 互动(post / comment / user 域)
|
||
|
||
| 事件名 | 触发时机 | 专有属性 |
|
||
| --- | --- | --- |
|
||
| `post_liked` | 点赞成功响应后 | `source`(`feed` / `post_detail`) |
|
||
| `post_unliked` | 取消点赞成功响应后 | `source` |
|
||
| `post_favorited` | 收藏成功响应后 | `source` |
|
||
| `post_unfavorited` | 取消收藏成功响应后 | `source` |
|
||
| `comment_create_succeeded` | 评论提交成功响应后 | `durationMs`、`isReply`(bool,楼中楼)、`textLengthBucket` |
|
||
| `comment_create_failed` | 评论提交失败 | `failureReason`、`errorCode`、`httpStatus`、`attemptSeq` |
|
||
| `user_followed` | 关注成功响应后 | `source`(`post_detail` / `feed` / `user_profile` / `follow_list`) |
|
||
| `user_unfollowed` | 取关成功响应后 | `source` |
|
||
|
||
取舍说明(与 v2 同款自觉取舍,复活条件注明):
|
||
|
||
- **点赞/收藏/关注不埋失败**:幂等写入、单点交互,失败率靠服务端接口错误率观测(`health_record_deleted` 先例)。若乐观更新回滚率成为问题,届时以 eventVersion=2 增补 `_failed`。
|
||
- **评论不设 `comment_create_started`**:短表单,沿 v2「编辑不设 started」先例;评论放弃率若成为问题再增补。
|
||
- **like/unlike 分立而非 `action` 属性**:v2 §1.2 废弃 `health_record_action` 的同一逻辑——H5 的「互动用户」分母定义只引用语义单一的事件名。
|
||
|
||
#### 实验基建(platform 域,A/B 前置 #5 提前进字典)
|
||
|
||
| 事件名 | 触发时机 | 专有属性 |
|
||
| --- | --- | --- |
|
||
| `experiment_exposed` | 用户**实际到达**实验触点时(渲染了变体 UI),非分配时 | `experimentKey`(实验注册表枚举)、`variant` |
|
||
|
||
M4 首实验才启用,但字典与白名单**本迭代一次进**:M3 后端反正要动 `EventDictionary`,避免 M4 为一个事件再开一轮字典工单;客户端强类型封装同批出(可先无调用方)。这直接把 A/B 前置 #5 在 M3 变绿(§5)。
|
||
|
||
### 1.5 v3 增量总览(19 个新事件)
|
||
|
||
| # | 事件名 | 版本 | 性质 |
|
||
| --- | --- | --- | --- |
|
||
| 22 | `post_create_started` | 1 | 新增 |
|
||
| 23 | `post_draft_saved` | 1 | 新增 |
|
||
| 24 | `post_publish_succeeded` | 1 | 新增(漏斗事件) |
|
||
| 25 | `post_publish_failed` | 1 | 新增 |
|
||
| 26 | `post_deleted` | 1 | 新增 |
|
||
| 27 | `post_media_upload_started` | 1 | 新增 |
|
||
| 28 | `post_media_upload_succeeded` | 1 | 新增(漏斗事件) |
|
||
| 29 | `post_media_upload_failed` | 1 | 新增 |
|
||
| 30 | `feed_viewed` | 1 | 新增(聚合曝光,首个高频事件) |
|
||
| 31 | `feed_load_failed` | 1 | 新增 |
|
||
| 32 | `post_liked` | 1 | 新增 |
|
||
| 33 | `post_unliked` | 1 | 新增 |
|
||
| 34 | `post_favorited` | 1 | 新增 |
|
||
| 35 | `post_unfavorited` | 1 | 新增 |
|
||
| 36 | `comment_create_succeeded` | 1 | 新增 |
|
||
| 37 | `comment_create_failed` | 1 | 新增 |
|
||
| 38 | `user_followed` | 1 | 新增 |
|
||
| 39 | `user_unfollowed` | 1 | 新增 |
|
||
| 40 | `experiment_exposed` | 1 | 新增(M4 启用,字典先行) |
|
||
|
||
后端 `EventDictionary` 白名单增量(工单可直接抄):
|
||
|
||
```java
|
||
// v3 增量 post 域(iteration-3 报告 06 §1.4)
|
||
Map.entry("post_create_started", Set.of("entryPoint")),
|
||
Map.entry("post_draft_saved", Set.of("trigger", "mediaCount")),
|
||
Map.entry("post_publish_succeeded",
|
||
Set.of("durationMs", "mediaCount", "topicCount", "textLengthBucket", "fromDraft")),
|
||
Map.entry("post_publish_failed",
|
||
Set.of("failureReason", "errorCode", "httpStatus", "attemptSeq")),
|
||
Map.entry("post_deleted", Set.of()),
|
||
Map.entry("post_media_upload_started", Set.of("mediaType", "sizeBucket")),
|
||
Map.entry("post_media_upload_succeeded", Set.of("mediaType", "sizeBucket", "durationMs")),
|
||
Map.entry("post_media_upload_failed",
|
||
Set.of("mediaType", "sizeBucket", "failureReason", "errorCode", "httpStatus", "attemptSeq")),
|
||
// v3 增量 feed 域(聚合曝光设计,§1.2 裁定)
|
||
Map.entry("feed_viewed",
|
||
Set.of("feedTab", "durationMs", "impressionCount", "loadMoreCount", "refreshCount")),
|
||
Map.entry("feed_load_failed",
|
||
Set.of("feedTab", "loadType", "failureReason", "errorCode", "httpStatus")),
|
||
// v3 增量互动
|
||
Map.entry("post_liked", Set.of("source")),
|
||
Map.entry("post_unliked", Set.of("source")),
|
||
Map.entry("post_favorited", Set.of("source")),
|
||
Map.entry("post_unfavorited", Set.of("source")),
|
||
Map.entry("comment_create_succeeded", Set.of("durationMs", "isReply", "textLengthBucket")),
|
||
Map.entry("comment_create_failed",
|
||
Set.of("failureReason", "errorCode", "httpStatus", "attemptSeq")),
|
||
Map.entry("user_followed", Set.of("source")),
|
||
Map.entry("user_unfollowed", Set.of("source")),
|
||
// A/B 前置 #5:曝光事件字典先行,M4 启用(§1.4)
|
||
Map.entry("experiment_exposed", Set.of("experimentKey", "variant"))
|
||
```
|
||
|
||
Flutter 侧沿用强类型封装惯例:新建 `post_analytics.dart` / `feed_analytics.dart` / `community_interaction_analytics.dart`,枚举编译期锁死。
|
||
|
||
### 1.6 漏斗闭环与维度够用性复核
|
||
|
||
复核方法同 v2 §1.6:以 §3 假设与 M3 验收逐条反推数据源。
|
||
|
||
**闭环成立**:发布漏斗四段 `page_viewed(post_form) → post_create_started → post_publish_succeeded/failed`(媒体上传子漏斗嵌套其中,started→succeeded/failed 配对完整);Feed 消费闭环 `feed_viewed`(曝光量)→ `page_viewed(post_detail)`(点进)→ 互动事件。**发布漏斗的「到达→动笔」段由 pageName 新增 `post_form` 承接**(§6),与 v2 修订 1 的 `pet_form` 同构——这次在设计期就补上,不留缺口。
|
||
|
||
**缺口 1(接受不埋)**:逐帖曝光-点击归因——§1.2 已论证,M4+ 走服务端日志路线,backlog 登记。
|
||
|
||
**缺口 2(接受不埋)**:评论/帖子的浏览深度(评论区滚动)——`page_viewed(post_detail)` 足够回答「点进率」,评论区消费深度在排序实验之前无消费方。
|
||
|
||
**缺口 3(待拍板)**:话题关注——若 M3 UI 有「关注话题」按钮,需增补 `topic_followed/unfollowed(source)`(不报话题名,红线 3);UI 定稿前挂起(§8 拍板 5)。
|
||
|
||
**维度够用性**:H5 需互动/发布事件按 userId 去重(有);H6 需发布漏斗配对 + 媒体子漏斗(有);H7 需互动事件与 `health_record_create_succeeded` 的 userId + serverTs(有,跨域 join);H8 需 `feed_viewed.impressionCount/loadMoreCount`(有)。**全部假设可由 v3 字典 + community 事实表回答,判定通过。**
|
||
|
||
---
|
||
|
||
## 2. Feed 曝光量级评估与「零扩容」结论复核
|
||
|
||
### 2.1 v3 上线后的单用户日事件量重估
|
||
|
||
| 来源 | 条/DAU/日 | 说明 |
|
||
| --- | --- | --- |
|
||
| M2 存量(auth + page_viewed + pet/health_record) | 15–30 | v2 §7.1 估算,实测待巡检校准 |
|
||
| `page_viewed` 社区页面增量 | +5–10 | post_detail 点进是主要来源 |
|
||
| `feed_viewed`(聚合) | +3–8 | 每浏览段一条 |
|
||
| 互动(like/favorite/comment/follow 及 un-*) | +3–10 | 活跃互动者 |
|
||
| 发布漏斗 + 媒体 + 草稿 | +0.5–3 | 发布是低频动作(H5 预估 ≤10% 用户/周) |
|
||
| **合计** | **27–61** | **约 M2 的 2 倍** |
|
||
|
||
### 2.2 「零扩容」结论复核:聚合设计下成立,逐卡设计下不成立
|
||
|
||
**聚合设计(本方案)**:
|
||
|
||
- 接收端:61 条/日、满 20 条冲刷 ≈ 3–4 请求/日/用户,即便未来补 60 请求/5 分钟限流也有百倍余量。契约、批上限 50、接收逻辑**均不动**。
|
||
- 存储:1,000 DAU × 60 条 × 365 天 ≈ **2,200 万行/年**,距 5,000 万分区阈值仍有约 2 年余量。**不分区决策继续有效。**
|
||
- 队列:500 条 ≈ 8 天以上离线积压(vs M2 两周,可接受),**上限不调**。
|
||
- **结论:零改动,「零扩容」结论继续成立。**
|
||
|
||
**逐卡设计(被否决方案,留数字供复议)**:
|
||
|
||
- 活跃刷 Feed 用户 2–4 段/日 × 20–60 卡 ≈ 40–240 条曝光/日,总量升至 100–250 条/DAU/日。
|
||
- 存储:1,000 DAU 中位 ≈ 4,400 万行/年、上沿 ≈ 9,100 万行/年——**7~14 个月击穿分区阈值**,M3 就得启动分区 + 保留策略改造。
|
||
- 突发:快速滑动 5–10 卡/秒 → 每 2–4 秒满 20 条冲刷一次 → 单用户可达 75–150 请求/5 分钟;限流未实现(§0),这是对接收端和数据库的无背压直写。
|
||
- 队列:500 条仅容 2–4 天离线积压,挤压其他事件的 at-least-once 保障。
|
||
- **结论:逐卡设计使 M2「零扩容」结论失效**——这就是 §1.2 裁定的量化依据。
|
||
|
||
### 2.3 队列遗留三项的 M3 处置(优先级重排)
|
||
|
||
| 遗留项(29 号 §4) | M3 处置 | 理由 |
|
||
| --- | --- | --- |
|
||
| 30s 定时冲刷 | **本迭代第一波做**(P1) | 社区场景出现「长前台会话」(刷 Feed 半小时不切页),现有三触发点在这种会话里最多积压 19 条不上传;定时器同时改善当日监控的数据新鲜度。实现按 15 号 §4 既定方案。 |
|
||
| 退避 + 429 处理 | **客户端退避本迭代做**(5xx/网络错误指数退避 + 抖动);429 分支随后端限流落地一并做 | 后端限流是 09 号出入 5 项排期评估的一部分(后端侧决策);客户端 5xx 退避不依赖它,社区量级翻倍后重试风暴的伤害面变大,先行。 |
|
||
| anonymousId 持久化 | **本迭代做**(P2,一行级改动) | 现状每次冷启动新生成(`analytics_service.dart` 构造器 `Uuid().v4()`),登录前事件无法跨启动归并。A/B 前置 #4 的「登录前实验 anonymousId 分流」硬依赖持久化——M3 不做,M4 首实验若涉及注册/登录前触点就被卡住。 |
|
||
|
||
以上三项均为 `lib/analytics/` 内改动,与社区功能开发无耦合,建议与字典 v3 后端工单同批排入第一波。
|
||
|
||
---
|
||
|
||
## 3. 产品假设:H5~H8 新增 + H1~H4 出数日历
|
||
|
||
### 3.0 方法约定(沿 v2 §3,两点强调)
|
||
|
||
判定线上线前登记并 PM 会签冻结,届满出「支持 / 证伪 / 数据不足」三态判定。**H5~H8 的观察窗自社区功能对用户可用之日(下称 T0,随 M3 发布日落定)起算**,T0 后第 1 周为尝鲜噪声期,除 H6 外剔除。社区上线会扰动 M2 假设的在途窗口,处理纪律见 §3.2。
|
||
|
||
### H5:社区消费者远多于生产者,但生产者渗透率决定内容池成活(发布渗透假设)
|
||
|
||
- **陈述**:稳定期内,周活跃用户中当周产生 ≥1 条 `post_publish_succeeded` 的比例 ≥ 5%。
|
||
- **判定指标**:周去重 `userId`(post_publish_succeeded) / 周去重 userId(任意事件);辅助读数:互动渗透率(≥1 条 like/favorite/comment/follow 的占比)。
|
||
- **判定线**:支持 = ≥ 5%;证伪 = 连续 3 周 < 2%;2–5% 顺延。
|
||
- **窗口**:T0 后第 2–5 周。
|
||
- **行动**:证伪 → 发布门槛过高或动机不足,「从健康记录一键生成帖子」类降门槛引导进 A/B 候选池;不在 Feed 排序上浪费资源(内容池不成活时排序无意义)。支持 → 内容池自生长成立,资源投向消费侧(H8)。
|
||
|
||
### H6:媒体上传是发布漏斗的最大流失段(漏斗诊断假设)
|
||
|
||
- **陈述**:发布漏斗完成率(`post_publish_succeeded` / `post_create_started`,24h 归因窗)≥ 60%,且流失集中在含媒体的发布(含媒体发布的完成率比纯文字低 ≥ 15pp)。
|
||
- **判定指标**:漏斗配对 + `post_media_upload_failed` 按 `sizeBucket × failureReason` 分布交叉定位。
|
||
- **判定线**:支持 = 完成率 ≥ 60% 且媒体差 ≥ 15pp;证伪 = 完成率 < 40%(漏斗整体坏,另找原因)或媒体差 < 5pp(流失不在媒体段);其余顺延。
|
||
- **窗口**:T0 后第 1–4 周(**含噪声周**——漏斗诊断恰恰要看首批用户的失败形态,沿 H4 先例)。
|
||
- **行动**:支持 → 上传压缩/断点续传优化排 M4 前置;证伪且完成率低 → 按 failureReason 分布重新归因(validation_error 高则查表单/文案)。
|
||
|
||
### H7:社区活跃提升记录回访(社区-记录协同假设,北极星拍板的数据依据)
|
||
|
||
- **陈述**:首记后 7 日内产生过 ≥1 次社区互动(like/favorite/comment/follow/publish 任一成功事件)的用户,其 7 日回访记录率比无互动者高 ≥ 8pp。
|
||
- **判定指标**:北极星 SQL(M2 06 号 §2.1)按「窗口内是否有社区互动事件」分两群比较;回访事件仍只算 `health_record_create_succeeded`(社区行为只做分群、不充当回访,无循环)。
|
||
- **判定线**:支持 = 差值 ≥ 8pp 且两群各 ≥ 100 人;证伪 = 差值 < 3pp 或倒挂;3–8pp 顺延。
|
||
- **窗口**:T0 后 6 周(需 ≥ 2 个成熟队列)。
|
||
- **方法论警示**:观察性对照,只证相关(活跃用户本来什么都多做,自选择偏差与 H3 同款)。支持的正确用法是把「记录完成页引导分享到社区」列为 A/B 候选,用随机化坐实;同时它是 §4 北极星复评的核心输入——**若证伪(社区与记录是两个不相干场景),复合北极星的动议应就地终结**。
|
||
- **行动**:支持 → A/B 候选池 + 北极星复评启动;证伪 → 社区按独立场景运营,北极星保持记录型不再复议。
|
||
|
||
### H8:Feed 首屏之外仍有消费需求(内容供给/消费深度假设)
|
||
|
||
- **陈述**:稳定期内,≥ 40% 的 Feed 浏览段发生翻页(`feed_viewed.loadMoreCount` ≥ 1)。
|
||
- **判定指标**:翻页浏览段占比;辅助读数:浏览段 `impressionCount` 中位数、`durationMs` 分布(截断 30min,§1.4)。
|
||
- **判定线**:支持 = ≥ 40%;证伪 = 连续 3 周 < 20%;20–40% 顺延。
|
||
- **窗口**:T0 后第 2–5 周。
|
||
- **行动**:证伪 → 首屏即耗尽兴趣,指向内容供给不足(结合 H5 判定:若 H5 也证伪则是供给问题,运营/官方内容或 M4 AI 创作「一键发帖」提前;若 H5 支持则是分发问题);支持 → 游标分页体验(预加载、去重)投入合理,排序实验(M4+)有消费基础。
|
||
|
||
### 3.2 H1~H4 与北极星在 M3 期间的出数安排
|
||
|
||
M2 冻结的窗口自 2026-09-08 起算,判定日历与责任人如下(周节奏:**每周一**数据侧跑 M2 06 号 §6 全部对账 SQL + §2.1 北极星 SQL,本角色复核读数并记入巡检记录):
|
||
|
||
| 项 | 窗口 | 关键日期 | 跑数责任 | 判定责任 |
|
||
| --- | --- | --- | --- | --- |
|
||
| 北极星首个成熟周队列 | W37 队列(09-07~09-13 首记)+8 天成熟 | **2026-09-21(周一)首次出数**,此后每周一滚动 | 数据侧 | 本角色发布(Wilson 95% CI,<50 人周合并) |
|
||
| H4 激活链路 | 上线后 4 周(含第 1 周) | **2026-10-06 判定** | 数据侧 | 本角色 + PM 会签 |
|
||
| H2 多宠 | 上线后 4 周末读数 | **2026-10-06 判定**(`pet.pets` 真值侧) | 数据侧 | 同上 |
|
||
| H1 记录类型分布 | 第 2–5 周(09-15~10-12) | **2026-10-13 判定** | 数据侧(§6.2 SQL 即读数) | 同上 |
|
||
| H3 提醒-回访 | 6 周(≥2 成熟队列) | **2026-10-20 判定** | 数据侧 | 同上 |
|
||
|
||
**社区上线对在途窗口的污染纪律**:若 T0(社区发布日)落在 H1/H3 窗口内,判定线**不改**(冻结纪律),但读数发布时必须按 T0 前/后拆周标注;H3 若前后两段方向不一致,判定记「数据不足-顺延」并注明混杂因素,不得挑一段下结论。H7 的对照组恰好提供了交叉检验。
|
||
|
||
**前置风险**:H1~H4 与北极星的一切读数都以真实事件流入库为前提。真机补验(30 号清单)未完成前,Android 端数据可信度未验证——**补验必须在 09-21 首次北极星出数前完成**,否则首批读数只能标「未验收数据,仅供方向参考」。
|
||
|
||
---
|
||
|
||
## 4. 北极星:M3 保持「7 日回访记录率」,不引入社区复合指标(待拍板)
|
||
|
||
社区上线后「北极星要不要变」是必答题。本角色立场:**M3 全程保持现北极星不变**,理由三条:
|
||
|
||
1. **基线刚建立,换指标即断线**。北极星 09-21 才出第一个成熟队列读数,M3 期间总共只会积累 4~6 个可比周。此时切换或掺入社区成分,等于永远失去「社区上线前后」这组最有价值的对照——北极星的首要职责是跨迭代可比。
|
||
2. **新功能光环效应会系统性高估社区成分**。任何复合指标(如「7 日回访有效行为率 = 记录或发帖或互动」)在社区上线后前几周必然被尝鲜流量冲高,读数好看但不可解释,恰好违背 v2 选 A 弃 B 的原始理由(拒绝易被一次性行为冲高的指标)。
|
||
3. **「社区是否服务于留存」本身是待验假设,不是前提**。这正是 H7 的问题。把社区写进北极星等于未经验证就宣布答案。正确顺序:H7 出数(T0+6 周)→ 若支持且 A/B 坐实,M4 起再评估复合式(候选形态:分子扩为「记录 或 发布」,互动类行为因信号太弱不入分子);若 H7 证伪,动议终结。
|
||
|
||
**落定为拍板项**(§8 拍板 1):M3 保持不变,复评点 = M3 收官会 + H7 读数;PM 保留否决权,否决须给出替代定义式与断线代价的处置方案。社区侧的健康度用**辅助指标层**观测(不升格):周发布渗透率(H5 口径)、周互动渗透率、Feed 翻页率(H8 口径)——三者随 §7 巡检周报发布。
|
||
|
||
---
|
||
|
||
## 5. A/B 八项前置的 M3 推进计划
|
||
|
||
M2 06 号 §4.2 立的路线是「M3 末全绿、M4 首实验」。逐项落定 M3 的动作与责任侧:
|
||
|
||
| # | 前置条件 | M3 动作 | 责任侧 | M3 末预期 |
|
||
| --- | --- | --- | --- | --- |
|
||
| 1 | 数据质量验收 | 真机补验(30 号清单,0.5 天)→ M2 字典 v2 事件 2 周巡检达标(丢失 <5%、对账偏差 <5%、去重 <10%、serverTs 100%、无红线泄漏) | 数据 + 真机执行人 | **绿**(拦路项是真机,见 §3.2 风险) |
|
||
| 2 | 指标基线 | 北极星 + M2 漏斗连续 ≥2 周稳定产出(09-21 起自然达成),留档均值与方差 | 数据 | **绿** |
|
||
| 3 | 样本量规则成文 | 基线率 × MDE × α=0.05 × 功效 80% 的计算方法 + 查表 + 按实测 DAU 换算最短运行时长;以 §3 实测基线代入(不再用 M2 的假设值) | 本角色 | **绿**(M3 中交付) |
|
||
| 4 | 稳定分流组件 | `hash(userId, experimentSalt) % buckets` 后端组件 + anonymousId 持久化(§2.3,登录前分流的前提)+ 登录后归并规则成文 | 后端(归并规则:本角色) | **绿** |
|
||
| 5 | 曝光事件 | `experiment_exposed` 已随 v3 进字典(§1.4);Flutter 强类型封装同批出 | 后端 + Flutter | **绿**(本报告已完成设计) |
|
||
| 6 | 实验设计模板与评审流程 | 模板(假设/主指标/护栏/提前停止规则/多重比较约定)+ 评审流程成文;与 #3 同一文档交付 | 本角色 | **绿** |
|
||
| 7 | 护栏监控与回滚 | feature flag 开关机制随「社区功能发布开关」顺带落地(社区本就该有开关灰度);护栏**准实时监控**留 M4(依赖监控设施选型) | 后端/DevOps | **部分绿**(回滚绿、监控 M4) |
|
||
| 8 | 隐私合规复核 | 每实验一次,常态项 | 每实验 | 常态 |
|
||
|
||
**结论:M3 末 6 项全绿 + #7 部分绿,M4 初补齐监控即可启动首实验。** 首实验候选池按判定结果动态排序:H3 支持 →「默认引导创建提醒」;H4 证伪 →「建宠成功页引导首条记录」;H7 支持 →「记录完成页引导分享社区」;H5 证伪 →「记录一键生成帖子」。届时按 #3 的样本量规则做可行性检验(运行 >8 周即判不可行,退回观察,止损线预登记——沿 M2 §4.3 纪律)。
|
||
|
||
---
|
||
|
||
## 6. pageName 枚举增量与 page_viewed 覆盖检查
|
||
|
||
### 6.1 增量清单
|
||
|
||
现状(§0):字典正稿 9 个 + 客户端已自行补充 4 个未同步正稿。v3 一次收编 + 社区族增量:
|
||
|
||
| pageName | 性质 | 说明 |
|
||
| --- | --- | --- |
|
||
| `create` / `pet_archive` / `services` / `post_detail` | **收编转正**(客户端已存在) | 补进字典说明,消除枚举双源 |
|
||
| `post_form` | 新增 | 发帖编辑器——发布漏斗「到达段」承接者(§1.6),对应 v2 的 `pet_form` 教训,设计期即补 |
|
||
| `topic_list` | 新增 | 话题列表/广场 |
|
||
| `topic_detail` | 新增 | 话题详情(含话题内 Feed;话题 ID 剥离) |
|
||
| `user_profile` | 新增 | **他人**主页(自己的主页仍是 `profile`,两者语义不同不合并;用户 ID 剥离) |
|
||
| `follower_list` / `following_list` | 新增 | 粉丝/关注列表分立(关注关系的两个方向是不同页面) |
|
||
| `favorite_list` | 新增 | 我的收藏 |
|
||
| `draft_list` | 新增 | 草稿箱 |
|
||
|
||
共 **9 个新增 + 4 个收编**。Feed 本体不新增 pageName:首页 Tab 即 Feed,沿用 `home`(pageName 保持导航语义,Feed 消费的度量职责已由 `feed_viewed` 承担,避免一次改名断掉 M2 以来的 `home` 时序)。终稿在社区 UI 定稿后由 UI + 本角色对齐一次(§8 拍板 4)。
|
||
|
||
后端零改动提示:`page_viewed` 白名单只校验 props **键**(pageName/referrer),值级枚举由客户端编译期锁死 + 离线巡检兜底——pageName 增量**不需要动 EventDictionary**,只改 `analytics_page_name.dart` 与字典文档。
|
||
|
||
### 6.2 覆盖检查(社区页面族接线的验收 sanity)
|
||
|
||
沿 v2 §5.2/§6.3.2 框架,社区族新增三条关系式(数据侧入每日巡检):
|
||
|
||
1. **互动必有承载页**:产生过互动事件(like/favorite/comment/follow)的 session 必有 ≥1 条 `page_viewed`(pageName ∈ {home, post_detail, topic_detail, user_profile})。偏差 >5% = 社区页面路由漏挂。
|
||
2. **曝光先于点进**:日 `feed_viewed.impressionCount` 总和 ≥ 日 `page_viewed(pageName=post_detail)` 条数(点进的帖子必先曝光;深链/推送入口出现前该式恒成立,破式即 `feed_viewed` 聚合逻辑漏计)。
|
||
3. **发布必经编辑器**:日 `post_publish_succeeded + post_publish_failed` ≤ 日 `page_viewed(pageName=post_form)`(发布尝试必先到达编辑器)。
|
||
|
||
---
|
||
|
||
## 7. 对账 SQL v3 增量(真值:community 事实表)
|
||
|
||
v1/v2 巡检全部继续。表名以 M3 后端 DDL 定稿为准,下文假定 `community` schema(`community.posts`、`community.comments`、`community.post_likes`、`community.post_favorites`、`community.follows`),命名不同替换即可。
|
||
|
||
### 7.1 发布对账(H5 真值侧)
|
||
|
||
`post_publish_succeeded` 事件数 vs `community.posts` 当日新建行数(排除草稿态),UTC 日界,偏差 >5% 告警——结构同 v2 §6.1,替换事件名与表名即可,不重抄。评论对账同构(`comment_create_succeeded` vs `community.comments`)。
|
||
|
||
### 7.2 互动净值对账(点赞/收藏的 toggle 语义专用)
|
||
|
||
like/unlike 是幂等 toggle,事实表存的是**净状态**,逐日计数对账不成立,改对**净增量**:
|
||
|
||
```sql
|
||
-- 日 (post_liked - post_unliked) 事件净值 vs community.post_likes 当日净增行数
|
||
WITH evt AS (
|
||
SELECT date_trunc('day', server_ts AT TIME ZONE 'UTC') AS day,
|
||
count(*) FILTER (WHERE event_name = 'post_liked')
|
||
- count(*) FILTER (WHERE event_name = 'post_unliked') AS evt_net
|
||
FROM platform.product_events
|
||
WHERE event_name IN ('post_liked', 'post_unliked')
|
||
GROUP BY 1
|
||
)
|
||
SELECT e.day, e.evt_net, a.api_net,
|
||
abs(e.evt_net - a.api_net) AS diff_abs -- 相对偏差对净值无意义,看绝对差趋势
|
||
FROM evt e
|
||
JOIN (SELECT date_trunc('day', created_at AT TIME ZONE 'UTC') AS day,
|
||
count(*) AS api_net -- 若删行实现取消,需改为审计表/净增视图,DDL 定稿后校准
|
||
FROM community.post_likes GROUP BY 1) a USING (day)
|
||
ORDER BY e.day;
|
||
```
|
||
|
||
(若后端用删行实现取消点赞,`api_net` 须改从审计日志或快照差分取数——DDL 定稿后由数据侧校准,此处登记口径意图。收藏、关注同构。)
|
||
|
||
### 7.3 feed_viewed 自洽巡检(聚合事件的质量门)
|
||
|
||
聚合事件一旦逻辑有 bug,坏的是整段计数,须专设 sanity:
|
||
|
||
```sql
|
||
SELECT date_trunc('day', server_ts AT TIME ZONE 'UTC') AS day,
|
||
count(*) AS segments,
|
||
count(*) FILTER (WHERE (props->>'impressionCount')::int = 0
|
||
AND (props->>'durationMs')::int > 10000) AS zero_imp_long_stay,
|
||
-- 停留超 10s 却零曝光 = 曝光判定逻辑失效,>1% 告警
|
||
count(*) FILTER (WHERE (props->>'durationMs')::int > 1800000) AS over_cap
|
||
-- durationMs 超 30min 截断上限 = 计时暂停逻辑失效,期望恒 0
|
||
FROM platform.product_events
|
||
WHERE event_name = 'feed_viewed'
|
||
GROUP BY 1 ORDER BY 1;
|
||
```
|
||
|
||
### 7.4 巡检节奏汇总
|
||
|
||
- **每日**:v1/v2 既有全部 + §7.1~7.3 + 值级泄漏扫描(v2 §6.4,社区正文是新的高风险源)。
|
||
- **每周一**:北极星 + H 假设读数(§3.2 日历);辅助指标层三项(§4)随周报发布。
|
||
|
||
---
|
||
|
||
## 8. 待拍板清单(汇总)
|
||
|
||
| # | 事项 | 选项 | 本角色裁定/建议 |
|
||
| --- | --- | --- | --- |
|
||
| 1 | 北极星是否随社区调整 | 保持 7 日回访记录率 / 引入社区复合指标 | **建议保持**,复评点 = M3 收官 + H7 读数(论证 §4);PM 否决须给替代定义式与断线处置 |
|
||
| 2 | H5~H8 判定线 | §3 各阈值 | T0 前 PM 会签一次,会签后冻结(同 H1~H4 纪律) |
|
||
| 3 | `content_rejected` 枚举 | M3 是否有内容审核环节 | 有则保留,无则从枚举删(发布/评论两处) |
|
||
| 4 | `entryPoint`/`feedTab`/pageName 终稿 | 待社区 UI 定稿收敛 | 埋点工单开工前 UI + 本角色对齐一次(含拍板 5) |
|
||
| 5 | 话题关注事件 | UI 有「关注话题」则增补 `topic_followed/unfollowed` | 按 UI 定稿定(§1.6 缺口 3) |
|
||
| 6 | 逐帖曝光路线 | 本迭代不做(§1.2 裁定);M4+ 排序实验若立项,走服务端 Feed 下发日志 | backlog 登记,届时同评分区与采样 |
|
||
| 7 | 后端限流排期 | 09 号出入 5 项之一 | 建议 M3 排入(客户端 429 分支在等它,§2.3);非本角色职权,仅登记依赖 |
|
||
| 8 | 真机补验时限 | 30 号清单 0.5 天 | **建议 09-21 前完成**(北极星首次出数的数据可信前提,§3.2 风险) |
|
||
|
||
---
|
||
|
||
## 附:M3 埋点工单拆分建议(按依赖排序)
|
||
|
||
1. **真机补验**(30 号清单,独立于开发,越早越好——拍板 8)。
|
||
2. **Flutter 队列三小修**(§2.3:30s 定时器、5xx 退避、anonymousId 持久化)——`lib/analytics/` 内闭环,先于社区新事件。
|
||
3. **后端**:`EventDictionary` v3 增量 19 事件(§1.5 代码块可直抄,含 `experiment_exposed`)+ 集成测试;可与 2 并行。
|
||
4. **Flutter**:pageName 增量与收编(§6.1,`analytics_page_name.dart`)+ 社区页面族路由挂接。
|
||
5. **Flutter**:社区功能开发时按 §1.4 挂接(强类型封装先行;`feed_viewed` 的浏览段聚合器建议独立类 + 单测覆盖曝光判定/去重/计时暂停/30min 截断)。
|
||
6. **数据**:§7 对账 SQL 入巡检(7.2 口径待 DDL 定稿校准);§6.2 三条覆盖 sanity 随社区页面上线启用。
|
||
7. **后端**:分流组件 + 归并规则(A/B 前置 #4,§5)。
|
||
8. **本角色**:H5~H8 判定线 T0 前会签冻结(拍板 2);样本量规则 + 实验设计模板文档(前置 #3/#6)M3 中交付;每周一北极星/假设读数复核(§3.2 日历)。
|