Files
lixi a611acb358
CI / docs-build (push) Successful in 32s
docs: M3 第二波收口——报告 15~20 入档挂导航
- 15~17 社区后端纵切三单(帖子/Feed 作者链路/评论互动关注,226→310)
- 18 契约冻结 v1.3.0(31 路径/43 操作,26 项修正照单全收)
- 19 快照同步与全仓契约矩阵(173 格零漂移,修 allOf 校验盲区,→325)
- 20 收口总表:定型语义汇总(第三波接入依据)与质量事件记录

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 11:38:42 +08:00

122 lines
13 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.
# M3 契约冻结报告(T3-10community/media 域合入正典 v1.3.0
> 作者:API 契约工程师
> 日期:2026-09-09
> 工单:T3-10(契约冻结,第二波收口)
> 输入:草案 `openapi-community-draft.yaml` + 11 号草案说明;定型表 13(媒体凭据)/ 15(帖子生命周期)/ 16FeedCard/AuthorSummary/ 17(评论/互动/关注)
> 结论先行:**community/media 域按四份定型表照单全收合入 `docs/api/openapi.yaml`1.2.0 → 1.3.0:新增 13 路径 / 19 操作 / 27 schemas / 4 参数 / 7 响应组件 / 9 错误码,正典总量 31 路径 / 43 操作 / 72 schemas。草案→冻结修正 26 项逐条对照见 §3;四份定型表间未发现矛盾(两处表面分歧均已由报告自身声明口径,见 §4);草案 10 处 TODO-FREEZE 全部回填删除;YAML 解析、$ref 全解析、operationId 唯一性、`mkdocs build --strict` 全部通过。api 侧字节级快照同步为本冻结的硬依赖,由后续 api 侧工单执行(§5)。**
---
## 1. 冻结版本与总量
| 项 | 1.2.0 | 1.3.0 | 增量 |
| --- | --- | --- | --- |
| 路径 | 18 | 31 | +13 |
| 操作 | 24 | 43 | +19 |
| schemas | 45 | 72 | +27 |
| parameters | 4 | 8 | +4PostIdParam/AssetIdParam/UserIdParam/IdempotencyKeyRequiredHeader |
| responses | 6 | 13 | +7PostNotFound/CommentNotFound/MediaNotFound/UserNotFound/PostAccessDenied/IdempotencyPayloadMismatch/MediaNotReady |
| 错误码 | 19 | 28 | +940301/40403/40404/40405/40406/40905/42203/42204/42205 |
| servers | 3 | 4 | +:8084 patbond-community |
| tags | 6 | 12 | +media/posts/feed/comments/interactions/follows |
info 头同步动作:更新履历补 1.3.0 段;错误码表按码位序并入 9 码;新增「Community / Media 域约定」段(幂等域差异、媒体两步上传与签名读语义、防枚举码族、互动面=公开面、ADR-018 裁剪与 `/internal` 不入契约)——11 号报告 §6-5 要求的「Idempotency-Key 必带 + 比对 hash + ≤128 与 pets 域差异在 info 头显式成文」已落。
## 2. 冻结端点总表(13 路径 / 19 操作)
| # | 端点 | 操作 | 服务 | 成功 | 错误面(HTTP/业务码) |
| --- | --- | --- | --- | --- | --- |
| 1 | `/api/v1/media/uploads` | POST | user :8082 | 201 凭据 | 400/40000、401/40101 |
| 2 | `/api/v1/media/uploads/{assetId}/complete` | POST | user :8082 | 200 asset | 400/40000、401、404/40405、422/42205 |
| 3 | `/api/v1/posts` | POST | community :8084 | 201 Post | 400、401、404/40401+40405、409/40905、422/42203 |
| 4 | `/api/v1/posts/{postId}` | GET / PATCH / DELETE | community | 200 | GET401、404/40403PATCH400、401、403/40301、404/40403+40401+40405、409/40902、422/42203DELETE401、403、404 |
| 5 | `/api/v1/me/posts` | GET | community | 200 分页 Post | 400、401 |
| 6 | `/api/v1/feed` | GET | community | 200 分页 FeedCard | 400、401 |
| 7 | `/api/v1/posts/{postId}/comments` | GET / POST | community | 200 / 201 | GET400、401、404/40403POST400、401、404/40403+40406、409/40905 |
| 8 | `/api/v1/comments/{commentId}` | DELETE | community | 200 Void | 401、403/40301、404/40404 |
| 9 | `/api/v1/posts/{postId}/like` | PUT / DELETE | community | 200 LikeState | 401、404/40403 |
| 10 | `/api/v1/posts/{postId}/bookmark` | PUT / DELETE | community | 200 BookmarkState | 401、404/40403 |
| 11 | `/api/v1/me/bookmarks` | GET | community | 200 分页 FeedCard | 400、401 |
| 12 | `/api/v1/users/{userId}/follow` | PUT / DELETE | community | 200 FollowState | 401、404/40406PUT 另有 422/42204 |
| 13 | `/api/v1/users/{userId}/follow-stats` | GET | community | 200 FollowStats | 401、404/40406 |
全部端点强制 Bearer 鉴权。裁剪不出现(ADR-018):话题端点、关注/粉丝列表、作者主页帖子列表、`region`/`generationJob`/`visibility=followers|private``/internal/users/profiles` 为服务间接口,**不入公网契约**(形态以 16 号报告 §3 为准)。
## 3. 草案 → 冻结修正项对照(26 项,照单全收)
### 3.1 媒体域(依据:13 号报告 §3/§4)
| # | 草案 | 冻结 | 依据 |
| --- | --- | --- | --- |
| M1 | 读取侧 URL 形态留白(公共读 vs 签名读) | **私有桶 + 预签名 GET**(TTL 默认 1 小时,配置项);MediaAsset.url / PostMediaItem.url / AuthorSummary.avatarUrl 描述统一注明「时效性、每次响应现签、客户端不得持久化、过期即重取」 | 13 号偏差 #1 + 用户拍板 |
| M2 | complete「校验失败置 failed」一刀切 | 对象不存在 → 422/42205 **保持 uploading 可重试**;对象存在但大小/类型不符 → 置 failed 终态 422/42205 | 13 号偏差 #2 |
| M3 | 「有 sha256 则一并核」 | sha256 **照收照存,M3 不核验**(字段描述改写;后续经存储侧 checksum 补齐不改契约形态) | 13 号偏差 #3 |
| M4 | complete 未声明 400 | 补 400/ValidationError(非 UUID assetId | 13 号偏差 #4 |
| M5 | purpose 白名单待定 | 定 `post_image` 一项;P6 扩展为向后兼容枚举追加 | 13 号偏差 #5 |
| M6 | mime 白名单与 HEIC 待定 | 定 jpeg/png/webp**不收 HEIC** | 13 号偏差 #6 |
| M7 | byteSize 上限草案 10 MiB | 定 10485760(配置项) | 13 号偏差 #7 |
| M8 | requiredHeaders「键集草案态」 | 定型为恒且仅 `{"Content-Type": <mimeType>}` 一键,**并入 required**expiresAt TTL 10 分钟维持 | 13 号 §3 定型表 |
### 3.2 帖子域(依据:15 号报告 §2/§4)
| # | 草案 | 冻结 | 依据 |
| --- | --- | --- | --- |
| P1 | Post.author 占位争议(T3-04 曾落 authorId 裸字段) | **Post.author = AuthorSummary**T3-05 回填闭环,authorId 不出现) | 15 号偏差 #1 + 16 号偏差 #7(同一事项两端) |
| P2 | status 对作者是否露 hidden 留白 | **不露**hidden/archived 对作者读写一律 404/40403,枚举保持 `[draft, published]`,权限矩阵写入 getPost 描述 | 15 号偏差 #2 |
| P3 | 「其余迁移 400/40000」 | **published→published 为幂等 no-op200version 照常 +1**400 只留给 draft/hidden/archived 目标值 | 15 号偏差 #3 |
| P4 | 幂等重试撞已删首帖未覆盖 | 同键同 hash 撞已删首帖 → 404/40403,写入 IdempotencyKeyRequiredHeader 描述 | 15 号偏差 #4 |
| P5 | request_hash 规范化细则未定 | 「hash 对象是规范化后的创建命令(trim、缺省展开),语义等价即命中」写入头参数描述与 info 头 | 15 号偏差 #5 |
| P6 | PostMediaItem.url required 与降级冲突 | **维持 required + 运维前提**(生产恒配置),描述注明现签与 TTL | 15 号偏差 #6(报告建议后者) |
| P7 | PATCH media 整组替换「草案态」 | 定型确认,删标注:字段出现即删旧插新、`[]` 清空、缺席不动 | 15 号 §2.6(草案预设确认) |
| P8 | isCover 全 false「展示层取 position 0」 | 改为**写侧落库置真**:库内恒有唯一封面行;PostMediaAttachRequest/PostMediaItem 描述同步 | 15 号 §2.6 |
| P9 | —(草案未列) | createPost/updatePost 404 显式声明 40401petId)与 40405asset)双例;Post.updatedAt 注明「互动计数亦推动该值,判编辑以 version 为准」 | 15 号 §2.3 复用码行为 + 17 号 §3 记录在案行为 |
### 3.3 Feed / 作者域(依据:16 号报告 §2/§3/§6)
| # | 草案 | 冻结 | 依据 |
| --- | --- | --- | --- |
| F1 | AuthorSummary required `[userId, nickname]` | **required 收为 `[userId]`**nickname nullablenull 仅降级/墓碑;正常路径恒非空语义写入描述) | 16 号偏差 #1 + 用户拍板 |
| F2 | contentPreview「200 字符 + 完整边界截断」 | **200 Unicode 码点、码点边界截断(增补面字符不劈)、不加省略号** | 16 号偏差 #2 |
| F3 | FeedCard 是否带图列表待定 | **只带 coverImage + mediaCount**0~9);裁剪面(无 content 全文/petId/visibility/version/media 整组/created/updated)写入 schema 描述 | 16 号偏差 #3 |
| F4 | bio/username/墓碑待定 | **不补 bio、不露 username**;墓碑 = id-only 形态(`{userId, nickname: null, avatarUrl: null}`),与降级同形 | 16 号偏差 #4 |
| F5 | 封面「isCover 优先→position 0 兜底」 | 读侧**只认唯一 is_cover 行**(兜底已在写侧完成),FeedCard.coverImage 描述改写 | 16 号偏差 #5 |
| F6 | likedByMe/bookmarkedByMe「批量查询」 | 实现为内联 EXISTS——契约无感知,仅在此记录,条文不动 | 16 号偏差 #6 |
| F7 | avatarUrl 示例为公共读稳定 URL 形态 | 示例删除,描述改为预签名 GET 语义(与 M1 同源) | 16 号 §3 + 13 号偏差 #1 |
### 3.4 评论 / 互动 / 关注域(依据:17 号报告 §2/§5)
| # | 草案 | 冻结 | 依据 |
| --- | --- | --- | --- |
| C1 | 帖子不可见 404(未提作者草稿) | **互动面 = 帖子公开面**:作者本人草稿在评论(读写)与 like/bookmark 全部路径同样 404/40403——comments GET/POST、like/bookmark PUT/DELETE 六处描述逐一补「含作者本人草稿」,并入 info 头与 40403 错误表行 | 17 号偏差 #1 |
| C2 | replyToUserId 校验语义未定 | 目标须为存活用户,不存在/注销合并 **404/40406**createComment 404 双例:40403/40406 | 17 号偏差 #2 |
| C3 | unfollow 未提自取关 | **自取关 200 幂等 no-opfollowing 恒 false**;42204 只在 PUT,双端描述与错误表行写明 | 17 号偏差 #3 + 用户拍板 |
| C4 | 42205 列为「草案新增」 | 42205 属 T3-03 已启用码,口径修正;对 1.3.0 契约错误码表仍是本次新收录(1.2.0 表中无此码) | 17 号偏差 #4 |
| C5 | 幂等重试撞已删首评未覆盖 | 404/40404,与帖子域 40403 平行写入 IdempotencyKeyRequiredHeader 描述 | 17 号偏差 #5 |
| C6 | Comment 形态确认 | 无 updatedAtM3 无评论编辑,schema 描述注明);replyToUser 为完整 AuthorSummaryrequired 收敛沿 `[userId]`,含降级 id-only 形态) | 17 号偏差 #6 |
| C7 | 评论删除权限 | **仅评论作者可删——帖主不可删他人评论(D3-7 首版不做)**在 deleteComment 描述显式写明;对可见评论的非作者(含帖主)403/40301 | 17 号 §2.1 + 用户拍板 |
### 3.5 错误码收录裁定(用户拍板全收)
新收录 9 码:40301 / 40403 / 40404 / 40405 / 40406 / 40905 / 42203 / 42204 / 4220542205 在实现侧属 T3-03 既有,但 1.2.0 契约表无此码,故按实际入 1.3.0 表)。**40400 不复用**:该码已承担四服务 NoResourceFound「路由级资源不存在」兜底语义,关注/回复目标缺失独立取 40406,理由成文进错误码表行。复用既有码(40000/40101/40401/40902/50000/50300)不新增行、语义不动。
## 4. 定型表间一致性核验(未发现矛盾)
逐对交叉核验四份定型表,两处表面分歧均已由报告自身声明口径,不构成矛盾:
1. **15 号(draft 对作者可见)vs 17 号(作者草稿在互动路径 404)**:17 号 §2.3 显式声明为「收窄而非矛盾」——可见性回答「能不能看」,互动门禁回答「能不能社交」。冻结采两者:getPost 描述保留作者可见 draft,互动六端点补「含作者本人草稿」。
2. **15 号(PostMediaItem.url 未配置降级为 nullvs 草案 required**15 号偏差 #6 自身给出两选项并建议「维持 required + 运维前提」,16 号 coverImage 的同规注记同源。冻结采建议项:url 保持 required,描述注明运维前提。
## 5. 冻结纪律重申
1. **本文件即契约**1.3.0 起 community/media 域 13 路径进入冻结面——任何字段/语义变更须显著上报、两端同步;错误码只增不改义、永不复用改号;裁剪字段/端点按纯增量补入(ADR-010/ADR-018 先例)。
2. **api 侧字节级快照同步是本冻结的硬依赖**patbond-api 现有契约一致性测试持有 v1.2.0 字节级快照(至少 patbond-pet 与 patbond-auth 的 `src/test/resources/contract/` 两处复制,13 号报告 §8 亦要求 T3-10 冻结时同步),**本仓升版 1.3.0 后,api 侧快照未同步前其快照守卫测试将保持红灯(CI 红)**——这是防漂移门禁按设计生效,不是事故。快照同步(连同 community 域契约矩阵测试 T3-11 的入场)由**后续 api 侧工单**执行,本报告仅冻结契约本体并注明该依赖顺序:先本仓合入推送,再 api 侧同字节复制快照。
3. **草案文件处置**`openapi-community-draft.yaml` 与 11 号说明保留原地作为过程档案,不再维护;此后一切消费方(SDK/客户端/契约测试)以 `docs/api/openapi.yaml` v1.3.0 为唯一权威。
4. **校验通过项**YAML 解析、283 处 $ref 全解析、43 个 operationId 无重复、全操作 security 声明齐、草案 10 处 TODO-FREEZE 归零、`mkdocs build --strict` 通过。
## 6. 遗留与交接
- api 侧:快照同步 + community 契约矩阵测试(见 §5-2,后续工单)。
- 本仓:本报告(18 号)随波末统一挂导航入档;mkdocs.yml 本次不动。
- 头像上传口子(purpose 扩 user_avatar)与设置昵称端点随 P6 拍板另立工单,届时按「枚举追加 + 新端点」纯增量升 1.4.x,不触碰本次冻结面。