Files
patbond-doc/docs/development/iterations/iteration-3/09-community-foundation-report.md
T
lixi f17e6f1215
CI / docs-build (push) Successful in 34s
docs: M3 第一波收口——报告 09~14 与契约草案入档挂导航
- 09 V5+community 骨架(191→206)、10 埋点队列三项(272→286)、
  11 契约草案(13 路径/19 操作)、12 防泄漏三仓落地、
  13 media MinIO 闭环 + auth 契约测试(→226,抓修 1 漂移)、14 收口总表
- backend-modules.md 更新五模块/六容器口径
- 媒体凭据形态已定型(契约冻结输入),剩余待定型点在 T3-04/05

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 17:13:49 +08:00

121 lines
10 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.
# M3 第一波社区地基施工报告(数据与骨架线:V5 迁移 + patbond-community 骨架)
> 作者:Senior Developer(后端)
> 日期:2026-09-08
> 工单:T3-01Flyway V5 community schema 迁移)、T3-02patbond-community 模块骨架与鉴权接入)
> 代码基线:patbond-api `64c9b72`191 测试全绿)→ 交付 `3c671fc`(206 测试全绿)
> 结论先行:**V5 建 community 全部 8 表,2 条跨 schema 外键(generation_job_id→creation、region_id→platform.regions)按拍板剥离;patbond-community:8084)挂入构建链、自骨架起 /api/v1/** 即接 RS256 校验并纳入 compose 第五容器;干净 postgres:18 上 V1→V5 全量迁移一次成功,全套 206 测试全绿。**
---
## 1. 提交清单
按工单各一逻辑提交,全部已推送 `origin/dev`
| 提交 | 内容 |
| --- | --- |
| `a97814a` | feat: Flyway V5 community schema 基线 + 迁移验证集成测试(T3-01) |
| `3c671fc` | feat: 新建 patbond-community 模块骨架(ADR-017T3-02 |
## 2. Flyway V5:表清单与裁剪对照(T3-01)
### 2.1 V5 结构基线(`patbond-user/src/main/resources/db/migration/V5__community_baseline.sql`
从目标模型 `patbond-doc/docs/database/patbond_postgresql.sql`718~875 行)提取,共建 **8 张表**
| # | 表 | 处置 | 与目标模型的差异 |
| --- | --- | --- | --- |
| 1 | `community.posts` | 建 | **剥离 2 条跨 schema FK**(见 2.2);列全保留,其余约束/索引无差异 |
| 2 | `community.post_media` | 建 | 无差异(`asset_id → media.assets` RESTRICT 保留,media 表 V1 已建;封面部分唯一索引 `uq_post_media_cover` 照建) |
| 3 | `community.comments` | 建 | 无差异(刻意单层平铺,`reply_to_user_id` 支持 @ 回复;幂等列 `client_request_id + request_hash` 照建) |
| 4 | `community.post_likes` | 建 | 无差异(PK (post_id, user_id) 天然幂等) |
| 5 | `community.post_bookmarks` | 建 | 无差异(同上) |
| 6 | `community.user_follows` | 建 | 无差异(含禁自关注 CHECK) |
| 7 | `community.topics` | 建 | 无差异(`name citext UNIQUE`)。**表建功能剪**ADR-018 话题剪出 M3 MVP,但结构按目标模型建;**无种子数据进生产链**(测试断言 topics 为空) |
| 8 | `community.post_topics` | 建 | 无差异 |
其余保留项:全部 CHECK 约束(`ck_posts_publish_state``ck_posts_idempotency``ck_comments_deleted` 等)、Feed 部分索引 `ix_posts_feed (published_at DESC, id DESC) WHERE status='published' AND visibility='public'``uq_posts_author_idempotency`、2 个 `updated_at` 触发器(posts/comments,复用 V1 的 `platform.set_updated_at()`)。到 `identity.users``pet_health.pets``media.assets` 的跨 schema FK 全部保留(三个 schema V1/V3 已存在,共库阶段先例)。
### 2.2 强制裁剪:2 条跨 schema 外键(逐条对照)
按拍板(工单 T3-01,照 V3 剪 4 条 marketplace FK 的先例格式),对应字段保留为**裸可空 uuid 列**,索引照建,迁移文件头注释逐条标明补回时点:
| # | 原定义 | V5 处置 | 补回时点 |
| --- | --- | --- | --- |
| 1 | `posts.generation_job_id → creation.generation_jobs(id) ON DELETE SET NULL` | 剥离;裸列保留,`ix_posts_generation_job` 索引保留 | **M4** 建 creation schema 的迁移补回 |
| 2 | `posts.region_id → platform.regions(id) ON DELETE SET NULL` | 剥离;裸列保留,`ix_posts_region``ix_posts_region_feed` 索引保留 | **M5** 地区体系迁移补回(ADR-018 将 region 剪出 M3 |
说明:`platform.regions` 表 V1 已存在(02 号评估 1.4 节原判「保留」),但 PM 拆解按 ADR-018 范围裁剪把 region 体系整体划入 M5,本工单按拍板剪 FK——列与索引保留,M5 补回约束零成本。
### 2.3 扩展启用
- **`pg_trgm`**:02 号评估发现 V1 只建了 pgcrypto 与 citext,而 `ix_posts_content_trgm`gin, `gin_trgm_ops`)需要 pg_trgm——V5 文件头 `CREATE EXTENSION IF NOT EXISTS pg_trgm` 补齐;postgres:18 官方镜像含 contribTestcontainers 与 compose 均实测无障碍。trgm 索引按 02 号建议照建(M3 无搜索需求,但成本极低、剪了偏离目标模型)。
- **`citext`**V1 已建;V5 以 `IF NOT EXISTS` 幂等重申(迁移日志出现一条「already exists, skipping」提示,无害)。
### 2.4 主键 DEFAULT 的取舍
02 号评估表格建议 V5「去掉主键 `DEFAULT gen_random_uuid()`」,但核对既有链:V1(users)与 V3pets**均保留了该 DEFAULT**,应用侧显式写入 UUIDv7、DB DEFAULT 仅作兜底。V5 与 V1/V3 同规**保留 DEFAULT**(工单要求「照 V3 先例写法」优先于评估建议;两者对运行时行为无差异,应用永远显式供 id)。
## 3. patbond-community 模块骨架(T3-02ADR-017
```text
patbond-community/
├── Dockerfile # 同 user/auth/pet 模式(temurin-17-jreuid 10001,无状态,EXPOSE 8084
├── pom.xml # 挂入父 pom;依赖对齐 petcommon/web/validation/jdbc/jjwt + 测试侧 user jar + Flyway + Testcontainers
└── src/
├── main/java/com/patbond/patbond/community/
│ ├── CommunityApplication.java # Spring Boot 入口
│ ├── config/CommunitySecurityProperties.java # patbond.jwt.public-key
│ ├── config/SecurityConfig.java # BearerAuthFilter 注册到 /api/v1/*order 20
│ ├── config/JacksonConfig.java # 整数字段拒绝小数(与其余服务同规)
│ ├── security/{BearerAuthFilter,JwtVerifier,RsaPublicKeyLoader}.java # RS256 资源侧校验(user/pet 同款第三份复制)
│ ├── web/GlobalExceptionHandler.java # {code,message,data} 信封契约
│ └── controller/HealthController.java # GET /health 探活(含 SELECT 1 连通检查,在 /api/v1 之外)
├── main/resources/application.yml.sample # .sample 模式,默认端口 8084,DB/公钥经环境变量注入
└── test/java/com/patbond/patbond/community/
├── TestcontainersConfiguration.java # postgres:18 @ServiceConnection;测试 classpath 挂 user jar + Flyway 跑全链 V1..V5
├── CommunityApplicationTests.java # 上下文冒烟
├── controller/HealthControllerTest.java # /health 200 + db=up
├── security/BearerAuthIntegrationTest.java # 无 token/畸形/错签/过期 → 401+40101;有效 token 过滤器放行(未实现路由 404+40400)
└── support/TestJwtKeys.java # 运行时生成 RSA 对,无密钥材料入库
```
关键取舍:
- **鉴权自骨架起接入**(与 pet 骨架期不同):T3-02 验收要求无 token/过期 token 返回 401 + 40100 系,故 `BearerAuthFilter`/`JwtVerifier`/`RsaPublicKeyLoader` 随骨架落地(user/pet 同款第三份复制)。02 号评估 P9 建议的「纯 Java 件下沉 common」未进 ADR-016~021 拍板,本单不做,留待后续决策——届时三处复制件切换为共享件、测试全绿即证等价。
- **Flyway 归属不拆**community 生产 classpath 无 Flyway;单迁移链(V1..V5)由 patbond-user 启动统一执行。模块只经 JdbcClient 读写 `community` schema。
- **compose 第五容器**:照 pet 服务块模式(build + .sample 挂载 + 环境变量注入 + 公钥只读挂载);`depends_on` postgres 健康 + user 先起(保证 V5 已执行、community schema 就绪)。
- **作者资料取数**:按 ADR-017/D3-9 方案 Buser 增 /internal 批量公开资料接口),本单只搭骨架不实现。
## 4. compose 五容器验证
`./mvnw -DskipTests package` + `docker compose up -d --build` 实测(既有 pgdata volume,数据库处于 V4):
- **五容器全部 Up**postgreshealthy+ auth + user + pet + community。
- **增量迁移零影响**:user 启动日志 `Migrating schema "public" to version "5 - community baseline"``Successfully applied 1 migration ... now at version v5`——在带 M2 数据的既有库上 V5 增量应用成功(纯增量 schema,对 V1~V4 数据零影响的实证)。
- **community 探活**`GET :8084/health``{"code":0,...,"data":{"status":"ok","db":"up"}}`pet :8083 同绿)。
- **鉴权实证**`GET :8084/api/v1/posts` 无 token → HTTP 401 + `{"code":40101,"message":"token 无效或过期"}`,与验收标准一致。
- 验证后 `docker compose down`(保留 pgdata volume),恢复环境原状。
## 5. 测试数变化:191 → 206+15,0 回归)
| 模块 | 基线 | 交付 | 新增内容 |
| --- | --- | --- | --- |
| patbond-common | 3 | 3 | — |
| patbond-user | 68 | 76 | `CommunityMigrationIntegrationTest` 8 例(schema 存在、8 表齐、pg_trgm 扩展与 trgm 索引、2 条裁剪 FK 确不存在且裸列在、保留 FK 抽查、结构抽查、触发器 2 个、topics 无种子) |
| patbond-auth | 31 | 31 | — |
| patbond-pet | 89 | 89 | — |
| patbond-community | — | 7 | 上下文冒烟 1 + /health 探活 1 + 鉴权集成 5(无 token/畸形/错签/过期 → 401+40101、有效 token 放行)+ 迁移链随上下文启动隐式验证 |
| **合计** | **191** | **206** | `JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw clean test` 一次通过,BUILD SUCCESS |
V1→V2→V3→V4→V5 全量迁移经 Testcontainers 在全新 postgres:18 容器上自动验证通过(user 与 community 两模块的每个 @SpringBootTest 上下文启动即执行全链迁移)。
## 6. 遗留与下一波衔接
- **2 条裁剪 FK 补回**`generation_job_id` 随 M4 creation schema 迁移、`region_id` 随 M5 地区体系迁移(V5 文件头注释已标明)。
- **P9 共享设施下沉**:JWT 校验件已是第三份复制,待拍板后统一下沉 common。
- **作者公开资料 /internal 批量接口**D3-9 方案 B):随 Feed/评论纵切(T3-05 等)在 patbond-user 侧落地。
- **media 上传流程**T3-03):归 patbond-user,本模块只做 asset 只读校验,随帖子纵切接入。
- **CI**:多模块 reactor 自动含 patbond-community`.gitea/workflows/ci.yml` 零改动。
- **backend-modules.md**:已同步加 patbond-community 行与五容器口径(doc 仓工作区修改,随波末收口提交)。