新增:pets feature 数据层(T2-11,契约 v1.2.0 全 18 操作)
CI / flutter-gates (push) Successful in 1m12s

- pet_models.dart:pets 域 DTO 逐字段对齐冻结契约(宠物/品种/体重/
  疫苗目录/疫苗/健康事件/照护提醒/聚合摘要 + cursor 分页信封),
  枚举严格解析,请求体部分更新语义(缺席字段不出现)
- pets_repository.dart:PetsRepository 抽象 + ApiPetsRepository,
  12 路径 18 操作全覆盖;四个 POST 携带 Idempotency-Key;
  复用既有 ApiClient token 拦截与单飞刷新重放
- pet_exceptions.dart:新 8 错误码映射为类型化异常
  (40300/40401/40402/40902/40903/40904/42201/42202),
  仍可按 ApiBusinessException 基类捕获
- pets_controller.dart:宠物档案状态自 AppState 拆出
  (Controller → Repository → API Client 分层,四态就绪供 T2-12)
- money.dart:元/分换算工具(DTO 层保持整数分,UI 层 T2-14 使用)
- api_client.dart:新增 patbondPetApiBaseUrl(:8083,--dart-define
  可覆盖)与 query 参数支持;api_exception.dart 补 pets 域错误码
- 测试 64 → 126:DTO 映射、错误类型化映射、请求线路
  (路径/方法/鉴权/分页/幂等键/tz)、控制器状态机、金额换算

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-08 11:22:24 +08:00
parent 33b993ca0c
commit 7fb9031f8d
12 changed files with 2665 additions and 1 deletions
+107
View File
@@ -0,0 +1,107 @@
/// pets 域测试样本(契约 openapi.yaml v1.2.0 各 schema 全字段 JSON)。
library;
Map<String, Object?> okListEnvelope(List<Object?> data) => {
'code': 0,
'message': 'ok',
'data': data,
};
Map<String, dynamic> samplePetJson({
Map<String, Object?> overrides = const {},
}) => {
'id': 'p-1',
'name': '豆豆',
'species': 'dog',
'breedId': 'b-1',
'breedDisplayName': '柴犬',
'customBreedName': null,
'sex': 'male',
'birthDate': '2024-03-15',
'birthDateEstimated': false,
'personality': null,
'microchipNo': null,
'sterilizedOn': null,
'status': 'active',
'myRole': 'owner',
'createdAt': '2026-09-01T10:00:00+08:00',
'updatedAt': '2026-09-02T10:00:00+08:00',
'version': 3,
...overrides,
};
Map<String, dynamic> sampleWeightJson() => {
'id': 'w-1',
'petId': 'p-1',
'weightKg': 4.35,
'measuredAt': '2026-09-07T09:00:00+08:00',
'source': 'manual',
'note': null,
'createdAt': '2026-09-07T09:01:00+08:00',
};
Map<String, dynamic> sampleVaccinationJson() => {
'id': 'vx-1',
'petId': 'p-1',
'vaccineId': 'v-1',
'vaccineName': '狂犬疫苗',
'seriesKey': 'rabies-initial',
'doseNo': 1,
'doseLabel': null,
'status': 'scheduled',
'plannedOn': '2026-10-01',
'administeredOn': null,
'nextDueOn': null,
'manufacturer': null,
'batchNo': null,
'notes': null,
'createdAt': '2026-09-01T10:00:00+08:00',
'updatedAt': '2026-09-01T10:00:00+08:00',
'version': 1,
};
Map<String, dynamic> sampleHealthEventJson() => {
'id': 'e-1',
'petId': 'p-1',
'eventType': 'medical',
'occurredAt': '2026-09-05T14:00:00+08:00',
'title': '皮肤检查',
'notes': null,
'amountCents': 12850,
'createdByUserId': 'u-1',
'createdAt': '2026-09-05T14:05:00+08:00',
'updatedAt': '2026-09-05T14:05:00+08:00',
'version': 1,
};
Map<String, dynamic> sampleCareReminderJson() => {
'id': 'r-1',
'petId': 'p-1',
'reminderType': 'checkup',
'title': '年度体检',
'dueAt': '2026-10-01T00:00:00+08:00',
'status': 'pending',
'completedAt': null,
'createdAt': '2026-09-01T10:00:00+08:00',
'updatedAt': '2026-09-01T10:00:00+08:00',
};
Map<String, dynamic> samplePetSummaryJson() => {
'petId': 'p-1',
'latestWeight': {'weightKg': 4.35, 'measuredAt': '2026-09-07T09:00:00+08:00'},
'vaccinationProgress': {'completedDoses': 2, 'totalDoses': 3},
'nextVaccination': {
'vaccinationId': 'vx-3',
'vaccineId': 'v-1',
'vaccineName': '狂犬疫苗',
'doseNo': 3,
'doseLabel': null,
'dueOn': '2026-08-01',
'source': 'planned',
},
'monthlyExpense': {
'month': '2026-09',
'timezone': 'Asia/Shanghai',
'amountCents': 12850,
},
};