Files
lixi 60258324e4
CI / docs-build (push) Successful in 2m3s
docs: M2 第一波收口——报告 09/10/11 入档挂导航
- 09 契约补录 events(关闭 D-1/放行条件②,另含实现与旧规范 5 处出入记录)
- 10 Flutter 埋点修复(接线+三偏差+SessionTracker+page_viewed,34→51 测试,12/12 验收)
- 11 后端地基(V3/V4 迁移 8 表+种子、4 条跨 schema FK 剥离、patbond-pet 骨架、ADR-013 执行,82→95 测试)
- mkdocs build --strict 通过

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

9.1 KiB
Raw Permalink Blame History

M2 第一波后端地基施工报告(B 线:V3 迁移 + patbond-pet 骨架 + ADR-013

作者:Senior Developer(后端) 日期:2026-09-07 工单:T2-01Flyway V3/V4)、T2-02 前置(patbond-pet 模块骨架)、ADR-013 执行、错误码预置 代码基线:patbond-api 0d81c3882 测试全绿)→ 交付 58576f895 测试全绿) 结论先行:V3 建 8 表(health_event_media 按 ADR-010 不建),4 条 marketplace 跨 schema 外键全部剥离;patbond-pet 模块挂入构建链并纳入 composehealth_record_action 已从白名单移除;全套 95 测试在干净 postgres:18 上全绿。


1. 提交清单

按拆分建议分三个提交,全部已推送 origin/dev

提交 内容
49299fb feat: Flyway V3 pet_health 结构基线 + V4 字典种子 + pet 域错误码(T2-01)
0eae1c9 feat: 新建 patbond-pet 模块骨架(ADR-009T2-02 前置)
58576f8 refactor: 移除 EventDictionary 的 health_record_actionADR-013

流程说明iteration-2/08 规划中 Flyway 迁移属「短命分支 + PR 合入 dev」的推荐实践;本次第一波经用户拍板直接推 dev,特此注明。

2. Flyway V3/V4:表清单与裁剪对照

2.1 V3 结构基线(patbond-user/src/main/resources/db/migration/V3__pet_health_baseline.sql

从目标模型 patbond-doc/docs/database/patbond_postgresql.sql333~554 行)原样提取,共建 8 张表

# 处置 与目标模型的差异
1 pet_health.breeds 无差异
2 pet_health.pets 无差异(avatar_asset_id FK 到 media.assets 保留——media 表 V1 已建,仅上传流程未实现,列 M2 不写入)
3 pet_health.pet_owners 无差异(含 owner/caregiver/viewer 角色约束与 primary owner 部分唯一索引,ADR-015 权限模型的数据基础)
4 pet_health.pet_weight_records 无差异
5 pet_health.vaccine_catalog 无差异
6 pet_health.pet_vaccinations 剥离 2 条跨 schema FK(见 2.2);列全保留
7 pet_health.health_events 剥离 2 条跨 schema FK(见 2.2);列全保留
8 pet_health.care_reminders 无差异
pet_health.health_event_media 不建 ADR-010media/附件剪出 M2;该表 asset_id 为 NOT NULL FK 到 media.assets 且 media 上传流程零代码,与 pet 域业务强耦合无意义。纯增量表,待 media 专项落地时以新版本迁移补建,零成本

其余保留项:全部 CHECK 约束、部分唯一索引(uq_pet_vaccination_doseuq_pet_primary_owneruq_pets_microchip 等)、4 个 updated_at 触发器(复用 V1 的 platform.set_updated_at(),无需新建函数)。pet_owners.user_idhealth_events.created_by_user_ididentity.users 的跨 schema FK 保留(与 V1 中 media.assets.owner_user_id 先例一致,共库阶段成立)。

2.2 强制裁剪:4 条 marketplace 跨 schema 外键(逐条对照)

bootstrap SQL 第 1156~1166 行(现实核查已证实行号)以 ALTER TABLE 追加的 4 条约束,V3 全部剥离,对应字段保留为裸可空 uuid 列,索引照建:

# 约束名 原定义 V3 处置
1 fk_vaccinations_provider pet_vaccinations.provider_id → marketplace.providers(id) ON DELETE SET NULL 剥离;provider_id uuid 裸列保留,ix_vaccinations_provider 索引保留
2 fk_vaccinations_booking pet_vaccinations.booking_id → marketplace.bookings(id) ON DELETE SET NULL 剥离;booking_id uuid 裸列保留,ix_vaccinations_booking 索引保留
3 fk_health_events_provider health_events.provider_id → marketplace.providers(id) ON DELETE SET NULL 剥离;裸列 + ix_health_events_provider 保留
4 fk_health_events_booking health_events.booking_id → marketplace.bookings(id) ON DELETE SET NULL 剥离;裸列 + ix_health_events_booking 保留

迁移文件头部注释已逐条列出并标明「M5 迁移 marketplace schema 时以新版本迁移补回」。集成测试断言这 4 条 FK 确不存在(防照抄回归)。

2.3 V4 字典种子(V4__pet_health_dictionary_seed.sql

按 02 号评估建议采用「V3 结构 + V4 种子」划分:breeds/vaccine_catalog 是应用 FK 指向的生产参考数据,走正式迁移链而非 db/dev(与开发 fixture 性质不同)。

  • breeds:28 条(犬 16 + 猫 12,常见品种,含「中华田园犬/猫」兜底项)
  • vaccine_catalog:10 条(犬 6:二/四/五/八联、狂犬、犬窝咳;猫 4:三联、狂犬、白血病、衣原体)
  • 正典目录内容与量级按 D2-6 由产品侧供稿,届时以后续迁移追加/修订

3. patbond-pet 模块骨架(ADR-009

patbond-pet/
├── Dockerfile                      # 同 user/auth 模式(temurin-17-jreuid 10001,无状态)
├── pom.xml                         # 挂入父 pom,依赖对齐既有模块(common/web/validation/jdbc + Testcontainers
└── src/
    ├── main/java/com/patbond/patbond/pet/
    │   ├── PetApplication.java             # Spring Boot 入口
    │   ├── controller/HealthController.java # GET /health 探活(含 SELECT 1 连通检查)
    │   └── web/GlobalExceptionHandler.java  # 同一 {code,message,data} 信封契约
    ├── main/resources/application.yml.sample # .sample 模式,默认端口 8083,DB 经环境变量注入
    └── test/java/com/patbond/patbond/pet/
        ├── TestcontainersConfiguration.java # postgres:18 @ServiceConnection
        ├── PetApplicationTests.java         # 上下文启动冒烟
        └── controller/HealthControllerTest.java # /health 200 + db=up 断言

关键取舍:

  • Flyway 归属不拆pet 模块不携带 Flyway。单一迁移链(V1..V4,含 pet_health 基线)仍由 patbond-user 启动时统一执行——共库单 flyway_schema_history,拆链需为新模块配独立 history 表,收益为零。pet 模块只经 JdbcClient 读写 pet_health schema(第二波接口落地时)。
  • compose 编排已纳入:既有模式是每服务一个 compose servicebuild + .sample 挂载 + 环境变量注入),pet 照此加入;depends_on postgres 健康 + user 先起(保证迁移已执行、pet_health schema 就绪)。
  • 鉴权后置第二波:骨架暂无 /api/v1 业务端点,故未接入 JWT 校验;/health 刻意放在 /api/v1 之外(基础设施探针无业务数据)。第二波接口落地时按 user 模块同一约定接入 RS256 本地验签(BearerAuthFilter 模式,届时评估下沉 common 或复制)。

4. ADR-013 执行与错误码预置

  • EventDictionary 移除 health_record_action 白名单项,注释同步改写(引 ADR-013);page_viewed 与 v1 auth 漏斗事件保留为完整白名单。既有测试无一引用该事件,零测试改动;新增 EventDictionaryTest3 例)锁定移除后的白名单边界。
  • ErrorCodepatbond-common)按 02 号建议预置 4 个 pets 域错误码,延续既有编号段、不重编号:
code 枚举名 HTTP 语义
40300 PET_ACCESS_DENIED 403 对可见宠物无相应操作权限(如 viewer 尝试写)
40401 PET_NOT_FOUND 404 宠物不存在或调用者不可见(防 ID 枚举)
40402 RECORD_NOT_FOUND 404 宠物下的记录不存在
40902 VERSION_CONFLICT 409 乐观锁版本冲突

当前无消费方,第二波接口纵切直接使用;契约(openapi.yaml)本波不动,随 T2-09 冻结时一并写入。

5. 测试数变化:82 → 95(+13,0 回归)

模块 基线 交付 新增内容
patbond-common 3 3
patbond-user 48 59 PetHealthMigrationIntegrationTest 8 例(schema 存在、8 表齐、结构抽查、4 条 marketplace FK 确不存在、触发器 4 个、V4 种子非空与抽查);EventDictionaryTest 3 例
patbond-auth 31 31
patbond-pet 2 上下文冒烟 + /health 探活
合计 82 95 JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw clean test 一次通过,BUILD SUCCESS

V1→V2→V3→V4 全量迁移经 Testcontainers 在全新 postgres:18 容器上自动验证通过(每个 @SpringBootTest 上下文启动即执行全链迁移)。

6. 遗留与下一波衔接

  • T2-02 剩余部分(第二波):pet 模块接入 JWT 资源侧校验(BearerAuthFilter/JwtVerifier/UuidV7 下沉 common 或复制的决策届时定)、当前用户解析注入。
  • health_event_media:随 media 专项(对象存储选型拍板后)以新迁移补建。
  • 4 条 marketplace FKM5 迁移 marketplace schema 的版本迁移中补回(V3 文件注释已标明)。
  • CI.gitea/workflows/ci.yml./mvnw -B clean test,多模块 reactor 自动含 patbond-pet,无需改动;push 后 CI 状态由波末闭环核对。
  • 正典字典数据:D2-6 产品侧供稿后以后续迁移替换/扩充 V4 种子。