Files
patbond-doc/docs/development/iterations/iteration-2/19-contract-freeze-report.md
T
lixi 222990e587
CI / docs-build (push) Successful in 1m19s
docs: M2 第二波收口——报告 13~21 与契约草案入档挂导航
- 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>
2026-09-08 10:40:27 +08:00

12 KiB
Raw Blame History

19 · T2-09 契约冻结报告:pets 域 12 路径合入正典(v1.2.0)

  • 日期2026-09-08
  • 工单T2-09M2 第二波,契约冻结)
  • 仓库patbond-docmain 分支
  • 角色API 契约工程师
  • 结论先行docs/api/openapi.yaml 由 1.1.06 路径)升至 1.2.018 路径 / 24 操作 / 45 schemapets 域 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 分页信封 cursormeasured_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 §114 号裁量 #1 照采)
12 /api/v1/pets/{petId}/health-events GET READ 200 分页信封 cursoroccurred_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+840300/40401/40402/40902/40903/40904/42201/42202
复用组件 新增 responses 4PetNotFound/RecordNotFound/PetWriteDenied/VersionConflict)、parameters 4PetIdParam/PageLimitParam/PageCursorParam/IdempotencyKeyHeader

2. 草案 → 冻结的全部修正项对照(22 项)

草案 = openapi-pets-draft.yaml + 14 号起草报告;修正一律以实现定型表为准(实现定型表 > 草案)。

2.1 错误码(拍板 A1

# 草案 冻结定案 依据
1 剂次重复用 40903 40904 VACCINATION_DOSE_EXISTS40903 已被 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/reminderId14 号裁量 #7 id,关联字段保留类型名(petId/vaccineId 等);路径参数名不变 16 偏差 #3、17 偏差 #2
7 Vaccination 无疫苗名称 响应增加 vaccineName(同 breedDisplayName 先例) 16 偏差 #4
8 Pet 无 breedDisplayName;列表 Pet 不带 myRole14 号裁量 #8 增加 breedDisplayNamemyRole 进全部宠物响应(列表/详情/创建/更新统一 Pet schemaPetDetail 撤销) 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/40000limit 越界、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 仅 ownerspecies 不可改;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 全量替换:nextVaccinationdueOn+`source(planned nextDue) 替代单一 plannedOn,增加 vaccinationId/vaccineId/doseNo/doseLabelmonthlyExpense恒非 null、增 timezone 回显、month 定 ISO year-month;进度分母 = 已登记剂次;无记录 null 语义(progress null ≠ 0/0);四项聚合口径**逐字**进 schema 描述;新增tz` 查询参数(IANA,缺省 UTC,非法 40000
22 实现侧收紧补进契约描述 疫苗须存在/enabled/物种匹配(40000);doseNo ≤32767health-event notes ≤2000amountCents 拒绝小数(40000,不静默截断);title btrim 空白 40000;创建提醒不收 statuscreatedByUserId 取自 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 号新增偏差 #5weights/health-events 为可选性确认),vaccinations 属草案既有、无争议项。冻结契约按实现收录四个 POST 的可选 Idempotency-Key。若此判断与拍板本意不符,请显著上报——收窄为三个属于从契约中移除已实现并已测试的行为,需两端同步。

4. 冻结纪律声明

自 v1.2.0 起,pets 域 12 路径与全部 schema/错误码冻结

  1. 任何字段变更(增、删、改名、改类型、改必填性、改枚举、改口径)须显著上报,经评审后走契约变更流程,两端(后端 patbond-api、客户端 patbond-flutter)同步,禁止任一侧单方面偏离。
  2. 纯增量扩展(新增可选响应字段、新增端点、新增错误码)允许在次版本内追加,但同样先改契约再改实现(契约先行,docs/api/index.md 约定)。
  3. 错误码永不复用、永不改号、永不改义(40903=MICROCHIP_EXISTS、40904=VACCINATION_DOSE_EXISTS、42201=疫苗专属、42202=提醒专属,已在错误码表定死)。
  4. 已知的未来破坏性调整须走上报流程的存量项:① totalDoses 分母若引入「系列应打总针数」(18 §6);② 幂等键无 TTL 若改为 idempotency 表 + TTL16 §7);③ ADR-010 裁剪字段(avatar/certificate/provider/bookingM5 按纯增量补入(非破坏性,但须契约先行)。
  5. 提醒的 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 未动。