Files
patbond-doc/docs/development/device-verification.md
T
lixi f5457c2f4c
CI / docs-build (push) Successful in 2m2s
docs: M3 第三波收口——报告 21~27 入档挂导航
- 21~26 Flutter 社区接入五单 + 字典 v3 白名单(flutter 286→502、api 325→334)
- 27 收口总表:社区 demo 三页消亡、M3 四条验收标准逐条取证、
  乐观更新与媒体链路端到端、实现期修正记录
- device-verification.md 的 M3 四项真机步骤已由各单收口补全

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

14 KiB
Raw Permalink Blame History

真机验证清单(常设)

定位:跨迭代常设文档——凡「只能在真机/模拟器上验证」的事项都登记在此,按迭代分节;每项含操作步骤、通过标准与执行记录。真机到位或发版前照单执行。 维护约定:各迭代收官时把真机专属验证项登记进来;完成后填执行记录并同步 功能完成清单 对应条目状态。 原位置为 iteration-2/30 号报告,2026-09-08 提升为常设文档(M3 起亦有真机项)。

通用前置准备

设备Android 真机(推荐)或 Android 模拟器。桌面/Web 不可用——没有真实的移动端后台生命周期(paused 不触发),且 platform 值不在契约枚举内会被服务端整批拒绝。

后端(工作机上,进入你本地检出的 patbond-api 仓库目录执行):

cd <你的工作区>/patbond-api
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw -DskipTests package
docker compose up -d --build
docker compose ps   # 全部容器 Uppostgres healthy

装机运行(进入你本地检出的 patbond-flutter 仓库目录):

# 模拟器:宿主机地址用 10.0.2.2
flutter run -d <设备ID> \
  --dart-define=PATBOND_API_BASE_URL=http://10.0.2.2:8081 \
  --dart-define=PATBOND_USER_API_BASE_URL=http://10.0.2.2:8082 \
  --dart-define=PATBOND_PET_API_BASE_URL=http://10.0.2.2:8083

# 真机:换成工作机局域网 IP(真机与工作机须同一网络)
#   --dart-define=PATBOND_API_BASE_URL=http://<局域网IP>:8081  (其余同理)

注意:所有 base URL 都要传,漏传的会落到默认 127.0.0.1(指向手机自身)。M3 起若新增服务端口(如 community :8084),相应补 PATBOND_COMMUNITY_API_BASE_URL


M2 挂起项(2026-09-08 登记,待执行)

来源:报告 iteration-2/10 §2.1(验收 6)与 iteration-2/12 §3.2;方案 A 挂起决议见 iteration-2/29 §4。 时限提醒iteration-3/06):建议在 2026-09-21(北极星首次出数日)前完成,否则首批读数只能标「未验收」。

验证一:Android 事件真实落库(~10 分钟)

目的:确认埋点链路在真实移动端(platform=android)端到端落库——桌面端已验证全链路仅差 platform 枚举这一步。

步骤

  1. app 内注册新账号(用户名任意、手机号 11 位、密码 ≥8 位含字母数字),登录进入主页
  2. 操作产生事件:切几个 Tab、建一只宠物档案、记一条体重
  3. 把 app 退到后台(Home 键,触发离开前台冲刷),等 5 秒
  4. 工作机查库:
docker exec patbond-postgres-1 psql -U patbond -d patbond -c \
  "SELECT event_name, platform, session_id, client_ts
   FROM platform.product_events ORDER BY client_ts DESC LIMIT 20;"

通过标准

  • 有行返回,platform 列为 android
  • 事件覆盖 ≥3 类(如 page_viewed、pet_create_started/succeeded、health_record_create_succeeded
  • 本轮所有事件共享同一个 session_idUUIDv7 格式)

验证二:SessionTracker 30 分钟后台换会话(~45 分钟,含等待)

目的:验证 10 号报告 §2.1 验收 6——退后台超 30 分钟回前台应更换 sessionId,不超过则沿用。

步骤(接验证一,同一次登录、不杀进程):

  1. 回前台随便操作一下(记一条体重)
  2. 退后台等 5 分钟 → 回前台操作(再记一条体重或切 Tab)
  3. 退后台等 35 分钟 → 回前台操作一次
  4. 再退一次后台(触发冲刷),等 5 秒后查库:
docker exec patbond-postgres-1 psql -U patbond -d patbond -c \
  "SELECT DISTINCT session_id, min(client_ts) AS first_seen
   FROM platform.product_events
   WHERE user_id = (SELECT id FROM identity.users WHERE username = '<你的测试用户名>')
   GROUP BY session_id ORDER BY first_seen;"

通过标准

  • 恰好 2 个 session_id(第 2 步的 5 分钟不换会话、第 3 步的 35 分钟换新)
  • 两个会话的 first_seen 时间差 ≈ 40 分钟(与操作节奏吻合)

巡检 SQL 兜底(06 号 §5.1 口径,防「每事件一个 sessionId」缺陷复发):

docker exec patbond-postgres-1 psql -U patbond -d patbond -c \
  "SELECT count(DISTINCT session_id)::float / count(*) AS ratio
   FROM platform.product_events;"
# ratio 应远小于 0.9> 0.9 说明 sessionId 生成有问题,告警

收尾

cd <你的工作区>/patbond-api && docker compose down
# 测试数据不入库(协作规则 3):本清单产生的数据都在 compose 卷里,
# 需要干净环境时 docker compose down -v 清卷即可

两项都过后:填写下方执行记录 + 功能完成清单 第 9 节「Android 真机落库验证 + SessionTracker 30min 手测」由 🟡。若有任何一项不过,按惯例开缺陷单修复后复测。

M2 项执行记录

(待真机到位后填写:日期、设备型号/Android 版本、两项结果、psql 输出摘录(脱敏)、执行人)


M3 预登记(社区,随迭代交付补全)

以下为 M3 交付过程中预计产生的真机专属验证项,各工单收口时在此补全具体步骤与通过标准

  1. 媒体上传弱网表现T3-13 收口补全,2026-09-09):真机蜂窝/弱 Wi-Fi 下选图→压缩→预签名直传→确认全链路;中断重试不产生孤儿 asset。

    前置:通用前置准备的后端六容器在位;PATBOND_MINIO_PUBLIC_ENDPOINT 必须配置为手机可达地址(工作机局域网 IP:9000,.env 覆盖后重启 compose)——预签名直传 URL 直指 MinIO,漏配则手机端 PUT 必然连不上;flutter run 时四个 base URL 全传(含 PATBOND_COMMUNITY_API_BASE_URL),media 上传走 user 服务 :8082PATBOND_USER_API_BASE_URL)。入口:发布页(T3-17 落地后)九宫格选图。

    步骤与通过标准

    • a蜂窝正常网:相册多选 3 张 12MP 大图 → 逐格出现进度环且百分比递增(非一跳 100%)→ 全部转 ready;后端 media.assets 对应 3 行 status='ready'。压缩耗时中端机单张 ≤2s(超出记录机型上报)。
    • b弱网中断重试:开发者选项限速或电梯/地库弱网,上传中开飞行模式掐断直传 → 该格转失败态(红色蒙层 + 重试通栏),其余图不受影响;恢复网络点格内重试 → 转 ready。
    • c孤儿不引用:在(b)失败态与上传中态各尝试一次发布 → 发布钮 gating 拦截(全部 ready 前不可提交);发帖成功后 psql 核对 community.post_media 引用的 assetId 全部 status='ready',且不含(b)中断产生的旧 assetId(该行保持 uploading,属服务端超时清理范围,不算失败)。
    • d凭据过期:选一张图后挂起 App >10 分钟再恢复触发重试 → 客户端自动换新凭据完成上传(用户无感知,不弹「签名过期」类错误)。
    • eHEIC/方向iPhone 传输的 HEIC 图与横拍竖拍各一张 → 压缩层统一出 jpeg 且方向正确(服务端 mime 白名单不收 HEIC,此项只能真机验证原生编解码)。
  2. 乐观更新真机手感T3-15/16 收口补全,2026-09-09):点赞/收藏快速连点的合并与回滚动画在真机帧率下的表现;Feed 卡片与详情页跨页状态一致。

    前置:通用前置准备的后端六容器在位;flutter run 时四个 base URL 全传(含 PATBOND_COMMUNITY_API_BASE_URL=http://<局域网IP>:8084)。数据:Feed 内至少一条他人发布的帖子(可按 iteration-3 24 号报告 §5a)种子方式造)。

    步骤与通过标准

    • a激活动画帧率:Feed 卡片与详情页各点赞一次 → 图标同帧翻转 + 240ms 弹性缩放(1→1.25→1)+ 计数即时 ±1;中低端机无可见掉帧或延迟出现的「二次跳动」。取消点赞仅颜色渐出、无缩放。
    • b快速连点合并:同一帖 1 秒内连点点赞 5~6 次 → 视觉每次即时翻转;抓包或服务端访问日志核对该帖 like 端点请求 ≤2 个(单飞 + 最终意图补发);停点后终态与最后一次点击一致,计数与 GET /api/v1/posts/{id} 权威值相符。
    • c断网回滚:开飞行模式后点赞 → 图标即时翻转,数秒内零动画直接跳回原状态(不得出现「心已灭计数未减」的中间帧或回弹动画)+ SnackBar「操作失败,请重试」恰一条;恢复网络重点 → 正常收敛。
    • d跨页一致:Feed 卡片点赞 → 进详情页应已是激活态;详情页取消收藏 → 返回 Feed 卡片同步取消(同一 ToggleSync 实例,无需刷新)。
    • e减弱动态:系统开启「移除/减弱动画」后点赞 → 状态瞬变、无缩放动画,功能不受影响。
  3. Feed 图片加载T3-14 收口补全,2026-09-09):真机上滚动 Feed 的图片加载/缓存/占位表现;MinIO 经局域网/公网访问 URL 的可达性差异。

    前置:通用前置准备的后端六容器在位;PATBOND_MINIO_PUBLIC_ENDPOINT 必须配置为手机可达地址(工作机局域网 IP:9000,.env 覆盖后重启 compose)——Feed 卡片封面 URL 是服务端现签的预签名 GET、直指 MinIO,漏配则真机图片全部走失败兜底(surfaceTint 底 + pets 图标);flutter run 时四个 base URL 全传(含 PATBOND_COMMUNITY_API_BASE_URL=http://<局域网IP>:8084)。数据:桌面/工作机先按 iteration-3 24 号报告 §5(a)的种子方式发 ≥26 帖(含单图/多图),保证两页以上可翻。

    步骤与通过标准

    • a首屏与占位:登录进 Feed → 图片卡先出 surfaceTint 加载块(无白闪/布局跳动),随后出图;多图卡右下「+N」角标可读(ink 80% 胶囊白字)。
    • b滚动加载:连续滚到列表底再回顶 → 中低端机不掉帧卡死;回滚经过已看过的图不重新转圈(缓存 key 已剥签名参数,同图不同签名命中同一内存缓存——若出现「每次刷新同图重新下载」即为缓存 key 回归,判失败)。
    • c下拉刷新后的缓存命中:下拉刷新(服务端对同一批图重新现签、URL 必然变化)→ 已展示过的封面应即时出图不过转圈;抓包或 MinIO 访问日志核对同对象未重复 GET。
    • d过期 URL 重取Feed 停留 >1 小时(预签名 TTL)后滚到未加载过的卡 → 旧 URL 过期图走失败兜底属预期,下拉刷新取新签 URL 后恢复出图,无崩溃。
    • e可达性差异:Wi-Fi(局域网 IP)与蜂窝(若 MinIO 未公网暴露)各滚一遍——蜂窝下连不上 MinIO 时应稳定显示失败兜底图标而非无限转圈;记录两种网络的首图出图耗时。
  4. 社区事件落库T3-17 收口补全,2026-09-10):community 域 v3 事件(platform=android)落库观察(沿 M2 验证一的方法,事件名换 v3 增量)。桌面端不可替代Linux 桌面的 platform=linux 不在契约枚举内,整批 400 被拒(analytics_service.dart 既有预期行为),故 v3 事件的落库只能在 Android 上验证;键集与形态的落库正确性已在工作机以 curl 造真实 payload 验证(iteration-3/26 §5c 发布/媒体 8 事件、iteration-3/25 §5c 互动 8 事件)。

    前置:通用前置准备的后端六容器在位;flutter run 时四个 base URL 全传(含 PATBOND_COMMUNITY_API_BASE_URL);PATBOND_MINIO_PUBLIC_ENDPOINT 配为手机可达地址(媒体三段需真实直传)。可与第 1、2 项同一轮操作合并执行。

    步骤:登录 → 首页 Feed 滚两屏并下拉刷新一次 → 进一条帖详情点赞/收藏/评论一次 → 返回 → 创作 Tab「发布动态」→ 输入正文 + 选 2 张图 → 「存草稿」一次 → 「发布」→ 回 Feed 确认新帖 → 退到后台等 5 秒(触发冲刷)→ 工作机查库:

    docker exec patbond-postgres-1 psql -U patbond -d patbond -c \
      "SELECT event_name, platform, props FROM platform.product_events
       WHERE event_name LIKE 'post\_%' OR event_name LIKE 'feed\_%'
          OR event_name LIKE 'comment\_%' OR event_name LIKE 'user\_%'
       ORDER BY client_ts DESC LIMIT 40;"
    

    通过标准

    • a发布漏斗成链post_create_started(entryPoint=create_tab) → post_draft_saved(trigger=manual, mediaCount=2) → post_publish_succeeded(fromDraft=true、mediaCount=2、topicCount=0、textLengthBucket、durationMs>0) 三条齐全且 platform=android;无 post_publish_failed(顺利路径)。
    • b媒体三段逐文件成对post_media_upload_started / _succeeded2 条(每张图一条),sizeBucket 同一张图的 started/succeeded 取值一致,durationMs 为真实上传耗时(非 0);中断重试的那张(与第 1 项(b)合并执行时)另有 post_media_upload_failed(failureReason=network_error, attemptSeq=1) + 重试后 started 的 attemptSeq 递进。
    • c隐私红线:上述 props 中不含 postId / assetId / commentId / 文件名 / 本地路径 / URL / 精确字数(textLength/ 精确字节数(byteSize)——出现任一即验收失败(红线 1/2/4)。
    • d互动与 Feedpost_liked/post_favorited(source=feed 或 post_detail)、comment_create_succeededfeed_viewed(离开 Feed 时一条,impressionCount>0、refreshCount=1)落库; post_impression/post_viewed(字典锁死为 unknown,若出现即客户端违规)。
    • e页名归一化page_viewed 出现 pageName='post_form'(发布页)与 'post_detail',且 pageName/referrer 中不含 UUID
    • (f)拒绝计数为 0:查 app 日志无 Analytics batch permanently rejected,或服务端响应 rejected=0(有 rejected 说明事件名/键集与字典不符,属回归)。

执行记录(M3

(待补)