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

5.7 KiB
Raw Blame History

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@64c9b72patbond-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.javapatbond-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 durationMsspeciespetIndex 一致
pet_create_failed failureReasonerrorCodehttpStatusattemptSeq 一致

2.2 health_record 域(7 事件)

事件名 白名单 props 与 06 号 §1.5
health_record_create_started recordTypeentryPoint 一致
health_record_create_succeeded recordTypedurationMsphotoCount 一致
health_record_create_failed recordTypefailureReasonerrorCodehttpStatusattemptSeq 一致
health_record_viewed recordTypesource 一致
health_record_edit_succeeded recordTypefieldCount 一致
health_record_edit_failed recordTypefailureReasonerrorCodehttpStatus(无 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 的 recordTypeweight/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。

EventDictionaryTest3 → 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

AnalyticsIntegrationTest7 → 11+4

测试 边界
acceptsV2HealthRecordFunnelEvent v2 事件(合法 recordType)端到端 accepted 且落库
stripsPropsOutsideV2Whitelist v2 事件白名单外键(内容型 recordTitle)被剥离,recordType 保留
enumOutRecordTypeValuePassesIngestForOfflinePatrol 枚举外 recordType 值过 ingest(§3 决策的锁定)
retiredHealthRecordActionStaysRejected 废弃事件带 v2 同名 props 上报仍整条 rejectedunknown_event_name

5. 未尽事项

  • entryPoint 枚举(profile_empty_state/pet_list/post_register_guide 等)06 号标注「待 UI 定稿收敛」——键已入白名单,枚举收敛属 Flutter 半边与 UI 定稿,后端无阻塞。
  • pet_create_failed.failureReasonpet_limit_reached 待拍板(无上限则删)——纯值级枚举,不影响本次键级白名单。
  • T2-17 Flutter 半边(细分事件挂接、pageName 编译期枚举、RouteObserver)不在本工单范围。