a611acb358
CI / docs-build (push) Successful in 32s
- 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>
122 lines
13 KiB
Markdown
122 lines
13 KiB
Markdown
# M3 契约冻结报告(T3-10:community/media 域合入正典 v1.3.0)
|
||
|
||
> 作者:API 契约工程师
|
||
> 日期:2026-09-09
|
||
> 工单:T3-10(契约冻结,第二波收口)
|
||
> 输入:草案 `openapi-community-draft.yaml` + 11 号草案说明;定型表 13(媒体凭据)/ 15(帖子生命周期)/ 16(FeedCard/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 | +4(PostIdParam/AssetIdParam/UserIdParam/IdempotencyKeyRequiredHeader) |
|
||
| responses | 6 | 13 | +7(PostNotFound/CommentNotFound/MediaNotFound/UserNotFound/PostAccessDenied/IdempotencyPayloadMismatch/MediaNotReady) |
|
||
| 错误码 | 19 | 28 | +9(40301/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 | GET:401、404/40403;PATCH:400、401、403/40301、404/40403+40401+40405、409/40902、422/42203;DELETE:401、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 | GET:400、401、404/40403;POST:400、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/40406;PUT 另有 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-op(200,version 照常 +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 显式声明 40401(petId)与 40405(asset)双例;Post.updatedAt 注明「互动计数亦推动该值,判编辑以 version 为准」 | 15 号 §2.3 复用码行为 + 17 号 §3 记录在案行为 |
|
||
|
||
### 3.3 Feed / 作者域(依据:16 号报告 §2/§3/§6)
|
||
|
||
| # | 草案 | 冻结 | 依据 |
|
||
| --- | --- | --- | --- |
|
||
| F1 | AuthorSummary required `[userId, nickname]` | **required 收为 `[userId]`**,nickname nullable(null 仅降级/墓碑;正常路径恒非空语义写入描述) | 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-op(following 恒 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 形态确认 | 无 updatedAt(M3 无评论编辑,schema 描述注明);replyToUser 为完整 AuthorSummary(required 收敛沿 `[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 / 42205(42205 在实现侧属 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 未配置降级为 null)vs 草案 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,不触碰本次冻结面。
|