23ce404548
CI / docs-build (push) Successful in 57s
- 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>
15 KiB
15 KiB
功能完成清单
目的:直观呈现哪些功能已完成且有自动化测试、哪些部分完成、哪些尚未开始,方便针对性验证与回归。 维护约定:每波工单合入后由执行人更新本清单;状态以
dev分支 + 门禁全绿为准。 最后更新:2026-09-08(M2 第三波收口:patbond-api64c9b72191 测试、patbond-flutterba50332272 测试,均门禁全绿;三次 compose 实测无契约偏差;M2 条目见第 7~9 节)
图例:✅ 已完成且已测试 | 🟡 部分完成/有已知限制 | ⬜ 未开始
1. 后端基础设施
| 功能 | 状态 | 自动化测试 | 说明 |
|---|---|---|---|
| Spring Boot 3 / JDK 17 基线(ADR-001) | ✅ | 全量门禁 | Boot 3.5.16 + Spring Cloud 2025.0.3 |
| 移除 Nacos,Feign 静态地址(ADR-002) | ✅ | AuthApplicationTests |
干净检出可启动、可测试 |
| Flyway V1 baseline(platform/identity/media) | ✅ | UserPersistenceIntegrationTest.flywayBaselineAppliedOnCleanPostgres16 |
干净 postgres:18 全量执行;dev 种子默认不加载 |
| Testcontainers postgres:18(ADR-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 约束 + 冲突翻译 |
| 手机号唯一 → 40901;E.164 校验(DTO 与 DB CHECK 对齐) | ✅ | UserControllerTest、databaseRejectsNonE164PhoneEvenIfValidationWereBypassed |
|
| 密码校验(含防账号探测的哑 hash 比对) | ✅ | UserControllerTest.verifyPassword* |
|
| 登录失败限制(窗口计数→锁定→423/42300) | ✅ | LoginLockoutIntegrationTest(3 例)+ E2E |
按用户名维度,5 次/15 分钟锁 15 分钟,全部配置项;成功登录重置窗口 |
GET /api/v1/me(Bearer,RS256 公钥本地验签) |
✅ | 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 token:JWT RS256,15 分钟(配置项) | ✅ | 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 token(40101 兜底) |
| 会话记录设备信息(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 核对全部通过):
——已修复(UserProfile.fromJson将phone按非空 String 强转patbond-flutter@845e92f,phone 改为可空,30 测试全绿)。- 注册请求携带的
Idempotency-Key后端暂未实现幂等语义(开发计划仅要求帖子/预约类写接口支持);「刷新重放沿用同键」的设计正确,待后端实现后自动受益。 - 前端每请求携带的
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)、flutter(format/analyze/test,SDK 走 flutter-io.cn + toolcache 缓存)、doc(mkdocs --strict);全部零 GitHub 依赖 |
针对性测试速查
# 全量门禁
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):
# 注册 → 拿令牌对
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 owner;PATCH version 乐观锁 40902;芯片号唯一 40903 |
PetAccessService 三档权限闸口(READ/WRITE/MANAGE,ADR-015) |
✅ | 三角色矩阵 + caregiver 写正向用例 | 防枚举:无关系/不存在/已软删一律 404/40401 响应逐字一致(有测试断言) |
| 体重记录 + cursor 分页(T2-04) | ✅ | 8 例(分页不丢不重/同刻跨页专项) | {items,nextCursor,hasMore} 信封为全 API 分页正典;weight_kg (0,500] |
| 疫苗目录 + 疫苗记录 + 状态机(T2-05) | ✅ | 12 例 | scheduled→completed/cancelled;剂次唯一 40904;规则违反 42201;cancel 释放占位可重建 |
| 健康事件六类 + 时间线分页 + 顶层 PATCH(T2-06) | ✅ | 11 例 | amountCents 整数分非负;禁 float 静默截断;记录级防枚举 40402 |
| 照护提醒四类 + 状态流转(T2-07) | ✅ | 10 例 | pending→completed/dismissed;completed 必带 completedAt(42202);仅数据接口不推送 |
| 档案摘要四聚合(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_preferences,500 条 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 出入清单) |