- 22~26 Flutter 接入五单报告(T2-11~14 + 白名单扩充,flutter 测试 64→272) - 27 第三波收口总表:demo 数据消亡、四态纪律、埋点端到端贯通、DEBT-1 偿还 - 三次 compose 实测无契约偏差;波内 agent 中断续跑事故记录在案 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.7 KiB
24 · 事件白名单 v2 扩充(T2-17 后端半边)
依据:
06-experiment-tracking-plan.md§1.4/§1.5(事件字典 v2 增量)、§5.2(page_viewed 转正稿)、§6.4(值级巡检);ADR-013(health_record_action 移除,dev@58576f8)交付:
patbond-apidev@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 枚举维持同一分层:
- 客户端编译期枚举是第一道约束(06 号 §5.2 明确 pageName 为「编译期枚举」;recordType 同理);
- 接收端只校验键——枚举外的值(如
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:总计 191,failures 0,errors 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)不在本工单范围。