222990e587
CI / docs-build (push) Successful in 1m19s
- 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>
10 KiB
10 KiB
T2-09 起草报告:pets 域 OpenAPI 契约草案
作者:API 契约工程师 日期:2026-09-07 状态:起草态(DRAFT)——未冻结、未并入 docs/api/openapi.yaml 草案文件:
docs/development/iterations/iteration-2/openapi-pets-draft.yaml(可独立 YAML 解析:12 路径 / 18 操作 / 33 schema,$ref 全部可解析) 冻结条件:T2-03 权限/错误语义定型 + T2-08 聚合字段定型,由主会话协调执行冻结合并。
1. 范围与依据
| 依据 | 用途 |
|---|---|
| iteration-2/01 §1.1 端点表 + T2-03~T2-08 工单描述 | 端点清单、cursor 分页、乐观锁、Idempotency-Key 要求 |
| iteration-2/02 §4 资源设计草案 + 错误码扩展段 | 40300/40401/40402/40902 语义、P7 防枚举裁决 |
patbond-api V3 迁移(V3__pet_health_baseline.sql) |
字段名、长度、枚举值、CHECK 约束、状态机(唯一正典) |
既有契约 docs/api/openapi.yaml 1.1.0 |
信封、错误组件、camelCase、ISO 8601、securityScheme 风格 |
| ADR-010 | certificate/provider/booking/avatar 不开放写入 |
| ADR-015 | owner/caregiver/viewer 三角色权限模型,邀请流程后置 |
2. 起草的端点(12 路径 / 18 操作)
| # | 端点 | 操作 | 对应工单 |
|---|---|---|---|
| 1 | /api/v1/pets |
GET / POST | T2-03 |
| 2 | /api/v1/pets/{petId} |
GET / PATCH | T2-03 |
| 3 | /api/v1/breeds |
GET | T2-03 |
| 4 | /api/v1/pets/{petId}/weights |
GET / POST | T2-04 |
| 5 | /api/v1/vaccine-catalog |
GET | T2-05 |
| 6 | /api/v1/pets/{petId}/vaccinations |
GET / POST | T2-05 |
| 7 | /api/v1/vaccinations/{vaccinationId} |
PATCH | T2-05 |
| 8 | /api/v1/pets/{petId}/health-events |
GET / POST | T2-06 |
| 9 | /api/v1/health-events/{eventId} |
PATCH | T2-06 |
| 10 | /api/v1/pets/{petId}/care-reminders |
GET / POST | T2-07 |
| 11 | /api/v1/care-reminders/{reminderId} |
PATCH | T2-07 |
| 12 | /api/v1/pets/{petId}/summary |
GET | T2-08 |
DELETE /api/v1/pets/{petId}(软删)未起草:02 号评估标注"是否进 M2 契约冻结时定",且 PM 决策 D2-7 建议首版仅归档。归档经 PATCH status=archived 已覆盖,软删端点留待冻结时裁决(记入 TODO-FREEZE 清单第 11 项)。
3. 设计决策(起草者裁量,冻结评审时可推翻)
- 子资源 PATCH 走顶层短路径(
/api/v1/vaccinations/{id}而非/api/v1/pets/{petId}/vaccinations/{id}):记录 ID 全局唯一(UUID),短路径避免冗余 petId 校验歧义(path petId 与记录归属不一致时如何报错)。与 02 号评估的PATCH .../vaccinations/{id}写法在语义上一致,仅路径层级不同——冻结评审时需拍板(列入 TODO-FREEZE)。 - 提醒资源名用
care-reminders(与表名 care_reminders 对齐),02 号评估用的是reminders——冻结时统一。 - 疫苗目录路径用
/api/v1/vaccine-catalog,02 号评估用/api/v1/vaccines——冻结时统一。 - 新增错误码 40903(疫苗剂次重复)、42200(品种互斥)、42201(状态机违反):延续既有编号段追加,不与 40900/40901/40902 冲突。T2-05 验收标准要求"同系列同剂次重复登记返回冲突"与"状态机非法迁移被拒绝并返回稳定错误码",用 40902 一码多义会让客户端无法区分"重试可解"(版本冲突→刷新重提)与"业务性冲突"(剂次已存在→改剂次)。42200/42201 用 422 区分"参数格式合法但业务规则违反"与 40000 的"参数格式错误"。此三码为草案新提,需后端确认后进 ErrorCode 枚举。
- cursor 分页信封形态:
data: { items, nextCursor, hasMore }。既有契约无分页先例,此形态为 pets 域首次定义,将成为全 API 的分页正典——按"一次定死、处处一致"原则,weights 与 health-events 完全一致。 - Idempotency-Key 定为可选头:01 号拆解 T2-04 要求"写接口支持 Idempotency-Key",但 02 号评估指出开发计划 6.1 的强制名单不含 pets。草案折中:weights/vaccinations/health-events 三个 POST 声明可选头,语义为"带则幂等去重";care-reminders 与 pets 创建不声明(低重复风险,乐观锁与唯一约束兜底)。两份输入存在张力,冻结时需拍板。
- 响应字段 ID 命名:资源自身 ID 用类型化名(petId/weightId/vaccinationId/eventId/reminderId),与既有契约 Me.userId 的先例一致,避免裸
id在嵌套结构中歧义。 - PetDetail.myRole:详情返回调用者角色(02 号评估"详情含调用者自己的 role"),供前端决定编辑入口显隐。列表 Pet 不带 role(避免 N 次 join 语义进列表,前端列表页不需要)。
- 金额一律
amountCents整数分(int64、非负),日期区分date(birthDate/plannedOn 等,数据库 date 列)与date-time(timestamptz 列),与 V3 列类型一一对应。 - UpdateCareReminderRequest 无 version:care_reminders 表没有 version 列(V3 确认),状态流转 pending→completed/dismissed 天然幂等,不做乐观锁。其余三个 PATCH(pets/vaccinations/health-events)均强制 version。
4. 与 V3 约束的对照表
| V3 约束 | 契约体现 |
|---|---|
ck_pets_species (dog/cat/other) |
species enum,三处字典/宠物一致 |
ck_pets_sex (male/female/unknown) |
sex enum |
ck_pets_status 5 值 |
Pet.status enum 全 5 值;UpdatePetRequest 只开放 4 值(deleted 不开放写) |
ck_pets_breed breed/custom 互斥 |
请求描述 + 422/42200 错误分支 |
ck_pets_name 1–64 |
name minLength/maxLength |
ck_pet_weight >0 且 ≤500 |
weightKg minimum 0.01 / maximum 500(numeric(6,2),最小正两位小数值) |
ck_pet_weight_source 3 值 |
source enum (manual/clinic/device) |
ck_vaccination_status 3 值 |
status enum (scheduled/completed/cancelled) |
ck_vaccination_dates 状态-日期联动 |
createVaccination/updateVaccination 描述 + 422/42201 |
uq_pet_vaccination_dose(非 cancelled 唯一) |
409/40903 错误分支 |
ck_vaccination_dose >0 |
doseNo minimum 1 |
ck_health_event_type 6 值 |
eventType enum 与 V3 逐字一致 |
ck_health_event_title 1–160 |
title 长度约束 |
ck_health_event_amount ≥0 或 null |
amountCents minimum 0, nullable |
ck_care_reminder_type 4 值 |
reminderType enum |
ck_care_reminder_status 3 值 |
status enum (pending/completed/dismissed) |
ck_care_reminder_completed 联动 |
UpdateCareReminderRequest 描述 + 422 分支 |
| version 列(pets/vaccinations/health_events) | 三资源响应必含 version,PATCH 请求必填 version |
| care_reminders 无 version 列 | CareReminder 响应无 version,PATCH 无乐观锁 |
ix_pet_weight_pet_measured (measured_at DESC, id DESC) |
weights 分页排序描述与索引对齐 |
ix_health_events_pet_time (occurred_at DESC, id DESC) |
health-events 分页排序与索引对齐 |
| ADR-010 剪出列(avatar/certificate/provider/booking) | 全部请求体不含;Pet.avatarAssetId 只读回显、疫苗/事件的 provider/booking/certificate 字段响应中不出现(见 §5 注) |
注:certificate_asset_id、provider_id、provider_name_snapshot、booking_id 在草案的响应 schema 中整体未列出(而非标 readOnly)——M2 无任何写入路径,值恒为 null,列出只会诱导客户端建模死字段;M5/媒体迭代时按"新增可选响应字段"作纯增量扩展,无破坏性。pets.deleted_at 同理不出现(软删语义未开放)。
5. 与既有契约(1.1.0)风格一致性自查
| 检查项 | 结论 |
|---|---|
统一信封 {code, message, data},成功 code 恒 0(enum [0]) |
一致,每资源独立 XxxEnvelope,与 MeEnvelope 等先例同构 |
| ErrorEnvelope 结构(code integer / message / data nullable) | 逐字段一致 |
| ValidationError / AccessTokenInvalid 复用组件 | 与既有 components/responses 同名同构,合并时直接去重 |
| camelCase、UUID 字符串(format: uuid)、ISO 8601 date-time | 一致 |
| securityScheme bearerAuth(http/bearer/JWT) | 逐字一致 |
| 错误码不复用不改号,追加式扩展 | 40300/40401/40402/40902 取自 02 号评估;40903/42200/42201 为新提追加 |
| 中文 summary/description、错误响应带 code 注释 | 一致 |
| openapi 3.0.3、tags 分组 | 一致 |
| 与既有契约的偏差 | 仅两处有意偏差:创建返回 201(既有 auth 全 200,但 02 号评估明确"返回 201",且 pets 域为资源创建语义,属域内新约定不破坏旧端点);分页信封为新增形态(既有无先例) |
6. TODO-FREEZE 清单(11 项)
草案 YAML 内以 # TODO-FREEZE: 注释标注 10 处,加上本报告第 11 项:
| # | 位置 | 等待 | 内容 |
|---|---|---|---|
| 1 | info.description 权限模型段 | T2-03 | 每端点权限规则逐条定死(owner/caregiver/viewer 读写矩阵)与错误示例 |
| 2 | GET /pets | T2-03 | 列表是否分页(建议不分页) |
| 3 | GET /pets/{petId} | T2-03 | 不可见宠物 404/40401 vs 越权 403/40300 的最终边界(P7 建议已按防枚举写入,待实现确认) |
| 4 | PATCH /pets/{petId} | T2-03 | caregiver 是否可改档案(建议仅 owner) |
| 5 | GET .../vaccinations | T2-05 | 疫苗列表分页策略(量小或可不分页) |
| 6 | POST .../vaccinations | ADR-010 后续 | provider/booking/certificate 字段的未来开放方式(纯增量) |
| 7 | POST .../health-events | ADR-010 后续 | 同上(provider/booking) |
| 8 | GET .../care-reminders | T2-07 | 分页与 status=pending 过滤参数形态 |
| 9 | GET .../summary 端点描述 | T2-08 | 聚合字段命名、月度边界时区口径、进度分母口径、下次接种取值优先级 |
| 10 | PetSummary schema | T2-08 | 全 schema 为占位,逐字段待定 |
| 11 | 本报告 §2/§3 | 冻结评审 | DELETE 软删端点是否入 M2;子资源 PATCH 路径层级;care-reminders/vaccine-catalog 资源命名与 02 号评估用词统一;Idempotency-Key 可选 vs 强制;40903/42200/42201 三个新错误码后端确认 |
7. 冻结前禁止事项(自我约束声明)
- 本草案未合入
docs/api/openapi.yaml(仍为 1.1.0 / 6 端点,未做任何修改)。 - 未修改 mkdocs.yml、未 commit/push、未改动任何代码仓。
- 冻结时的合并动作:去重 components(ErrorEnvelope/两个 responses/securityScheme)、版本号升 1.2.0、错误码表并入 info.description、消除全部 TODO-FREEZE——由主会话在 T2-03/T2-08 定型后协调执行。