- 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>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# M2 第一波后端地基施工报告(B 线:V3 迁移 + patbond-pet 骨架 + ADR-013)
|
||||
|
||||
> 作者:Senior Developer(后端)
|
||||
> 日期:2026-09-07
|
||||
> 工单:T2-01(Flyway 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 模块挂入构建链并纳入 compose;health_record_action 已从白名单移除;全套 95 测试在干净 postgres:18 上全绿。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 提交清单
|
||||
|
||||
按拆分建议分三个提交,全部已推送 `origin/dev`:
|
||||
|
||||
| 提交 | 内容 |
|
||||
| --- | --- |
|
||||
| `49299fb` | feat: Flyway V3 pet_health 结构基线 + V4 字典种子 + pet 域错误码(T2-01) |
|
||||
| `0eae1c9` | feat: 新建 patbond-pet 模块骨架(ADR-009,T2-02 前置) |
|
||||
| `58576f8` | refactor: 移除 EventDictionary 的 health_record_action(ADR-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-010:media/附件剪出 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-jre,uid 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 service(build + .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 种子。
|
||||
Reference in New Issue
Block a user