/// pets 域测试样本(契约 openapi.yaml v1.2.0 各 schema 全字段 JSON) /// 与共享假仓库。 library; import 'package:patbond_flutter/features/pets/pet_models.dart'; import 'package:patbond_flutter/features/pets/pets_repository.dart'; Map okListEnvelope(List data) => { 'code': 0, 'message': 'ok', 'data': data, }; Map samplePetJson({ Map 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 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 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 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 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 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, }, }; Map sampleBreedJson({ String id = 'b-1', String species = 'dog', String code = 'shiba', String displayName = '柴犬', }) => {'id': id, 'species': species, 'code': code, 'displayName': displayName}; /// 快速构造 Pet(widget/controller 测试共用)。 Pet buildPet( String id, { String name = '豆豆', int version = 1, Map overrides = const {}, }) => Pet.fromJson( samplePetJson( overrides: {'id': id, 'name': name, 'version': version, ...overrides}, ), ); Map sampleVaccineCatalogJson({ String id = 'v-1', String code = 'rabies', String name = '狂犬疫苗', String species = 'dog', }) => { 'id': id, 'code': code, 'name': name, 'species': species, 'description': null, }; /// 快速构造体重记录(widget 测试共用)。 WeightRecord buildWeight( String id, { double weightKg = 4.35, String measuredAt = '2026-09-07T09:00:00+08:00', Map overrides = const {}, }) => WeightRecord.fromJson({ ...sampleWeightJson(), 'id': id, 'weightKg': weightKg, 'measuredAt': measuredAt, ...overrides, }); /// 快速构造疫苗记录。 Vaccination buildVaccination( String id, { Map overrides = const {}, }) => Vaccination.fromJson({...sampleVaccinationJson(), 'id': id, ...overrides}); /// 快速构造摘要(缺省为「三聚合齐备」样本;overrides 可置 null 验证空态)。 PetSummary buildSummary({Map overrides = const {}}) => PetSummary.fromJson({...samplePetSummaryJson(), ...overrides}); /// 假仓库:各方法可注入行为;listPets 默认空列表,其余未注入的方法抛 /// UnimplementedError(22 号报告 §7:widget 测试注入假仓库的共享实现)。 class FakePetsRepository implements PetsRepository { Future> Function()? listPetsHandler; Future Function(CreatePetRequest)? createPetHandler; Future Function(String)? getPetHandler; Future Function(String, UpdatePetRequest)? updatePetHandler; Future> Function(PetSpecies?)? listBreedsHandler; Future> Function(String, int?, String?)? listWeightsHandler; Future Function(String, CreateWeightRequest)? createWeightHandler; Future> Function(PetSpecies?)? listVaccineCatalogHandler; Future> Function(String)? listVaccinationsHandler; Future Function(String, CreateVaccinationRequest)? createVaccinationHandler; Future Function(String, String?)? getPetSummaryHandler; @override Future> listPets() => listPetsHandler?.call() ?? Future.value(const []); @override Future createPet(CreatePetRequest request) => createPetHandler!(request); @override Future getPet(String petId) => getPetHandler!(petId); @override Future updatePet(String petId, UpdatePetRequest request) => updatePetHandler!(petId, request); @override Future> listBreeds({PetSpecies? species}) => listBreedsHandler?.call(species) ?? Future.value([Breed.fromJson(sampleBreedJson())]); @override Future> listWeights( String petId, { int? limit, String? cursor, }) => listWeightsHandler!(petId, limit, cursor); @override Future createWeight( String petId, CreateWeightRequest request, ) => createWeightHandler!(petId, request); @override Future> listVaccineCatalog({PetSpecies? species}) => listVaccineCatalogHandler?.call(species) ?? Future.value([VaccineCatalogItem.fromJson(sampleVaccineCatalogJson())]); @override Future> listVaccinations(String petId) => listVaccinationsHandler!(petId); @override Future createVaccination( String petId, CreateVaccinationRequest request, ) => createVaccinationHandler!(petId, request); @override Future getPetSummary(String petId, {String? tz}) => getPetSummaryHandler?.call(petId, tz) ?? // 缺省给「无任何记录」空摘要(契约 null 语义),既有详情页测试 // 不必逐个注入。 Future.value( PetSummary.fromJson({ 'petId': petId, 'latestWeight': null, 'vaccinationProgress': null, 'nextVaccination': null, 'monthlyExpense': { 'month': '2026-09', 'timezone': 'UTC', 'amountCents': 0, }, }), ); @override dynamic noSuchMethod(Invocation invocation) => throw UnimplementedError('${invocation.memberName}'); }