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

121 lines
9.1 KiB
Markdown
Raw Permalink 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.
# M2 第一波后端地基施工报告(B 线:V3 迁移 + patbond-pet 骨架 + ADR-013
> 作者:Senior Developer(后端)
> 日期:2026-09-07
> 工单:T2-01Flyway V3/V4)、T2-02 前置(patbond-pet 模块骨架)、ADR-013 执行、错误码预置
> 代码基线:patbond-api `0d81c38`82 测试全绿)→ 交付 `58576f8`(95 测试全绿)
> 结论先行:**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.sql`333~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_dose``uq_pet_primary_owner``uq_pets_microchip` 等)、4 个 `updated_at` 触发器(复用 V1 的 `platform.set_updated_at()`,无需新建函数)。`pet_owners.user_id``health_events.created_by_user_id``identity.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
```text
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 漏斗事件保留为完整白名单。既有测试无一引用该事件,零测试改动;新增 `EventDictionaryTest`3 例)锁定移除后的白名单边界。
- `ErrorCode`patbond-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 FK**M5 迁移 marketplace schema 的版本迁移中补回(V3 文件注释已标明)。
- **CI**`.gitea/workflows/ci.yml``./mvnw -B clean test`,多模块 reactor 自动含 patbond-pet,无需改动;push 后 CI 状态由波末闭环核对。
- **正典字典数据**:D2-6 产品侧供稿后以后续迁移替换/扩充 V4 种子。