Files
lixi efdfe59a41
CI / docs-build (push) Successful in 1m0s
docs: v0.3.0 发布记录与发布前 E2E 回归门禁报告
- 新建常设「发布记录」页(挂开发文档导航),首条 v0.3.0 M3 社区
- 30 号报告:M2 11/11 + M3 14/14 同环境各连跑 3 轮零 flake、契约偏差 0;
  附契约向后兼容结构化比对(removed/changed 均 NONE)与共享代码面回归分析
- 记录首次发布的一次性操作:api main 孤儿历史重建(方案 A)、flutter 快进推法改进

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

16 KiB
Raw Permalink Blame History

30 首次发布门禁:M2+M3 双份 E2E 回归(checklist 第 2 步)

  • 执行人:QA(Explore / 回归执行)
  • 日期:2026-09-10
  • 依据:iteration-3/08 号《Git 工作流规划》§3.3 发布 checklist 第 2 步—— 「compose 全栈起,跑 M2+M3 两份 E2E 烟囱脚本,全场景 PASS,证据入波次报告」
  • 环境:patbond-apidocker compose 六容器)+ patbond-flutterdart 脚本直连)
  • 测试脚本:patbond-flutter/test_e2e_m2_manual.dartM2 收官版,11 场景) 与 patbond-flutter/test_e2e_m3_manual.dartM3 收官版,14 场景),均取 flutter@0e87413
  • 冻结契约:patbond-doc/docs/api/openapi.yaml v1.3.0doc@f848476
  • 参照模式:iteration-2/28 与 iteration-3/28 号收官报告(格式与取证标准沿用)

三仓代码零改动:本次只起 compose、跑两份既有脚本、做只读 psql/git 取证。 未修改、未 commit、未 push 任何代码仓;未改 mkdocs.yml


0. 执行概要

门禁结论

PASS——发布 checklist 第 2 步满足。

项目 结果
M2 场景通过数 11 / 1144 条断言全绿,exit 0
M3 场景通过数 14 / 1488 条断言全绿,exit 0
契约偏差数 0 个(对照冻结契约 v1.3.0
失败项 0 项
稳定性 同一 compose 环境内 两份脚本各连跑 3 轮6 次全通过、零 flake
两域共存 M2→M3→M2→M3 交叉执行,pet_health 与 community 数据同库共存,互不干扰
容器异常 六容器 RestartCount 全 0;四个应用容器日志 ERROR/Exception 计数全 0

本单目标(与 M3 收官报告的区别)

M3 版脚本已在 M3 收官(iteration-3/28)跑过 14/14。本单的增量价值在于复跑 M2 版: M3 期间 pets 域未做功能变更,但两域共享 patbond-common、契约快照文件、CI 流水线与同一 数据库实例——需要实测确认 M3 交付没有回归 M2 的宠物健康档案域。为把「共存」也一并证实, 两份脚本在同一次 compose 生命周期内交叉执行。

脱敏声明

token 一律截断至前 20 字符 + <REDACTED>(脚本内置 redact());MinIO 预签名 URL 的 查询串替换为 <SIGNATURE_REDACTED>;幂等键显示为 <KEY-1>;密码与 .env 内容不出现在 任何输出。


1. 环境记录

1.1 构建与启动(patbond-api 代码零改动)

cd <你的工作区>/patbond-api
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw -DskipTests package
# BUILD SUCCESS —— Total time: 4.263 s(增量编译,五模块 reactor 全 SUCCESS
# 产出四个 -exec.jarauth 36MB / community 51MB / pet 25MB / user 41MB

docker compose up -d --build
# Container patbond-postgres-1 Healthy
# Container patbond-minio-1    Healthy
# Container patbond-user-1 / auth-1 / community-1 / pet-1  Started

1.2 六容器状态与镜像版本

CONTAINER             REPOSITORY          TAG                            SIZE     STATUS
patbond-postgres-1    postgres            18                             162MB    Up (healthy)
patbond-minio-1       minio/minio         RELEASE.2025-04-22T22-12-26Z    64MB    Up (healthy)
patbond-auth-1        patbond-auth        latest(本次重建)              141MB    Up   :8081
patbond-user-1        patbond-user        latest(本次重建)              147MB    Up   :8082
patbond-pet-1         patbond-pet         latest(本次重建)              132MB    Up   :8083
patbond-community-1   patbond-community   latest(本次重建)              155MB    Up   :8084
组件 版本
PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2)
MinIO RELEASE.2025-04-22T22-12-26Z
容器内 JRE Temurin OpenJDK 17.0.20+8
宿主 Docker 29.7.2 / Docker Compose 5.5.1
Dart SDK(跑脚本) 3.12.2 (stable)

1.3 启动耗时与就绪验证

依赖顺序符合编排:postgres/minio 先 Healthy,四应用容器随后 Started(容器创建到启动 约 3 秒),Spring Boot 自身启动耗时:

Started AuthApplication      in  9.752 seconds
Started UserApplication      in 13.194 seconds
Started PetApplication       in  9.255 seconds
Started CommunityApplication in 11.135 seconds

无 token 探活(预期 401 信封):

curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8082/api/v1/me     # 401
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8083/api/v1/pets    # 401
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8084/api/v1/feed    # 401

1.4 脚本执行耗时(第三轮,取权威计时)

脚本 场景数 耗时 退出码 断言数 失败标记
test_e2e_m2_manual.dart 11 1149 ms 0 44 0
test_e2e_m3_manual.dart 14 1662 ms 0 88 0

1.5 数据残留说明(非缺陷)

pgdata / minio-data 卷延续自 M3 收官那次运行(compose 只重建容器与镜像,未 down -v)。 两份脚本均以时间戳随机账号运行、且 M3 的全量翻页断言按「本轮新增 26 条 vs 全量 117 条」 的相对口径校验,因此残留数据不影响判定,反而额外证明了跨轮数据共存无干扰


2. M2 版 E2E 逐场景结果(11/11 PASS

本轮取证账号 e2e_m2_a_1789025850872petId 01a08a40-1ad6-7a26-962c-a5f6cd3706a1

# 场景 结果 关键断言实测
1 注册账号 A → 登录 ✓ PASS register 200 / login 200token eyJhbGciOiJSUzI1NiJ9...<REDACTED>
2 建档(含品种)→ 列表/详情读回 ✓ PASS 品种目录 16 条;POST /pets 201myRole=ownerversion=0breedDisplayName=中华田园犬;列表/详情四字段一致
3 记体重 ×2 → cursor 分页 ✓ PASS 8.20/8.45kg 各 201;第一页 8.45 在前 + hasMore=true;第二页 8.20 + hasMore=falsenextCursor=null
4 疫苗登记(scheduled)→ 标记完成 ✓ PASS 疫苗目录 6 条;PATCH 200version 0→1administeredOn/nextDueOn 回读一致
5 健康事件(整数分)→ 时间线 ✓ PASS amountCents=12500 原样回读;createdByUserId = token subject;时间线 1 条
6 提醒创建 → 标记完成 ✓ PASS 创建恒 pending + completedAt=nullPATCH 后 completedAt = 客户端提交时刻
7 /summary?tz=Asia/Shanghai 四项聚合 ✓ PASS 最新体重 8.45kg;疫苗进度 1/1;下次接种 2027-09-10source=nextDue);当月花费 12500 分、month=2026-09、tz 回显
8 账号 B 越权访问 A 的宠物四路(防枚举) ✓ PASS 详情/体重/疫苗/摘要四路全 404 / 40401,响应体逐字节一致 {"code":40401,"message":"宠物不存在","data":null}B 列表为空
9 账号 A 第二设备重新登录 → 全量读回 ✓ PASS 新会话 token 与设备 1 不同(独立 token family);宠物 1 / 体重 2 / 疫苗 1 / 事件 1 / 提醒 1 全量一致
10 /api/v1/events v2 事件上报 ✓ PASS 4 条 → 202accepted=4, duplicated=0, rejected=0
11 乐观锁冲突明确性 ✓ PASS 第一次 PATCH 200version 0→1);同过期 version 第二次 409 / 40902;读回确认先写者数据保留

3. M3 版 E2E 逐场景结果(14/14 PASS

本轮取证账号 e2e_m3_a_1789025861236postId 01a08a40-422a-7d60-a915-f04d7b0061be。 (三轮运行的断言输出逐条一致,仅随机账号名与 UUID 不同;下表的字面值取自取证轮输出。)

# 场景 结果 关键断言实测
1 注册 A/B 两账号 → 登录 ✓ PASS 两账号注册 200 且为两个独立 userId(模拟两客户端)
2 两步上传直传 MinIO/media/uploads → complete ✓ PASS 登记 201 返回 SigV4 预签名凭据、requiredHeaders 恒且仅 {Content-Type}344 字节直传 200complete 后 uploading→readybyteSize=344readyAt 已写);重复 complete 幂等 200 同一 asset
3 草稿创建 → 发布(PATCH draft→published ✓ PASS 草稿 status=draft/publishedAt=null、media 挂接 position=0 且服务端置唯一 isCover;发布后 status=publishedpublishedAt 已写、version 0→1
4 验收① B 拉 /feed A 的帖首位可见 ✓ PASS published_at DESC 首位命中;FeedCard 必填齐备;AuthorSummary 不露 bio/username;裁剪生效(无 content 全文/media 整组/version
5 预签名 GET 字节往返 + 桶私有 ✓ PASS 344 字节逐字节一致;去签名直访 403
6 验收② 连续 3 次 PUT like → count 恰 1 ✓ PASS 三次均 200 权威终态 {liked:true,likeCount:1}(非 409);DELETE → 0;重复 DELETE 幂等
7 收藏 → /me/bookmarks → 取消 ✓ PASS 列表项形态 = FeedCard,视角字段为 B;取消后不含
8 评论 ×2 / 作者软删 / 帖主越权删被拒 ✓ PASS B 删自己 200;A(帖主)删 B 的 403 / 40301commentCount 同事务 +1/-1 准确
9 关注幂等 + follow-stats + 自关注拒绝 ✓ PASS 重复 PUT 幂等 200;自关注 422 / 42204;自取关 200 no-opA 查自己 followedByMe 恒 false
10 验收③ 25 帖 → 双粒度全量翻页比对 ✓ PASS limit=7 → 17 页 117 条;limit=100 → 2 页 117 条;两种页大小逐位一致,零重复零遗漏
11 验收④ 软删一帖 → 出 Feed + 直接 GET 404 ✓ PASS 删后全量恰少 1;B 与作者 A 直接 GET 均 404 / 40403 且响应体逐字节一致
12 防枚举:草稿 vs 随机 UUID 响应体一致 ✓ PASS 四路全 404 / 40403 一致 {"code":40403,"message":"帖子不存在","data":null};互动面恒为公开面;草稿对作者详情仍可见、不入公共 Feed
13 v3 社区事件上报 + 白名单/字典兜底 ✓ PASS 8 条 → 202 accepted=8;白名单外键剥离后仍 accepted;字典外 post_impression 整条 rejectedunknown_event_name),批次仍 202
14 幂等重放:同键同 hash / 异 hash / 缺头 / 跨作者 ✓ PASS 同键同 hash 返回原帖不产生第二帖;异 hash 409 / 40905;缺 Idempotency-Key 400 / 40000;幂等键按作者隔离

4. 数据库证据(两域共存,只读 psql)

4.1 schema 与 Flyway 迁移

-- \dn
community | identity | media | pet_health | platform | public

-- select installed_rank, version, description, success from public.flyway_schema_history;
 1 | 1 | identity media baseline        | t
 2 | 2 | create platform product events | t
 3 | 3 | pet health baseline            | t
 4 | 4 | pet health dictionary seed     | t
 5 | 5 | community baseline             | t

V1~V5 全 success=tM3 的 V5 未触碰 M2 的 V3/V4(不可变迁移纪律保持)。

4.2 两域数据行数(六次脚本运行累计,含既有残留)

 pet_health.pets         |   8      community.posts         | 131
 pet_weight_records      |   9      community.comments      |  10
 pet_vaccinations        |   7      community.post_likes    |   3
 health_events           |   6      community.user_follows  |   3
 care_reminders          |   6      media.assets            |  17
 pet_owners              |   8      platform.product_events |  61

两域各 8 张表并存于同一实例,交叉执行无外键/唯一键冲突、无死锁。

4.3 埋点落库核对

M2 本轮会话(session_id=1e1a8388-…,共 4 条 = 上报数):

 health_record_create_succeeded | android | 1.0.0+e2e | 2
 page_viewed                    | android | 1.0.0+e2e | 1
 pet_create_succeeded           | android | 1.0.0+e2e | 1

M3 本轮会话(session_id=a28da209-…,8 白名单事件 + 1 条隐私兜底事件):

 comment_create_succeeded | 1     post_liked                  | 2
 feed_viewed              | 1     post_media_upload_succeeded | 1
 page_viewed              | 1     post_publish_succeeded      | 1
 post_favorited           | 1     user_followed               | 1

隐私红线兜底与字典守门实测:

-- 字典外事件未落库
select count(*) from platform.product_events where event_name='post_impression';  -- 0

-- 混入白名单外 postId 的 post_liked:落库 props 已剥离
select props from platform.product_events where event_id='bc13cbbd-…';
 {"source": "feed"}      -- 无 postId

5. 契约偏差声明与共享面回归分析

5.1 契约偏差数:0 个

两份脚本共 132 条断言覆盖 HTTP 状态码、业务错误码、信封结构、字段形态、分页语义、 幂等语义、乐观锁语义与防枚举一致性,与冻结契约 v1.3.0 全部一致,无需修复项。

5.2 M2 契约面在 v1.3.0 中零漂移(结构化取证)

M3 期间四模块契约快照从 openapi-v1.2.0.yaml 换名到 openapi-v1.3.0.yaml 是本次回归最需要盯的共享面。对 doc@511617bv1.2.0 冻结)与 doc@f848476(v1.3.0 冻结) 做结构化比对(YAML 解析后按键排序序列化对比,非文本 diff):

info.version: 1.2.0 -> 1.3.0
paths18 -> 31+13,全部为 community/media 新增;removed: NONE
  v1.2.0 的 18 条 path 定义 —— CHANGED/MISSING: NONE(全部完全一致)
schemas45 -> 72+27
  removed schemas: NONE
  changed schemas: NONE

v1.3.0 相对 v1.2.0 严格增量:M2 的 18 条路径与 45 个 schema 一字未改, M2 版脚本对照 v1.3.0 运行等价于对照 v1.2.0 运行。

5.3 共享代码面回归分析(64c9b72..8089c06,即 M3 全区间)

patbond-pet/src/main/  →  0 个文件变更(pets 域生产代码 M3 期间未被触碰)
patbond-pet/ 变更仅在测试侧:ContractConformanceTest / ContractValidator /
             OpenApiContract + 快照文件改名(v1.2.0 → v1.3.0
patbond-common/        →  仅 ErrorCode.java +9 行,纯新增枚举常量:
  POST_ACCESS_DENIED(40301) / POST_NOT_FOUND(40403) / IDEMPOTENCY_PAYLOAD_MISMATCH(40905)
  MEDIA_NOT_FOUND(40405) / COMMENT_NOT_FOUND(40404) / TARGET_USER_NOT_FOUND(40406)
  MEDIA_NOT_READY(42203) / FOLLOW_RULE_VIOLATION(42204) / MEDIA_UPLOAD_STATE_INVALID(42205)
  —— 无任何 `-` 行,M2 错误码(40401/40902/40000…)定义未被修改或删除

三条共享面(契约快照、patbond-common、同一数据库实例)均验证为纯增量, 与实测的 11/11 结果互为印证:M3 交付未破坏 M2 功能,无回归


6. 失败项

无。 六次脚本运行(M2 ×3、M3 ×3)全部 exit 0,输出中 /FAIL 计数为 0, 无需区分「脚本环境问题」与「真实回归」。


7. 三仓状态(零改动核验)

cd <你的工作区>/patbond-api      && git status --short   # 空
cd <你的工作区>/patbond-flutter  && git status --short   # 空
cd <你的工作区>/patbond-doc      && git status --short   # 仅本报告(未 commit
HEAD
patbond-api 8089c06 feat: 事件字典 v3 白名单扩充…(T3-20)
patbond-flutter 0e87413 test: M3 E2E 烟囱脚本(T3-21 收官)
patbond-doc 3ebe562 docs: M3 收官——E2E 报告与收官总结入档,验收 PASSED

patbond-api/*/target/ 下的重建产物为 gitignore 覆盖项,不产生工作区脏状态。)


8. 环境清理

cd <你的工作区>/patbond-api && docker compose down

数据卷 pgdata / minio-data 按既往做法保留(未加 -v),便于下次复跑与事后取证。


9. 遗留与后续

  1. 真机挂起项不变:M2 的两项(Android 事件落库真机观察、SessionTracker 30min 手测)与 M3 的真机项仍按方案 A 挂起,本次脚本直连不替代真机验证——见 docs/development/device-verification.md
  2. 发布 checklist 后续步骤:本报告只闭合 §3.3 第 2 步;第 3 步(命名统一 + api main 重建)、 第 4~8 步(合并/打标/分支保护/发布说明/hotfix 纪律)待拍板后执行。
  3. E2E 自动化仍为手动模式iteration-3/08 §5 的结论(保持「波次收尾手动跑、证据入档」, 自动化做成 workflow_dispatch 手动工作流,低优先)未变;本次两份脚本 3 秒内跑完, 手动成本极低,暂无自动化紧迫性。