- 28 E2E 烟囱:11/11 场景全绿、契约偏差 0、四条验收标准全过(真机两项方案 A 挂起) - 29 收官总结:终态对照开工基线(测试 82/34→191/272、契约 5→18 路径冻结、 ADR 001~015、生产埋点从零到贯通)、遗留清单与 M3 方向输入 - 看板终态更新 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,407 @@
|
|||||||
|
# 28 M2 收官:E2E 烟囱测试报告(T2-18)
|
||||||
|
|
||||||
|
- 执行人:Frontend Developer
|
||||||
|
- 日期:2026-09-08
|
||||||
|
- 环境:patbond-flutter (dev 分支) + patbond-api (docker compose 编排,代码零改动)
|
||||||
|
- 测试脚本:`patbond-flutter/test_e2e_m2_manual.dart`(commit `720865b`,已推送 origin/dev)
|
||||||
|
- 参照模式:iteration-1/18 号收官报告(格式与取证标准沿用)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 执行概要
|
||||||
|
|
||||||
|
### 测试目标
|
||||||
|
|
||||||
|
M2 第四波收官(工单 T2-18):在 compose 真实后端上跑通 M2 完整链路烟囱并收集证据——
|
||||||
|
登录 → 建档 → 记体重 → 登记疫苗 → 记健康事件 → 摘要数值核对 → 第二账号访问被拒 →
|
||||||
|
第二设备同账号全量读回,外加埋点落库与乐观锁冲突两条链路。
|
||||||
|
|
||||||
|
### 测试结果
|
||||||
|
|
||||||
|
**✓ 11/11 场景全部通过**(单次运行一次通过;格式化后复跑再次 11/11)
|
||||||
|
|
||||||
|
- Docker Compose 四容器健康运行(postgres + auth:8081 + user:8082 + pet:8083)
|
||||||
|
- 契约一致性:响应字段、错误码、HTTP 状态码与冻结契约 openapi v1.2.0 完全一致
|
||||||
|
- **契约偏差数:0 个**
|
||||||
|
- Flutter 门禁三命令全绿:`dart format`(0 changed)/ `flutter analyze`(No issues)/
|
||||||
|
`flutter test`(**272 passed**)
|
||||||
|
- 数据库证据齐备:`pet_health` 六表 + `platform.product_events` psql 查证一致
|
||||||
|
|
||||||
|
### ⚠️ 真机挂起项(显著标注:真机待补验)
|
||||||
|
|
||||||
|
真机不可用(用户确认),以下两项按既定方案 A 挂起,**本报告不含其证据**,
|
||||||
|
待真机可用后补验:
|
||||||
|
|
||||||
|
| # | 挂起项 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 1 | **Android 真机事件落库观察** | 本报告以脚本直连 `/api/v1/events`(platform=android 模拟真机值)替代验证服务端链路;真机端 AnalyticsClient → 持久化队列 → 上报的端上链路待真机补验 |
|
||||||
|
| 2 | **SessionTracker 30min 会话超时手测** | 前后台切换超时重建 sessionId 的真机手测;单元测试已覆盖规则(T2-15),真机行为待补验 |
|
||||||
|
|
||||||
|
### 脱敏声明
|
||||||
|
|
||||||
|
全部 token 截断至前 20 字符 + `<REDACTED>`;密码不出现在任何输出;`.env` 内容未引用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 后端启动与健康检查
|
||||||
|
|
||||||
|
### 1.1 构建与启动(patbond-api 代码零改动)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd patbond-api
|
||||||
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw -DskipTests package
|
||||||
|
# BUILD SUCCESS
|
||||||
|
|
||||||
|
docker compose up -d --build
|
||||||
|
# Container patbond-postgres-1 Healthy
|
||||||
|
# Container patbond-auth-1 Started
|
||||||
|
# Container patbond-user-1 Started
|
||||||
|
# Container patbond-pet-1 Started
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 容器健康状态
|
||||||
|
|
||||||
|
```text
|
||||||
|
NAMES STATUS PORTS
|
||||||
|
patbond-pet-1 Up 12 seconds 0.0.0.0:8083->8083/tcp
|
||||||
|
patbond-auth-1 Up 12 seconds 0.0.0.0:8081->8081/tcp
|
||||||
|
patbond-user-1 Up 12 seconds 0.0.0.0:8082->8082/tcp
|
||||||
|
patbond-postgres-1 Up 15 seconds (healthy) 5432/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 服务就绪验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs patbond-pet-1 | grep Started
|
||||||
|
# Started PetApplication in 6.286 seconds
|
||||||
|
|
||||||
|
curl -s http://127.0.0.1:8083/api/v1/pets
|
||||||
|
# {"code":40101,"message":"token 无效或过期","data":null} ← 无 token 预期 401
|
||||||
|
curl -s http://127.0.0.1:8082/api/v1/me
|
||||||
|
# {"code":40101,"message":"token 无效或过期","data":null}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. E2E 烟囱测试执行记录(11 场景)
|
||||||
|
|
||||||
|
### 2.1 测试脚本
|
||||||
|
|
||||||
|
`test_e2e_m2_manual.dart`(纯 dart HttpClient 脚本,无 Flutter 运行时依赖,
|
||||||
|
与第一迭代 `test_e2e_manual.dart` 并列放仓库根目录,**不在 test/ 目录**、
|
||||||
|
不进 `flutter test`)。随机生成账号 `e2e_m2_a_<timestamp>` / `e2e_m2_b_<timestamp>`
|
||||||
|
避免冲突。运行方式:`docker compose up -d` 后 `dart run test_e2e_m2_manual.dart`。
|
||||||
|
|
||||||
|
本次取证运行:账号 A `e2e_m2_a_1788849543120`,petId `01a07fbd-dcad-756a-a844-fe5323aa0713`。
|
||||||
|
|
||||||
|
### 2.2 场景 1:注册账号 A → 登录
|
||||||
|
|
||||||
|
```text
|
||||||
|
[1/11] 注册账号 A → 登录
|
||||||
|
POST /api/v1/auth/register → 200
|
||||||
|
✓ 注册成功
|
||||||
|
userId(A): 01a07fbd-d92f-7ee7-a52c-d33c7fa80071
|
||||||
|
accessToken: eyJhbGciOiJSUzI1NiJ9...<REDACTED>
|
||||||
|
POST /api/v1/auth/login → 200
|
||||||
|
✓ 登录成功(设备 1 会话)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 场景 2:建档(含品种)→ 列表/详情读回核对
|
||||||
|
|
||||||
|
```text
|
||||||
|
[2/11] 建档(POST /pets,含品种)→ 列表/详情读回核对
|
||||||
|
GET /api/v1/breeds?species=dog → 200
|
||||||
|
✓ 品种目录返回 16 条
|
||||||
|
选用品种: 中华田园犬 (3a545503-a8ad-484a-8bbf-a38d08c6dcea)
|
||||||
|
POST /api/v1/pets → 201
|
||||||
|
✓ 建档成功(201)
|
||||||
|
petId: 01a07fbd-dcad-756a-a844-fe5323aa0713
|
||||||
|
myRole: owner / version: 0 / breedDisplayName: 中华田园犬
|
||||||
|
✓ 创建者角色为 owner
|
||||||
|
✓ 品种展示名解出一致
|
||||||
|
GET /api/v1/pets → 200
|
||||||
|
✓ 列表读回 1 只宠物且 id 一致
|
||||||
|
GET /api/v1/pets/{petId} → 200
|
||||||
|
✓ 详情读回核对通过(name/species/breedId/status)
|
||||||
|
```
|
||||||
|
|
||||||
|
契约验证:201 + PetEnvelope、`myRole=owner`(创建者自动 primary owner)、
|
||||||
|
`breedDisplayName` 由字典解出、petId 为 UUIDv7(前缀 `01a07fbd`)。
|
||||||
|
|
||||||
|
### 2.4 场景 3:记体重 ×2 → cursor 分页读回
|
||||||
|
|
||||||
|
```text
|
||||||
|
[3/11] 记体重 ×2 → 列表 cursor 分页读回
|
||||||
|
POST /weights (8.20kg, 2026-09-06T06:39:04.429336Z) → 201
|
||||||
|
POST /weights (8.45kg, 2026-09-07T06:39:04.429336Z) → 201
|
||||||
|
GET /weights?limit=1 → 200
|
||||||
|
✓ 第一页:最新体重 8.45kg 在前,hasMore=true,nextCursor 非空
|
||||||
|
GET /weights?limit=1&cursor=... → 200
|
||||||
|
✓ 第二页:8.20kg,hasMore=false,nextCursor=null
|
||||||
|
```
|
||||||
|
|
||||||
|
契约验证:分页正典形态 `{items, nextCursor, hasMore}`;`measured_at DESC` 排序;
|
||||||
|
末页 `nextCursor` 恒为 null。
|
||||||
|
|
||||||
|
### 2.5 场景 4:登记疫苗(scheduled)→ 标记完成(乐观锁)
|
||||||
|
|
||||||
|
```text
|
||||||
|
[4/11] 登记疫苗(scheduled)→ 标记完成(PATCH + version)
|
||||||
|
GET /api/v1/vaccine-catalog?species=dog → 200
|
||||||
|
✓ 疫苗目录返回 6 条
|
||||||
|
选用疫苗: 犬二联疫苗 (e07d9a48-a8d3-4b6a-a14b-03a42fe85589)
|
||||||
|
POST /vaccinations (scheduled, plannedOn=2026-09-08) → 201
|
||||||
|
vaccinationId: 01a07fbd-ddc2-7dc8-aa2e-768a51785dc7 / version: 0
|
||||||
|
PATCH /vaccinations/{id} (→completed, version=0) → 200
|
||||||
|
✓ 标记完成成功,version 0→1,administeredOn/nextDueOn 回读一致
|
||||||
|
```
|
||||||
|
|
||||||
|
契约验证:`scheduled → completed` 状态机合法迁移;`version` 提交比对通过后 +1;
|
||||||
|
`vaccineName` 由目录解出;`administeredOn=2026-09-08`、`nextDueOn=2027-09-08` 原样回读。
|
||||||
|
|
||||||
|
### 2.6 场景 5:记健康事件(金额整数分)→ 时间线读回
|
||||||
|
|
||||||
|
```text
|
||||||
|
[5/11] 记健康事件(amountCents 整数分)→ 时间线读回
|
||||||
|
POST /health-events (medical, amountCents=12500) → 201
|
||||||
|
✓ amountCents=12500 原样回读,createdByUserId=token subject
|
||||||
|
healthEventId: 01a07fbd-de0a-7532-8d71-023452ba21ad
|
||||||
|
GET /health-events → 200
|
||||||
|
✓ 时间线读回 1 条且字段一致
|
||||||
|
```
|
||||||
|
|
||||||
|
契约验证:金额整数分传输无精度损耗;`createdByUserId` 取自验签 token
|
||||||
|
(等于账号 A userId),不收请求体。
|
||||||
|
|
||||||
|
### 2.7 场景 6:创建提醒 → 标记完成(completedAt 校验)
|
||||||
|
|
||||||
|
```text
|
||||||
|
[6/11] 创建提醒 → 标记完成(completedAt 校验)
|
||||||
|
POST /care-reminders (deworming, dueAt=2026-10-08T06:39:04.429336Z) → 201
|
||||||
|
✓ 提醒创建成功,恒为 pending 且 completedAt=null
|
||||||
|
reminderId: 01a07fbd-de48-779f-87f6-11135ae93be7
|
||||||
|
PATCH /care-reminders/{id} (→completed) → 200
|
||||||
|
✓ 标记完成成功,completedAt=2026-09-08T06:39:04.429336Z(客户端提交时刻回读)
|
||||||
|
```
|
||||||
|
|
||||||
|
契约验证:创建恒为 `pending`(不收 status);`completedAt` 由客户端提交、
|
||||||
|
非空当且仅当 `status=completed`。
|
||||||
|
|
||||||
|
### 2.8 场景 7:摘要四项聚合逐项断言
|
||||||
|
|
||||||
|
```text
|
||||||
|
[7/11] GET /summary?tz=Asia/Shanghai 四项聚合逐项断言
|
||||||
|
GET /summary → 200
|
||||||
|
✓ 最新体重 = 8.45kg(第二条写入,measured_at DESC 首行)
|
||||||
|
✓ 疫苗进度 = 1/1(scheduled→completed 后)
|
||||||
|
✓ 下次接种 = completed 行的 nextDueOn(2027-09-08,source=nextDue)
|
||||||
|
✓ 当月花费 = 12500 分,month=2026-09,timezone 回显 Asia/Shanghai
|
||||||
|
```
|
||||||
|
|
||||||
|
四项聚合与前述写入逐项一致(口径 = iteration-2 报告 18 §3 定型表):
|
||||||
|
|
||||||
|
| 聚合项 | 前述写入 | 摘要返回 | 结论 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| latestWeight | 8.45kg(measured_at 最新) | weightKg=8.45 | ✓ |
|
||||||
|
| vaccinationProgress | 1 条 completed / 1 条已登记 | completedDoses=1, totalDoses=1 | ✓ |
|
||||||
|
| nextVaccination | completed 行 nextDueOn=2027-09-08 | dueOn=2027-09-08, source=nextDue, vaccinationId 命中 | ✓ |
|
||||||
|
| monthlyExpense | amountCents=12500(当月事件) | amountCents=12500, month=2026-09, timezone=Asia/Shanghai | ✓ |
|
||||||
|
|
||||||
|
### 2.9 场景 8:权限拒绝——账号 B 访问 A 的宠物四路(防枚举)
|
||||||
|
|
||||||
|
```text
|
||||||
|
[8/11] 注册账号 B → 用 B 的 token 访问 A 的宠物四路(防枚举核对)
|
||||||
|
POST /api/v1/auth/register (B) → 200
|
||||||
|
详情 GET /pets/{id} → 404 / code 40401 ✓
|
||||||
|
体重 GET /pets/{id}/weights → 404 / code 40401 ✓
|
||||||
|
疫苗 GET /pets/{id}/vaccinations → 404 / code 40401 ✓
|
||||||
|
摘要 GET /pets/{id}/summary → 404 / code 40401 ✓
|
||||||
|
✓ 四路响应体完全一致(防枚举):{"code":40401,"message":"宠物不存在","data":null}
|
||||||
|
✓ B 的宠物列表为空(列表天然隔离)
|
||||||
|
```
|
||||||
|
|
||||||
|
契约验证:无关系调用者与「宠物不存在」响应逐字节一致,随机探测 UUID 无法区分
|
||||||
|
是否命中真实记录(防枚举语义)。
|
||||||
|
|
||||||
|
### 2.10 场景 9:跨设备读取——账号 A 重新登录全量读回
|
||||||
|
|
||||||
|
```text
|
||||||
|
[9/11] 账号 A 重新登录(模拟第二设备新会话)→ 全量数据读回
|
||||||
|
POST /api/v1/auth/login (设备 2) → 200
|
||||||
|
✓ 新会话 token 与设备 1 不同(独立 token family)
|
||||||
|
✓ 宠物列表:1 只(旺财M2)
|
||||||
|
✓ 体重记录:2 条
|
||||||
|
✓ 疫苗记录:1 条(completed)
|
||||||
|
✓ 健康事件:1 条
|
||||||
|
✓ 提醒:1 条(completed,completedAt=2026-09-08T06:39:04.429336Z)
|
||||||
|
```
|
||||||
|
|
||||||
|
设备 1 写入的全部五类数据在设备 2 新会话完整读回,服务端为唯一事实源。
|
||||||
|
|
||||||
|
### 2.11 场景 10:埋点链路——v2 事件上报与落库
|
||||||
|
|
||||||
|
```text
|
||||||
|
[10/11] POST /api/v1/events 上报 v2 事件(platform=android 模拟真机值)
|
||||||
|
eventId: 0c673914-... (pet_create_succeeded)
|
||||||
|
eventId: dd59a83a-... (health_record_create_succeeded, recordType=weight)
|
||||||
|
eventId: 3993beb4-... (health_record_create_succeeded, recordType=vaccine)
|
||||||
|
eventId: 87a4a82b-... (page_viewed)
|
||||||
|
POST /api/v1/events (4 条) → 202
|
||||||
|
✓ 4/4 逐条 accepted(accepted=4, duplicated=0, rejected=0)
|
||||||
|
```
|
||||||
|
|
||||||
|
**落库查证(docker exec psql)**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
patbond=# SELECT event_name, event_version, platform, user_id,
|
||||||
|
left(event_id::text,8) AS event_id_prefix, props
|
||||||
|
FROM platform.product_events
|
||||||
|
WHERE session_id = '4d8375a7-ec77-45bf-90d6-1b7eff73a1ff'
|
||||||
|
ORDER BY event_name;
|
||||||
|
|
||||||
|
event_name | event_version | platform | user_id | event_id_prefix | props
|
||||||
|
--------------------------------+---------------+----------+--------------------------------------+-----------------+-------------------------------------------------------
|
||||||
|
health_record_create_succeeded | 2 | android | 01a07fbd-d92f-7ee7-a52c-d33c7fa80071 | dd59a83a | {"durationMs": 640, "recordType": "weight"}
|
||||||
|
health_record_create_succeeded | 2 | android | 01a07fbd-d92f-7ee7-a52c-d33c7fa80071 | 3993beb4 | {"durationMs": 820, "recordType": "vaccine"}
|
||||||
|
page_viewed | 2 | android | 01a07fbd-d92f-7ee7-a52c-d33c7fa80071 | 87a4a82b | {"pageName": "pet_detail", "referrer": "pet_list"}
|
||||||
|
pet_create_succeeded | 2 | android | 01a07fbd-d92f-7ee7-a52c-d33c7fa80071 | 0c673914 | {"species": "dog", "petIndex": 1, "durationMs": 1200}
|
||||||
|
(4 rows)
|
||||||
|
```
|
||||||
|
|
||||||
|
4 条 v2 事件全部落 `platform.product_events`,eventId、props 白名单键、
|
||||||
|
platform=android、user_id 归因逐项一致。(真机端上链路见 §0 挂起项 1。)
|
||||||
|
|
||||||
|
### 2.12 场景 11:乐观锁冲突明确性
|
||||||
|
|
||||||
|
```text
|
||||||
|
[11/11] 两次 PATCH 宠物档案提交同一 version → 第二次 409/40902
|
||||||
|
PATCH /pets/{id} (version=0, 第一次) → 200
|
||||||
|
✓ 第一次 PATCH 成功,version 0→1
|
||||||
|
PATCH /pets/{id} (同一过期 version=0, 第二次/设备 2) → 409
|
||||||
|
✓ 第二次被明确拒绝:409/40902(数据已被修改,请刷新后重试),先写者数据保留
|
||||||
|
✓ 读回确认先写者数据保留(personality=沉稳)
|
||||||
|
```
|
||||||
|
|
||||||
|
契约验证:不静默覆盖;先写者胜出;后写者得到明确的 409/40902 与可行动 message。
|
||||||
|
(Flutter 端对 40902 的「明确提示 + 取新 version 重提」交互已有 widget 测试覆盖,
|
||||||
|
见 `test/features/pets/pet_form_page_test.dart`。)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 数据库查询证据(pet_health schema)
|
||||||
|
|
||||||
|
```text
|
||||||
|
patbond=# SELECT name, species, status, version, personality FROM pet_health.pets WHERE id = '01a07fbd-...0713';
|
||||||
|
name | species | status | version | personality
|
||||||
|
--------+---------+--------+---------+-------------
|
||||||
|
旺财M2 | dog | active | 1 | 沉稳
|
||||||
|
|
||||||
|
patbond=# SELECT role, is_primary FROM pet_health.pet_owners WHERE pet_id = ...;
|
||||||
|
role | is_primary
|
||||||
|
-------+------------
|
||||||
|
owner | t
|
||||||
|
|
||||||
|
patbond=# SELECT weight_kg, measured_at FROM pet_health.pet_weight_records WHERE pet_id = ... ORDER BY measured_at DESC;
|
||||||
|
weight_kg | measured_at
|
||||||
|
-----------+-------------------------------
|
||||||
|
8.45 | 2026-09-07 06:39:04.429336+00
|
||||||
|
8.20 | 2026-09-06 06:39:04.429336+00
|
||||||
|
|
||||||
|
patbond=# SELECT status, dose_no, administered_on, next_due_on, version FROM pet_health.pet_vaccinations WHERE pet_id = ...;
|
||||||
|
status | dose_no | administered_on | next_due_on | version
|
||||||
|
-----------+---------+-----------------+-------------+---------
|
||||||
|
completed | 1 | 2026-09-08 | 2027-09-08 | 1
|
||||||
|
|
||||||
|
patbond=# SELECT event_type, title, amount_cents FROM pet_health.health_events WHERE pet_id = ...;
|
||||||
|
event_type | title | amount_cents
|
||||||
|
------------+-------------+--------------
|
||||||
|
medical | M2 烟囱体检 | 12500
|
||||||
|
|
||||||
|
patbond=# SELECT reminder_type, status, completed_at FROM pet_health.care_reminders WHERE pet_id = ...;
|
||||||
|
reminder_type | status | completed_at
|
||||||
|
---------------+-----------+-------------------------------
|
||||||
|
deworming | completed | 2026-09-08 06:39:04.429336+00
|
||||||
|
```
|
||||||
|
|
||||||
|
验证点:全部数据持久化落库;`pets.version=1`(一次成功 PATCH 后)与 40902 拒绝语义
|
||||||
|
互证;`amount_cents` 整数分无损;`completed_at` 与 API 回读一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Flutter 门禁验证(三命令随行取证)
|
||||||
|
|
||||||
|
### 4.1 格式化检查
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dart format --output=none --set-exit-if-changed lib test
|
||||||
|
# Formatted 97 files (0 changed) in 0.39 seconds.
|
||||||
|
# EXIT: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 静态分析
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter analyze
|
||||||
|
# Analyzing patbond-flutter...
|
||||||
|
# No issues found! (ran in 0.9s)
|
||||||
|
```
|
||||||
|
|
||||||
|
(含根目录两个 E2E 脚本在内全仓 0 issues;两脚本头部 `ignore_for_file: avoid_print`。)
|
||||||
|
|
||||||
|
### 4.3 单元/组件测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter test
|
||||||
|
# 00:17 +272: All tests passed!
|
||||||
|
```
|
||||||
|
|
||||||
|
**✓ 272 个测试全部通过**(E2E 脚本在仓库根目录,不被 `flutter test` 收集)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. M2 四条验收标准逐条对照
|
||||||
|
|
||||||
|
| # | 验收标准 | 证据 | 结论 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 1 | **跨设备数据一致**:同账号第二设备读到全部数据 | 场景 9:设备 2 新会话读回宠物/体重×2/疫苗/事件/提醒全量一致;§3 psql 证实服务端持久化 | ✓ 通过 |
|
||||||
|
| 2 | **无权限访问被拒**:他人宠物不可见 | 场景 8:账号 B 四路全部 404/40401 且响应体逐字节一致(防枚举);B 列表为空 | ✓ 通过 |
|
||||||
|
| 3 | **并发冲突明确**:不静默覆盖 | 场景 4(疫苗 version 0→1)+ 场景 11(同 version 二次 PATCH → 409/40902,读回证实先写者保留);前端 40902 交互有 widget 测试 | ✓ 通过 |
|
||||||
|
| 4 | **双端测试齐备** | 后端:compose 真实链路 11 场景全绿 + 契约测试基线(报告 20);前端:272 单元/组件测试全绿 + 门禁三命令 0 偏差 | ✓ 通过(真机两项挂起,见 §0) |
|
||||||
|
|
||||||
|
## 6. 契约偏差声明
|
||||||
|
|
||||||
|
**契约偏差数:0 个。**
|
||||||
|
|
||||||
|
本次烟囱对照冻结契约 openapi v1.2.0(报告 19 冻结)逐场景核验:HTTP 状态码
|
||||||
|
(200/201/202/404/409)、业务错误码(40401/40902)、信封结构 `{code, message, data}`、
|
||||||
|
分页正典形态、乐观锁语义、防枚举响应体、埋点逐条结果语义,全部一致,无需修复项。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 环境清理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd patbond-api && docker compose down
|
||||||
|
# Container patbond-pet-1 / patbond-auth-1 / patbond-user-1 / patbond-postgres-1 Removed
|
||||||
|
# Network patbond_default Removed
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 工作仓库状态
|
||||||
|
|
||||||
|
- patbond-flutter dev:`720865b` `test: M2 E2E 烟囱脚本(T2-18 收官)` 已推送 origin/dev
|
||||||
|
- patbond-api:**代码零改动**(仅 compose 起停)
|
||||||
|
- patbond-doc:本报告(28 号),提交与 mkdocs 导航由 T2-19 文档收口统一处理
|
||||||
|
|
||||||
|
## 9. 遗留清单
|
||||||
|
|
||||||
|
1. **真机待补验 ×2**(见 §0 显著标注):Android 真机事件落库观察、SessionTracker
|
||||||
|
30min 会话超时手测——真机可用后按方案 A 补验并追加证据。
|
||||||
|
2. caregiver/viewer 角色的 403/40300 路径本次未走(M2 无邀请入口,T2-10 已用
|
||||||
|
测试数据直构场景覆盖,见报告 20),烟囱层面留待 M3 邀请流程落地后自然覆盖。
|
||||||
|
3. E2E 脚本可在 M3 纳入 CI 定期回归(当前为手动验收工具,与第一迭代建议一致)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Frontend Developer**
|
||||||
|
日期:2026-09-08
|
||||||
|
验收状态:**PASSED**(11/11 场景,契约偏差 0,M2 四条验收标准全部通过;真机两项挂起待补验)
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# 29 M2 收官总结:宠物健康档案
|
||||||
|
|
||||||
|
**迭代周期**:2026-09-07 ~ 2026-09-08(开工分析 + 四波交付)
|
||||||
|
**验收结论**:**PASSED**(E2E 烟囱 11/11、契约偏差 0、M2 四条验收标准全过;真机两项按方案 A 挂起待补验)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 终态对照开工基线(07 号基线快照)
|
||||||
|
|
||||||
|
| 维度 | 开工基线(2026-09-07) | 收官终态(2026-09-08) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| patbond-api 测试 | 82 | **191**(+109) |
|
||||||
|
| patbond-flutter 测试 | 34 | **272**(+238) |
|
||||||
|
| openapi.yaml | v1.0.0,5 路径 | **v1.2.0 冻结**,18 路径/24 操作/45 schema,契约测试锁定零漂移 |
|
||||||
|
| Flyway | V1/V2 | V1~V4(pet_health 8 表 + 字典种子) |
|
||||||
|
| 后端模块 | common/auth/user | + **patbond-pet**(:8083,ADR-009) |
|
||||||
|
| ADR | 001~008 | **001~015** |
|
||||||
|
| 错误码 | 基础段 | +8(40300/40401/40402/40902/40903/40904/42201/42202) |
|
||||||
|
| 档案功能 | Flutter demo 数据 | 全页面族真实后端,demo 消亡 |
|
||||||
|
| 生产埋点事件流 | **恒为零**(接线断链) | 端到端贯通,字典 v2 13 事件,分段持久化队列 |
|
||||||
|
| 迭代报告 | — | 29 份入档挂导航,strict 全程通过 |
|
||||||
|
|
||||||
|
开工时的 2 个证据缺口均闭环:events 契约缺口第一波补录(v1.1.0);CI 全绿不可复核经 Gitea commit status API 建立实查惯例。
|
||||||
|
|
||||||
|
## 1. 交付主线回顾
|
||||||
|
|
||||||
|
- **开工分析**(报告 01~08):8 角色并行评估 + 正式 Reality Checker/Experiment Tracker 复核接管;CONDITIONAL PASS 5 项放行条件;ADR-009~015 拍板
|
||||||
|
- **第一波**(09~12):M1 埋点债清偿(含收口期热修 5 项)+ V3/V4 + pet 骨架;放行条件①②④⑤闭环
|
||||||
|
- **第二波**(13~21):pets 域 18 操作后端纵切 + 契约冻结 v1.2.0 + 契约一致性测试(抓修 1 漂移)
|
||||||
|
- **第三波**(22~27):Flutter 四态页面族接入 + 埋点端到端 + DEBT-1 偿还;三次 compose 实测零偏差
|
||||||
|
- **第四波**(28~29):E2E 烟囱 11 场景收官取证 + 文档收口
|
||||||
|
|
||||||
|
## 2. M2 四条验收标准证据索引
|
||||||
|
|
||||||
|
| 标准 | 证据 |
|
||||||
|
| --- | --- |
|
||||||
|
| 跨设备读取 | 28 号场景 9(新会话五类数据全量读回)+ pet_health 六表 psql 证据 |
|
||||||
|
| 无权限拒绝 | 28 号场景 8(第二账号四路 404/40401 响应逐字节一致防枚举) |
|
||||||
|
| 并发冲突明确 | 28 号场景 4/11(40902 + 先写者保留)+ 前端自动重提 widget 测试 |
|
||||||
|
| 双端测试齐备 | 后端 191 + 前端 272 全绿;契约测试全响应矩阵;门禁三命令 0 偏差 |
|
||||||
|
|
||||||
|
## 3. 协作模式沉淀(本迭代新验证项)
|
||||||
|
|
||||||
|
- **迭代式契约冻结**:草案先行(TODO-FREEZE 标注)→ 实现定型表回填 → 拍板 → 冻结合入 + 字节级快照锁 CI——比第一迭代的一次性冻结更适应多工单纵切
|
||||||
|
- **同仓串行、跨仓并行**的派工纪律避免了全部工作树冲突;agent 中断续跑(半成品检查 + 断点续作)实战有效
|
||||||
|
- 实测取证纪律延续:每波 compose 实测、收官烟囱脚本化、证据脱敏入档
|
||||||
|
|
||||||
|
## 4. 遗留与 M3 建议
|
||||||
|
|
||||||
|
**挂起待补验(真机到位后,预计 0.5 天)**:Android 事件落库观察、SessionTracker 30 分钟手测(脚本在 10 号报告 §5)。
|
||||||
|
|
||||||
|
**M2 范围内遗留**:
|
||||||
|
1. T2-12 §8 三项交互待拍板:单宠直进/切换器、归档入口(listPets 过滤语义)、sterilizedOn 编辑
|
||||||
|
2. auth 域契约测试补齐(机制可复用,S)
|
||||||
|
3. 埋点队列完善:30s 定时冲刷、退避/429(依赖后端限流)、anonymousId 持久化(15 号 §4)
|
||||||
|
4. 09 号契约-实现出入 5 项排期评估(64KB 上限、429 限流等)
|
||||||
|
|
||||||
|
**跨迭代遗留(承自 M1,未变化)**:access token 黑名单、/internal 改 mTLS。
|
||||||
|
|
||||||
|
**M3 方向输入**:
|
||||||
|
- 照片/media 域(ADR-010 剪出项:对象存储选型 → 上传流程 → 宠物头像/疫苗证书/事件附件;health_event_media 表补建)
|
||||||
|
- 照护人邀请/绑定流程(ADR-015 后置项,权限框架已就绪)
|
||||||
|
- 北极星与 H1~H4 假设开始出数(首记日 +8 天成熟,对账 SQL 见 06 号 §6);A/B 前置 8 项按 06 号 §路线推进(目标 M3 末全绿)
|
||||||
|
- health_record_deleted 事件随删除端点设计
|
||||||
|
|
||||||
|
## 5. 收官提交索引
|
||||||
|
|
||||||
|
| 仓库 | 收官 HEAD | CI |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| patbond-api | dev@64c9b72(191 测试) | success |
|
||||||
|
| patbond-flutter | dev@720865b(272 测试 + E2E 脚本) | 待本提交 CI |
|
||||||
|
| patbond-doc | 本收口提交(29 报告 + 看板终态) | strict 通过 |
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# 第二迭代进展看板
|
# 第二迭代进展看板
|
||||||
|
|
||||||
> 目标:M2 宠物健康档案——宠物 CRUD + owner/caregiver/viewer 权限 + 体重/疫苗/健康事件/提醒 + 档案聚合,Flutter 档案页全量替换 demo 数据,依据[开发实施计划](../../development-plan.md)第 7 节。
|
> 目标:M2 宠物健康档案——宠物 CRUD + owner/caregiver/viewer 权限 + 体重/疫苗/健康事件/提醒 + 档案聚合,Flutter 档案页全量替换 demo 数据,依据[开发实施计划](../../development-plan.md)第 7 节。
|
||||||
> 更新日期:2026-09-08(第三波收口,待第四波 E2E 收官)。本页是团队共享的进度事实来源,每波工作交付后更新。
|
> 更新日期:2026-09-08(**M2 收官,验收 PASSED**)。本页是团队共享的进度事实来源。
|
||||||
|
|
||||||
## 当前状态一览
|
## 当前状态一览
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
| ✅ 第一波 | M1 遗留埋点清偿(接线/SessionTracker/page_viewed/持久化队列前置)+ 契约补录 events + Flyway V3/V4 + patbond-pet 骨架 |
|
| ✅ 第一波 | M1 遗留埋点清偿(接线/SessionTracker/page_viewed/持久化队列前置)+ 契约补录 events + Flyway V3/V4 + patbond-pet 骨架 |
|
||||||
| ✅ 第二波 | 后端接口纵切 T2-03~08(pets 域 18 操作)+ 契约冻结 v1.2.0 + 契约一致性测试入 CI |
|
| ✅ 第二波 | 后端接口纵切 T2-03~08(pets 域 18 操作)+ 契约冻结 v1.2.0 + 契约一致性测试入 CI |
|
||||||
| ✅ 第三波 | Flutter 页面接入 T2-11~14(档案 demo 数据消亡、四态齐备)+ 埋点字典 v2 端到端 + DEBT-1 偿还 |
|
| ✅ 第三波 | Flutter 页面接入 T2-11~14(档案 demo 数据消亡、四态齐备)+ 埋点字典 v2 端到端 + DEBT-1 偿还 |
|
||||||
| 🔜 第四波 | T2-18 E2E 烟囱收官战(需真机/联调配合)→ T2-19 收官总结 |
|
| ✅ 第四波 | E2E 烟囱 11/11 全绿、契约偏差 0、M2 四条验收标准全过(报告 28);收官总结见报告 29 |
|
||||||
| ⚠️ 遗留 | 真机补验(Android 事件落库 + SessionTracker 30min,第一波方案 A 挂起);T2-12 §8 三项交互待拍板;auth 域契约测试;埋点队列完善(30s 定时冲刷/退避/anonymousId 持久化) |
|
| ⚠️ 遗留 | 真机补验(Android 事件落库 + SessionTracker 30min,方案 A 挂起);T2-12 §8 三项交互待拍板;auth 域契约测试;埋点队列完善——完整清单见报告 29 §4 |
|
||||||
|
|
||||||
## 测试与契约演进
|
## 测试与契约演进
|
||||||
|
|
||||||
@@ -20,7 +20,8 @@
|
|||||||
| M2 开工基线 | 82 | 34 | v1.0.0(5 路径) |
|
| M2 开工基线 | 82 | 34 | v1.0.0(5 路径) |
|
||||||
| 第一波收口 | 95 | 51 | v1.1.0(+events) |
|
| 第一波收口 | 95 | 51 | v1.1.0(+events) |
|
||||||
| 第二波收口 | 182 | 64 | **v1.2.0 冻结**(18 路径/24 操作/45 schema) |
|
| 第二波收口 | 182 | 64 | **v1.2.0 冻结**(18 路径/24 操作/45 schema) |
|
||||||
| 第三波收口 | **191** | **272** | v1.2.0(契约测试锁定零漂移) |
|
| 第三波收口 | 191 | 272 | v1.2.0(契约测试锁定零漂移) |
|
||||||
|
| **收官(E2E 后)** | **191** | **272**(+E2E 烟囱脚本) | v1.2.0(E2E 逐场景核验偏差 0) |
|
||||||
|
|
||||||
## 已完成(附提交)
|
## 已完成(附提交)
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ nav:
|
|||||||
- 25 体重疫苗模块: development/iterations/iteration-2/25-weights-vaccines-ui-report.md
|
- 25 体重疫苗模块: development/iterations/iteration-2/25-weights-vaccines-ui-report.md
|
||||||
- 26 时间线与提醒页: development/iterations/iteration-2/26-timeline-reminders-report.md
|
- 26 时间线与提醒页: development/iterations/iteration-2/26-timeline-reminders-report.md
|
||||||
- 27 第三波收口: development/iterations/iteration-2/27-wave3-closure.md
|
- 27 第三波收口: development/iterations/iteration-2/27-wave3-closure.md
|
||||||
|
- 28 E2E 烟囱收官: development/iterations/iteration-2/28-e2e-smoke-report.md
|
||||||
|
- 29 M2 收官总结: development/iterations/iteration-2/29-m2-summary.md
|
||||||
- API:
|
- API:
|
||||||
- 契约说明: api/index.md
|
- 契约说明: api/index.md
|
||||||
- 架构:
|
- 架构:
|
||||||
|
|||||||
Reference in New Issue
Block a user