Files
patbond-doc/docs/development/iterations/iteration-2/24-event-whitelist-v2.md
T
lixi b81c050e03
CI / docs-build (push) Successful in 58s
docs: M2 第三波收口——报告 22~27 入档挂导航
- 22~26 Flutter 接入五单报告(T2-11~14 + 白名单扩充,flutter 测试 64→272)
- 27 第三波收口总表:demo 数据消亡、四态纪律、埋点端到端贯通、DEBT-1 偿还
- 三次 compose 实测无契约偏差;波内 agent 中断续跑事故记录在案

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 14:10:36 +08:00

82 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 24 · 事件白名单 v2 扩充(T2-17 后端半边)
> 依据:`06-experiment-tracking-plan.md` §1.4/§1.5(事件字典 v2 增量)、§5.2page_viewed 转正稿)、§6.4(值级巡检);ADR-013health_record_action 移除,dev@58576f8
>
> 交付:`patbond-api` dev@`64c9b72``patbond-user` 模块 analytics 包,3 文件,+216/9
## 1. 结论速览
| 项 | 结果 |
| --- | --- |
| 新增白名单事件 | 10 个(pet 域 3 + health_record 域 7),props 键集与 06 号 §1.5 可直抄块逐条一致 |
| page_viewed 转正核对 | **一致,零修正**:现行白名单已是 `Set.of("pageName", "referrer")`,与 v2 正稿键集相同;仅更新注释标注正稿地位与 pageName 枚举(含 §1.6 修订的 `pet_form` |
| health_record_action | 保持移除(ADR-013),新增集成测试锁定其仍被 `unknown_event_name` 拒绝 |
| 测试数 | 182 → **191**(+9:字典边界 5 + 接收端集成 4),`mvnw clean test` 全绿 |
| 契约变更 | **无需**`openapi.yaml` 的 events 契约对事件名开放(字符串 + 后端字典校验),本次未触碰 |
## 2. 新增事件与 props 对照(vs 06 号 §1.4/§1.5
`EventDictionary.java``patbond-user/src/main/java/com/patbond/patbond/user/analytics/``WHITELIST` 增量,逐条对照字典 v2
### 2.1 pet 域(3 事件)
| 事件名 | 白名单 props | 与 06 号 §1.5 |
| --- | --- | --- |
| `pet_create_started` | `entryPoint` | 一致 |
| `pet_create_succeeded` | `durationMs``species``petIndex` | 一致 |
| `pet_create_failed` | `failureReason``errorCode``httpStatus``attemptSeq` | 一致 |
### 2.2 health_record 域(7 事件)
| 事件名 | 白名单 props | 与 06 号 §1.5 |
| --- | --- | --- |
| `health_record_create_started` | `recordType``entryPoint` | 一致 |
| `health_record_create_succeeded` | `recordType``durationMs``photoCount` | 一致 |
| `health_record_create_failed` | `recordType``failureReason``errorCode``httpStatus``attemptSeq` | 一致 |
| `health_record_viewed` | `recordType``source` | 一致 |
| `health_record_edit_succeeded` | `recordType``fieldCount` | 一致 |
| `health_record_edit_failed` | `recordType``failureReason``errorCode``httpStatus`(无 `attemptSeq`,正稿如此) | 一致 |
| `health_record_deleted` | `recordType` | 一致 |
### 2.3 page_viewed 转正核对
现行条目 `Map.entry("page_viewed", Set.of("pageName", "referrer"))` 与 v2 正稿(§5.2)键集**完全一致,无需修正**。差异只在语义层:v2 要求 pageName 为编译期枚举(`login/register/home/profile/pet_list/pet_detail/pet_form/record_form/record_detail`)——这是客户端约束(T2-17 Flutter 半边)+ §6.4 值级巡检的职责,后端键级白名单结构不承载值枚举(见 §3)。已将枚举全集写入 `EventDictionary` 类注释作字典说明。
## 3. 枚举值的校验边界(设计决策,沿用现行架构)
当前 `EventDictionary` 是**键级白名单**(白名单外键剥离、红线键拒绝、未知事件名拒绝),不做值级枚举校验。v2 的 `recordType``weight/vaccine/health_event/reminder`)、失败枚举(含 `permission_denied/conflict/not_found`)、`pageName` 枚举维持同一分层:
1. **客户端编译期枚举**是第一道约束(06 号 §5.2 明确 pageName 为「编译期枚举」;recordType 同理);
2. **接收端只校验键**——枚举外的值(如 `recordType: "grooming"`**过 ingest 不拒绝**,由 §6.4 值级巡检 SQL 兜底发现。06 号 §1.5 的「可直抄」Java 块本身就是纯键集,本实现与其逐字一致,未擅自加严接收契约(加严会使客户端枚举漂移时整条事件丢失,与 §5.2 第 4 条「宁可不上报、不要报错名」的防洪水思路相悖)。
此边界已用集成测试 `enumOutRecordTypeValuePassesIngestForOfflinePatrol` 显式锁定为文档化行为,避免后人误当漏洞「修复」。
## 4. 测试增量(182 → 191,全绿)
`JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw clean test`:总计 191failures 0errors 0。
**`EventDictionaryTest`3 → 8+5**
| 测试 | 边界 |
| --- | --- |
| `v2PetDomainEventsMatchDictionary` | pet 域 3 事件 props 键集 `containsExactlyInAnyOrder` 全矩阵 |
| `v2HealthRecordCreateFunnelMatchesDictionary` | 创建漏斗 3 事件键集全矩阵 |
| `v2HealthRecordLifecycleEventsMatchDictionary` | viewed/edit/deleted 4 事件键集(含锁定 edit_failed 无 attemptSeq |
| `pageViewedFormalizedPropsAreExactlyPageNameAndReferrer` | 正稿键集恰为 pageName+referrer |
| `deliberatelyAbsentEventsStayUnknown` | §1.4 刻意不设的 `pet_viewed`/`health_record_edit_started`/`health_record_delete_failed` 保持 unknown |
**`AnalyticsIntegrationTest`7 → 11+4**
| 测试 | 边界 |
| --- | --- |
| `acceptsV2HealthRecordFunnelEvent` | v2 事件(合法 recordType)端到端 accepted 且落库 |
| `stripsPropsOutsideV2Whitelist` | v2 事件白名单外键(内容型 `recordTitle`)被剥离,`recordType` 保留 |
| `enumOutRecordTypeValuePassesIngestForOfflinePatrol` | 枚举外 recordType 值过 ingest(§3 决策的锁定) |
| `retiredHealthRecordActionStaysRejected` | 废弃事件带 v2 同名 props 上报仍整条 rejected`unknown_event_name` |
## 5. 未尽事项
- `entryPoint` 枚举(`profile_empty_state/pet_list/post_register_guide` 等)06 号标注「待 UI 定稿收敛」——键已入白名单,枚举收敛属 Flutter 半边与 UI 定稿,后端无阻塞。
- `pet_create_failed.failureReason``pet_limit_reached` 待拍板(无上限则删)——纯值级枚举,不影响本次键级白名单。
- T2-17 Flutter 半边(细分事件挂接、pageName 编译期枚举、RouteObserver)不在本工单范围。