- 13~18 后端纵切六单报告(T2-03~08,测试 95→182) - 14 + openapi-pets-draft.yaml 契约起草档案 - 19 契约冻结报告(v1.2.0,22 项草案修正对照) - 20 契约一致性测试(快照机制 + 1 漂移修复) - 21 第二波收口总表(定型语义汇总,第三波接入依据) - mkdocs build --strict 通过 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 KiB
19 · T2-09 契约冻结报告:pets 域 12 路径合入正典(v1.2.0)
- 日期:2026-09-08
- 工单:T2-09(M2 第二波,契约冻结)
- 仓库:patbond-doc,main 分支
- 角色:API 契约工程师
- 结论先行:
docs/api/openapi.yaml由 1.1.0(6 路径)升至 1.2.0(18 路径 / 24 操作 / 45 schema),pets 域 12 路径按 13/16/17/18 号定型表修正草案后合入;新增错误码 8 个(40300/40401/40402/40902/40903/40904/42201/42202,其中 40903/40904/42201/42202 为 M2 新引入,42200 不引入);校验通过(YAML 解析、$ref 全解析、mkdocs build --strict)。自本报告起 pets 域契约冻结。
1. 冻结端点总表(12 路径 / 18 操作)
| # | 端点 | 操作 | 权限档 | 成功 | 分页/排序 | 幂等 | 定型依据 |
|---|---|---|---|---|---|---|---|
| 1 | /api/v1/pets |
GET | 隐式(按 pet_owners 过滤) | 200 数组 | 不分页,created_at DESC | — | 13 §2.1 |
| 2 | /api/v1/pets |
POST | 任何登录用户 | 201 | — | 无键(唯一约束兜底) | 13 §2.1/§2.4 |
| 3 | /api/v1/pets/{petId} |
GET | READ | 200(含 myRole) | — | — | 13 §2.1 |
| 4 | /api/v1/pets/{petId} |
PATCH | MANAGE(仅 owner) | 200 | — | 乐观锁 version | 13 §2.1/§2.3 |
| 5 | /api/v1/breeds |
GET | 仅 Bearer(字典) | 200 数组 | 不分页,sort_order | — | 13 §2.1 |
| 6 | /api/v1/pets/{petId}/weights |
GET | READ | 200 分页信封 | cursor,measured_at DESC, id DESC | — | 16 §1 |
| 7 | /api/v1/pets/{petId}/weights |
POST | WRITE | 201 | — | 可选 Idempotency-Key | 16 §1/§3 |
| 8 | /api/v1/vaccine-catalog |
GET | 仅 Bearer(字典) | 200 数组 | 不分页,species, name | — | 16 §1 |
| 9 | /api/v1/pets/{petId}/vaccinations |
GET | READ | 200 数组 | 不分页,series_key, dose_no, created_at, id | — | 16 §1(偏差 #6) |
| 10 | /api/v1/pets/{petId}/vaccinations |
POST | WRITE | 201 | — | 可选 Idempotency-Key | 16 §1/§3 |
| 11 | /api/v1/vaccinations/{vaccinationId} |
PATCH | WRITE | 200 | 顶层短路径 | 乐观锁 version | 16 §1(14 号裁量 #1 照采) |
| 12 | /api/v1/pets/{petId}/health-events |
GET | READ | 200 分页信封 | cursor,occurred_at DESC, id DESC | — | 17 §1 |
| 13 | /api/v1/pets/{petId}/health-events |
POST | WRITE | 201 | — | 可选 Idempotency-Key | 17 §1 |
| 14 | /api/v1/health-events/{eventId} |
PATCH | WRITE | 200 | 顶层短路径 | 乐观锁 version | 17 §1 |
| 15 | /api/v1/pets/{petId}/care-reminders |
GET | READ | 200 数组 | 不分页,due_at ASC, id;?status= 过滤 |
— | 17 §1(偏差 #4) |
| 16 | /api/v1/pets/{petId}/care-reminders |
POST | WRITE | 201 | — | 可选 Idempotency-Key | 17 §1(偏差 #5,拍板 B) |
| 17 | /api/v1/care-reminders/{reminderId} |
PATCH | WRITE | 200 | 顶层短路径 | 状态守卫(无 version 列) | 17 §1/§2.2(偏差 #6) |
| 18 | /api/v1/pets/{petId}/summary |
GET | READ | 200 | ?tz= IANA,缺省 UTC |
— | 18 §1/§2/§3 |
软删除端点 DELETE /api/v1/pets/{petId} 不进 M2 契约(拍板 B;D2-7 首版仅归档,13 §1 明确不在单)。
汇总数
| 维度 | 1.1.0 | 1.2.0 |
|---|---|---|
| 路径 | 6 | 18(+12) |
| 操作 | 6 | 24(+18) |
| schema | 15 | 45(+30) |
| 错误码(业务码,不含 0) | 10 | 18(+8:40300/40401/40402/40902/40903/40904/42201/42202) |
| 复用组件 | — | 新增 responses 4(PetNotFound/RecordNotFound/PetWriteDenied/VersionConflict)、parameters 4(PetIdParam/PageLimitParam/PageCursorParam/IdempotencyKeyHeader) |
2. 草案 → 冻结的全部修正项对照(22 项)
草案 = openapi-pets-draft.yaml + 14 号起草报告;修正一律以实现定型表为准(实现定型表 > 草案)。
2.1 错误码(拍板 A1)
| # | 草案 | 冻结定案 | 依据 |
|---|---|---|---|
| 1 | 剂次重复用 40903 | 40904 VACCINATION_DOSE_EXISTS(40903 已被 T2-03 的 MICROCHIP_EXISTS 占用,按「不复用不改号」顺延) | 16 §4、偏差 #1 |
| 2 | 42200 BREED_CONSTRAINT_VIOLATION(品种互斥 422) | 不引入;品种双填/双空/物种错配/品种不存在或停用一律 400/40000 | 13 §2.4、16 偏差 #2 |
| 3 | 42201 疫苗状态机(草案提议) | 照采,语义定为疫苗专属 VACCINATION_RULE_VIOLATION | 16 §4 |
| 4 | 提醒 422 复用 42201 | 42202 REMINDER_RULE_VIOLATION(新增),跨资源不复用错误码 | 17 §4、偏差 #1 |
| 5 | 草案无 40903 芯片号语义 | 40903 MICROCHIP_EXISTS 新增(POST/PATCH pets 的 409 分支) | 13 §2.4 |
2.2 响应形态与命名(拍板 A2)
| # | 草案 | 冻结定案 | 依据 |
|---|---|---|---|
| 6 | 资源主键用类型化名(petId/weightId/vaccinationId/eventId/reminderId,14 号裁量 #7) | 裸 id,关联字段保留类型名(petId/vaccineId 等);路径参数名不变 |
16 偏差 #3、17 偏差 #2 |
| 7 | Vaccination 无疫苗名称 | 响应增加 vaccineName(同 breedDisplayName 先例) |
16 偏差 #4 |
| 8 | Pet 无 breedDisplayName;列表 Pet 不带 myRole(14 号裁量 #8) | 增加 breedDisplayName;myRole 进全部宠物响应(列表/详情/创建/更新统一 Pet schema,PetDetail 撤销) |
13 §2.2 |
| 9 | Pet 含 avatarAssetId(只读回显)、status 枚举含 deleted | avatarAssetId 移除(ADR-010 整体不出现);响应 status 枚举去 deleted(软删宠物一律 404/40401,永不返回) | 13 §2.2/§2.4 |
| 10 | 创建 201、分页信封 {items, nextCursor, hasMore}(草案形态) |
照采并升格为全 API 分页正典,写入 info 通用约定 | 拍板 A2、16 §1.1 |
2.3 分页与列表(拍板 A3)
| # | 草案 | 冻结定案 | 依据 |
|---|---|---|---|
| 11 | 疫苗列表分页待定(TODO-FREEZE #5) | 不分页,series_key, dose_no, created_at, id 排序定死;列表不过滤 status |
16 偏差 #6 |
| 12 | 提醒列表分页/待办过滤待定(TODO-FREEZE #8) | 不分页 + ?status= 白名单过滤,due_at ASC, id 排序 |
17 偏差 #4 |
| 13 | GET /pets 是否分页待定(TODO-FREEZE #2) | 不分页,created_at DESC | 13 §2.1 |
| 14 | 列表 GET 无 400 分支 | 补 400/40000(limit 越界、cursor 无效、status/species 非法参数) | 13 §2.4、16 §4、17 §4 |
2.4 PATCH 语义与请求体(拍板 A4/B)
| # | 草案 | 冻结定案 | 依据 |
|---|---|---|---|
| 15 | Update 请求字段标 nullable(暗示可清空) | 缺席=不变,不支持清空回 null,三个 Update schema 全部去 nullable;宠物品种对为唯一例外(整体替换) | 13 §2.3、16 偏差 #7、17 偏差 #3 |
| 16 | UpdatePetRequest 权限待定(TODO-FREEZE #4) | MANAGE 仅 owner;species 不可改;status=deleted 经 PATCH 一律 400/40000 | 13 §2.1/§2.3 |
| 17 | CreateVaccinationRequest.status 含 cancelled | 创建仅 scheduled/completed(创建即取消 400/40000) | 16 偏差 #5 |
| 18 | UpdateVaccinationRequest 可改 vaccineId/seriesKey/doseNo?(草案未禁) | 不可改(不在请求体),修正路径 cancel 后重建;completed/cancelled 均为终态 | 16 §1.2/§2 |
| 19 | care-reminders PATCH 无 409 | 补 409/40902(无 version 列,当前状态条件更新守卫落空) | 17 偏差 #6、§2.2 |
| 20 | 顶层短路径待拍板(TODO-FREEZE #11) | 照采;40402 定型为记录级防枚举(记录不存在与所属宠物不可见响应完全一致);40401 定型为宠物级防枚举(不存在/软删/无关系一致) | 拍板 A4、13 §2.4、16 §4 |
2.5 PetSummary 与其它(拍板 A5/B)
| # | 草案 | 冻结定案 | 依据 |
|---|---|---|---|
| 21 | PetSummary 全 schema 占位(TODO-FREEZE #9/#10) | 按 18 §2 全量替换:nextVaccination 用 dueOn+`source(planned |
nextDue) 替代单一 plannedOn,增加 vaccinationId/vaccineId/doseNo/doseLabel;monthlyExpense恒非 null、增 timezone 回显、month 定 ISO year-month;进度分母 = 已登记剂次;无记录 null 语义(progress null ≠ 0/0);四项聚合口径**逐字**进 schema 描述;新增tz` 查询参数(IANA,缺省 UTC,非法 40000) |
| 22 | 实现侧收紧补进契约描述 | 疫苗须存在/enabled/物种匹配(40000);doseNo ≤32767;health-event notes ≤2000;amountCents 拒绝小数(40000,不静默截断);title btrim 空白 40000;创建提醒不收 status;createdByUserId 取自 token 不收请求体;Idempotency-Key 语义细则(≤255、调用者×宠物×资源隔离、不比对请求体、无 TTL) | 16 §5 末段、17 §4/§6 末段 |
3. 定型表间矛盾核查
逐项交叉核对 13/16/17/18 号定型表:未发现互相矛盾处(40902 在提醒流转守卫上的复用为 17 号显式定型,非撞号;42201/42202 分立与「不复用不改号」原则自洽;防枚举语义 13→16→17 单点继承一致;18 号聚合口径与 16 号「聚合侧自行排除 cancelled」的移交一致)。
一处拍板措辞与定型表的出入(已按定型表执行,非仲裁):拍板 B 组表述为「Idempotency-Key 为可选头(weights/health-events/care-reminders 三个 POST)」,未列 vaccinations POST;而 16 号定型表明确 POST .../vaccinations 支持可选 Idempotency-Key 且有测试锁定(同键两次 201 同 id、落库 1 行),草案亦本已声明该头(14 号裁量 #6,三个 POST 含 vaccinations)。判断拍板枚举的是「本次需拍板的三处」(care-reminders 为 17 号新增偏差 #5,weights/health-events 为可选性确认),vaccinations 属草案既有、无争议项。冻结契约按实现收录四个 POST 的可选 Idempotency-Key。若此判断与拍板本意不符,请显著上报——收窄为三个属于从契约中移除已实现并已测试的行为,需两端同步。
4. 冻结纪律声明
自 v1.2.0 起,pets 域 12 路径与全部 schema/错误码冻结:
- 任何字段变更(增、删、改名、改类型、改必填性、改枚举、改口径)须显著上报,经评审后走契约变更流程,两端(后端 patbond-api、客户端 patbond-flutter)同步,禁止任一侧单方面偏离。
- 纯增量扩展(新增可选响应字段、新增端点、新增错误码)允许在次版本内追加,但同样先改契约再改实现(契约先行,docs/api/index.md 约定)。
- 错误码永不复用、永不改号、永不改义(40903=MICROCHIP_EXISTS、40904=VACCINATION_DOSE_EXISTS、42201=疫苗专属、42202=提醒专属,已在错误码表定死)。
- 已知的未来破坏性调整须走上报流程的存量项:① totalDoses 分母若引入「系列应打总针数」(18 §6);② 幂等键无 TTL 若改为 idempotency 表 + TTL(16 §7);③ ADR-010 裁剪字段(avatar/certificate/provider/booking)M5 按纯增量补入(非破坏性,但须契约先行)。
- 提醒的 title/dueAt 编辑与删除端点、宠物软删除端点均不在 M2 契约;M2 内改期路径为 dismiss 后重建(17 §7),归档经
PATCH status=archived。
5. 校验与提交
python3 yaml.safe_load解析通过;158 个$ref全部可解析;18 路径 / 24 操作 / 45 schema / 错误码表 19 行计数核对一致。mkdocs build --strict通过。- 提交:
docs/api/openapi.yaml+docs/api/index.md独立提交并推送 main(提交511617b);本报告与草案文件(14 号、openapi-pets-draft.yaml)按波末统一入档,暂不提交;mkdocs.yml 未动。