Files
patbond-doc/docs/development/feature-checklist.md
T
lixi 23ce404548
CI / docs-build (push) Successful in 57s
docs: T2-19 文档收口(E2E 前半)——迭代二看板 + 功能清单 M2 增补
- iteration-2/index.md 进展看板:三波交付纪年、测试与契约演进表、遗留清单
- feature-checklist 去掉「第一迭代」限定,新增第 7~9 节(宠物域后端 13 条/
  客户端 10 条/埋点体系 7 条),状态以 dev + 门禁全绿为准
- 待 E2E 收官后补:T2-18 证据归档与 M2 收官总结报告

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

174 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 功能完成清单
> 目的:直观呈现哪些功能**已完成且有自动化测试**、哪些**部分完成**、哪些**尚未开始**,方便针对性验证与回归。
> 维护约定:每波工单合入后由执行人更新本清单;状态以 `dev` 分支 + 门禁全绿为准。
> 最后更新:2026-09-08M2 第三波收口:patbond-api `64c9b72` 191 测试、patbond-flutter `ba50332` 272 测试,均门禁全绿;三次 compose 实测无契约偏差;M2 条目见第 7~9 节)
图例:✅ 已完成且已测试 | 🟡 部分完成/有已知限制 | ⬜ 未开始
## 1. 后端基础设施
| 功能 | 状态 | 自动化测试 | 说明 |
| --- | --- | --- | --- |
| Spring Boot 3 / JDK 17 基线(ADR-001 | ✅ | 全量门禁 | Boot 3.5.16 + Spring Cloud 2025.0.3 |
| 移除 NacosFeign 静态地址(ADR-002 | ✅ | `AuthApplicationTests` | 干净检出可启动、可测试 |
| Flyway V1 baselineplatform/identity/media | ✅ | `UserPersistenceIntegrationTest.flywayBaselineAppliedOnCleanPostgres16` | 干净 postgres:18 全量执行;dev 种子默认不加载 |
| Testcontainers postgres:18ADR-006/008 | ✅ | 所有 user 模块集成测试 + auth E2E | 不依赖本机数据库 |
| 统一响应信封 `{code,message,data}` + 稳定错误码 | ✅ | `ApiResponseTest`、各 Controller 测试 | 错误码表见 `docs/api/openapi.yaml` |
| 跨服务错误码透传(不折叠) | ✅ | `ApiErrorDecoderTest` + **E2E 真实链路** | 第三波修复两处存量缺陷(ErrorDecoder 未进 Feign 子上下文、JDK HttpURLConnection 读不到 401 错误体),此前真实调用中折叠为 503 |
## 2. 用户与凭证(patbond-user
| 功能 | 状态 | 自动化测试 | 说明 |
| --- | --- | --- | --- |
| 用户注册落库(UUIDv7、bcrypt、软删不可见) | ✅ | `UserPersistenceIntegrationTest``UserControllerTest` | 原生 JDBC 读回验证持久性 |
| 用户名唯一(citext 大小写不敏感)→ 40900 | ✅ | `UserControllerTest.duplicateUsernameCheckIsCaseInsensitive` 等 | 依赖 DB 约束 + 冲突翻译 |
| 手机号唯一 → 40901E.164 校验(DTO 与 DB CHECK 对齐) | ✅ | `UserControllerTest``databaseRejectsNonE164PhoneEvenIfValidationWereBypassed` | |
| 密码校验(含防账号探测的哑 hash 比对) | ✅ | `UserControllerTest.verifyPassword*` | |
| 登录失败限制(窗口计数→锁定→423/42300) | ✅ | `LoginLockoutIntegrationTest`(3 例)+ E2E | 按用户名维度,5 次/15 分钟锁 15 分钟,全部配置项;成功登录重置窗口 |
| `GET /api/v1/me`BearerRS256 公钥本地验签) | ✅ | `MeEndpointTest`(5 例:正常/缺失/过期/伪造/垃圾) | 响应恰好 `{userId, username, phone, createdAt}` |
## 3. 认证与会话(ADR-003
| 功能 | 状态 | 自动化测试 | 说明 |
| --- | --- | --- | --- |
| `POST /api/v1/auth/register`(冻结契约 6 字段响应) | ✅ | `AuthControllerTest` + `AuthE2eIntegrationTest.fullAuthVerticalFlow` | 时间字段 ISO 8601 带时区 |
| `POST /api/v1/auth/login`(多设备并行会话) | ✅ | 同上 + `logoutOnOneDeviceKeepsOtherDevicesLoggedIn` | |
| Access tokenJWT RS25615 分钟(配置项) | ✅ | `JwtSignerTest`(5 例) | 私钥仅 auth,公钥仅 user;密钥环境变量注入,仓库零密钥材料 |
| Refresh 会话:SHA-256 摘要落 `auth_sessions` | ✅ | `SessionLifecycleIntegrationTest.createSessionStoresSha256DigestNotPlaintext` | 明文不落库(逐字节断言) |
| `POST /api/v1/auth/refresh`:刷新即轮换 + 轮换链 | ✅ | `refreshRotatesTokenAndChainsSessions` + E2E | 旧行 revoked/rotated/replaced_by 三字段断言 |
| 旧 refresh 重用 → 40102 + 撤销整个 token family | ✅ | `reuseOfRotatedTokenRevokesWholeFamily` + E2E | 并发轮换同样按重用处理 |
| refresh 过期/未知 → 40102 | ✅ | `expiredRefreshTokenIsRejected``unknownRefreshTokenIsRejected` | |
| `POST /api/v1/auth/logout`:仅撤当前会话,幂等 | ✅ | `SessionLifecycleIntegrationTest`(含跨账号撤销不掉用例)+ E2E | 需有效 access token40101 兜底) |
| 会话记录设备信息(X-Device-Id / UA / IP | ✅ | `registerForwardsDeviceIdHeaderToTheSessionRecord` + 会话落库断言 | 前端每请求携带 X-Device-Id,为多设备会话列表备数据 |
| access 过期/伪造 → 40101 | ✅ | `MeEndpointTest``JwtSignerTest`、E2E | |
| `/internal/**` 服务间鉴权(X-Internal-Token | ✅ | `InternalAuthFilterTest`(3 例)+ E2E | 无凭证/错误凭证 401;未配置 fail-closed |
| access token 主动吊销(黑名单) | ⬜ | — | 退出后已签发 access 在剩余 ≤15 分钟内仍有效(jti/sid 已入库备用),见报告 16 §9.1 |
| auth_sessions 过期行清理任务 | ✅ | `SessionCleanupIntegrationTest` | `patbond-api@6528a06`@Scheduled 定时删除死亡超过保留期(默认 30d,即重用检测窗口)的行,间隔/保留期均配置项 |
## 4. API 契约与文档
| 功能 | 状态 | 说明 |
| --- | --- | --- |
| OpenAPI 3 正式契约(5 公开端点+错误码表+会话/锁定策略) | ✅ | `docs/api/openapi.yaml`;与冻结稿字段零偏差;新增 42300 已显著标注 |
| 后端三波迭代报告 | ✅ | `docs/development/iterations/iteration-1/`10、16 等) |
| README 运行手册(密钥生成、环境变量表、新端点) | ✅ | `patbond-api/Readme.md` |
## 5. 客户端(patbond-flutter
| 功能 | 状态 | 说明 |
| --- | --- | --- |
| 珊瑚橙主题迁移 + 认证基础组件(ADR-005) | ✅ | 第二波已交付(dart format / analyze / test 全绿) |
| dio API client + 信封解包 + 错误码映射(含 42300) | ✅ | 第三波并行交付(`patbond-flutter@8d890c0`+`da25804`,报告 17),基于 mock 验证 |
| Splash/登录/注册页 + secure storage + 登录态恢复 + 真实退出 | ✅ | 同上,登录页四态/注册校验 widget 测试锁定 |
| 401 单飞刷新拦截器 | ✅ | `TokenRefresher` 单元测试(刷新单飞、40102 清会话) |
| 与真实后端联调(烟囱测试) | 🟡 | 2026-09-04 手动联调通过(compose 后端 + 本地 Flutter,注册/登录链路无报错);自动化 `integration_test` 留第四波 |
**跨端核对发现(前端线跟进,后端已按 openapi.yaml 核对全部通过)**
1. ~~`UserProfile.fromJson` 将 `phone` 按非空 String 强转~~——**已修复**`patbond-flutter@845e92f`,phone 改为可空,30 测试全绿)。
2. 注册请求携带的 `Idempotency-Key` 后端暂未实现幂等语义(开发计划仅要求帖子/预约类写接口支持);「刷新重放沿用同键」的设计正确,待后端实现后自动受益。
3. 前端每请求携带的 `X-Device-Id` 后端已接入 `auth_sessions.device_id``patbond-api@8bdaf53`)。
## 6. 工程化
| 功能 | 状态 | 说明 |
| --- | --- | --- |
| 后端集成测试门禁(本地) | ✅ | `JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw clean test`82 测试(含埋点 +7 |
| 跨服务真实 HTTP E2E | ✅ | `AuthE2eIntegrationTest`(同 JVM 双服务 + 真实 postgres:18 |
| docker compose 最小编排(postgres:18 + 两无状态服务容器) | ✅ | `patbond-api@ab0265c``./deploy/init-secrets.sh``mvnw -DskipTests package``docker compose up -d --build`;完整冒烟实测通过(register→me→refresh→旧 token 重用 40102→internal 401→logout);用法见 `patbond-api/Readme.md` |
| 可执行镜像构建(repackage exec jar、非 root 运行) | ✅ | 同上;顺带修复无 starter-parent 时 package 产物不可执行 |
| 信封严格化(`success` 派生字段不再上线) | ✅ | `patbond-api@8a79971`,信封恰为 `{code, message, data}` |
| CI 载体(自动执行门禁) | ✅ | 三仓全覆盖:api(mvnw 82 测试,#6 全绿 3m18s)、flutterformat/analyze/testSDK 走 flutter-io.cn + toolcache 缓存)、docmkdocs --strict);全部零 GitHub 依赖 |
## 针对性测试速查
```bash
# 全量门禁
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw clean test
# 只跑会话生命周期 / 锁定 / me 鉴权(user 模块)
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw -pl patbond-user -am test \
-Dtest='SessionLifecycleIntegrationTest,LoginLockoutIntegrationTest,MeEndpointTest' \
-Dsurefire.failIfNoSpecifiedTests=false
# 只跑跨服务 E2E 纵切(auth 模块;-am 必带,避免 ~/.m2 旧 common 快照)
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw -pl patbond-auth -am test \
-Dtest='AuthE2eIntegrationTest' -Dsurefire.failIfNoSpecifiedTests=false
```
手动冒烟(两服务本地起好后,密钥与内部 token 配置见 `patbond-api/Readme.md`):
```bash
# 注册 → 拿令牌对
curl -s -X POST http://127.0.0.1:8081/api/v1/auth/register \
-H 'Content-Type: application/json' \
-d '{"username":"demo_user","phone":"+8613800138000","password":"secret123"}'
# me(换成上一步返回的 accessToken
curl -s http://127.0.0.1:8082/api/v1/me -H "Authorization: Bearer <accessToken>"
# 刷新(旧 refreshToken 随即失效;再用旧值应得 40102)
curl -s -X POST http://127.0.0.1:8081/api/v1/auth/refresh \
-H 'Content-Type: application/json' -d '{"refreshToken":"<refreshToken>"}'
# 退出(撤销当前会话)
curl -s -X POST http://127.0.0.1:8081/api/v1/auth/logout \
-H "Authorization: Bearer <accessToken>" \
-H 'Content-Type: application/json' -d '{"refreshToken":"<refreshToken>"}'
# /internal 无凭证应 401
curl -s -i http://127.0.0.1:8082/internal/users/by-username/demo_user | head -1
```
---
# M2 宠物健康档案(第二迭代)
## 7. 宠物域后端(patbond-pet:8083
| 功能 | 状态 | 自动化测试 | 说明 |
| --- | --- | --- | --- |
| Flyway V3 pet_health 8 表 + V4 字典种子(28 品种/10 疫苗) | ✅ | 迁移验证 8 例(干净 postgres:18 全量 V1..V4 | 4 条 marketplace 跨 schema FK 剥离标注 M5 补回,有测试断言 FK 不存在 |
| patbond-pet 独立模块(ADR-009)挂 pom + compose | ✅ | 骨架测试 + compose 实测 | /health 探活;迁移链仍归 patbond-user 单链 |
| 宠物 CRUD + breeds 目录(T2-03 | ✅ | 23 例(六类路径 + 三角色矩阵) | 创建者自动 primary ownerPATCH version 乐观锁 40902;芯片号唯一 40903 |
| `PetAccessService` 三档权限闸口(READ/WRITE/MANAGEADR-015 | ✅ | 三角色矩阵 + caregiver 写正向用例 | 防枚举:无关系/不存在/已软删一律 404/40401 响应逐字一致(有测试断言) |
| 体重记录 + cursor 分页(T2-04) | ✅ | 8 例(分页不丢不重/同刻跨页专项) | `{items,nextCursor,hasMore}` 信封为全 API 分页正典;weight_kg (0,500] |
| 疫苗目录 + 疫苗记录 + 状态机(T2-05 | ✅ | 12 例 | scheduled→completed/cancelled;剂次唯一 40904;规则违反 42201cancel 释放占位可重建 |
| 健康事件六类 + 时间线分页 + 顶层 PATCHT2-06 | ✅ | 11 例 | amountCents 整数分非负;禁 float 静默截断;记录级防枚举 40402 |
| 照护提醒四类 + 状态流转(T2-07 | ✅ | 10 例 | pending→completed/dismissedcompleted 必带 completedAt42202);仅数据接口不推送 |
| 档案摘要四聚合(T2-08) | ✅ | 12 例(空数据/双时区跨月/cancelled 不计/多宠隔离/零写入红线) | 实时聚合不持久化展示串;tz 参数(IANA)缺省 UTC;无记录 null 语义 |
| 写接口幂等(Idempotency-Key 可选头,四个 POST) | ✅ | 幂等重试用例 | 键派生确定性主键 + ON CONFLICT,零迁移 |
| 契约一致性测试(v1.2.0 字节级快照) | ✅ | 全响应矩阵 + mutation 自证 + 版本守卫 | 契约未声明字段即报漂移;升版须同步快照否则 CI 红;已抓修 1 项漂移(sex 必填) |
| 照片/附件(头像、疫苗证书、事件附件) | ⬜ | — | ADR-010 剪出 M2,待对象存储选型;health_event_media 表未建(纯增量后补零成本) |
| 照护人邀请/绑定流程 | ⬜ | — | ADR-015 后置;权限校验已用测试数据覆盖三角色 |
| auth 域契约测试补齐 | ⬜ | — | 机制可直接复用(报告 20 §建议),另立工单 |
## 8. 宠物域客户端(patbond-flutter
| 功能 | 状态 | 说明 |
| --- | --- | --- |
| pets 数据层(契约 18 操作 DTO/Client/Repository 全覆盖,T2-11 | ✅ | 8 新错误码类型化异常;三服务分端口直连共享 TokenRefresher 单飞;DTO 映射 62 例测试 |
| 宠物列表/详情/建档/编辑页真实数据(T2-12) | ✅ | 四态齐备有 widget 测试;40902 自动取新 version 重提;40903 字段级报错;品种目录 + 自定义互斥;demo 数据消亡 |
| 体重录入 + 历史列表(cursor 分页,T2-13) | ✅ | 契约区间前端校验 + 后端兜底;加载更多/翻页失败保留重试 |
| 疫苗登记/列表 + 完成/取消流转 + 厂商批号补录(T2-13/14) | ✅ | 状态-日期规则双重前端拦截 + 42201/40904 兜底;按系列分组三态 TagPill |
| 摘要接数(最新体重/疫苗进度/下一针/月度花费)替换 demo 展示串 | ✅ | null → 空态而非 0/0(测试锁定);月度花费透传设备时区 tz |
| 健康事件时间线(六类、按月分组、元/分换算)+ 录入/编辑(T2-14) | ✅ | 金额换算单测锁定;40902 自动重提 |
| 照护提醒列表/创建/完成/忽略(T2-14) | ✅ | 逾期红标双通道;档案页「健康提醒」卡真实数据驱动(demo 硬编码移除) |
| 跨设备读取验收(M2 验收标准) | ✅ | compose 实测:同账号新会话全量可见;第二账号四路访问均 40401 |
| DEBT-1 TagPill 对比度债偿还(ADR-014) | ✅ | 深变体映射四组全达 WCAG AA;既有调用零参数回归 |
| 单宠直进/切换器、归档入口、sterilizedOn 编辑 | 🟡 | 三项交互细节待拍板(报告 23 §8) |
## 9. 埋点体系(M2 演进)
| 功能 | 状态 | 说明 |
| --- | --- | --- |
| M1 遗留清偿:生产接线/eventId v7/SessionTracker/page_viewed | ✅ | 第一波交付(报告 10,12/12 验收);生产事件流自 M1 以来首次非零 |
| events 契约补录(v1.1.0)+ 上传端口纠正 + 毒丸批次防护 | ✅ | 4xx 永久拒绝不重试;离开前台冲刷(低活跃用户事件不再滞留) |
| 分段持久化队列(shared_preferences500 条 at-least-once | ✅ | 冷启动恢复离线积压;损坏段容错;按段拼批 ≤50 |
| 事件字典 v2 白名单(pet 域 3 + health_record 域 7 | ✅ | 后端白名单 + 边界测试(api@64c9b72);page_viewed 正稿核对零修正 |
| 客户端挂接:pet 域 3 事件 + health_record 域 6 事件 + pet_form 等页名 | ✅ | 强类型封装(pet_analytics/health_record_analytics);deleted 留待删除端点 |
| Android 真机落库验证 + SessionTracker 30min 手测 | 🟡 | 桌面端全链路已通(platform 枚举拒绝属契约内);真机验证按方案 A 挂起待设备 |
| 队列完善:30s 定时冲刷、退避/429、anonymousId 持久化 | ⬜ | 报告 15 §4 遗留;429 依赖后端限流实现(报告 09 出入清单) |