新增:体重录入与历史列表接入真实数据(T2-13 体重半边)
- WeightRecordsPage:cursor 分页(加载更多/末页收起/翻页失败保留重试)、 loading/empty/error/retry 四态、下拉刷新;viewer 隐藏录入入口 - WeightFormPage:weightKg 契约区间 (0,500] 与两位小数前端校验 + 40000 兜底;称重时间转 UTC 上送;错误三层分层沿用登录纵切 - health_record 域埋点强类型封装(06 §1.4:create_started/succeeded/ failed + viewed,httpStatus 由业务码推导;viewed 按工单口径取列表曝光) - health_record_display 纯函数(体重解析/展示、疫苗状态映射、42201 规则前端拦截函数);PetsController 暴露 repository(22 号 §7 交接) - 测试 177 → 205(+28)全绿 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -129,6 +129,44 @@ Pet buildPet(
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> 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<String, Object?> overrides = const {},
|
||||
}) => WeightRecord.fromJson({
|
||||
...sampleWeightJson(),
|
||||
'id': id,
|
||||
'weightKg': weightKg,
|
||||
'measuredAt': measuredAt,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
/// 快速构造疫苗记录。
|
||||
Vaccination buildVaccination(
|
||||
String id, {
|
||||
Map<String, Object?> overrides = const {},
|
||||
}) =>
|
||||
Vaccination.fromJson({...sampleVaccinationJson(), 'id': id, ...overrides});
|
||||
|
||||
/// 快速构造摘要(缺省为「三聚合齐备」样本;overrides 可置 null 验证空态)。
|
||||
PetSummary buildSummary({Map<String, Object?> overrides = const {}}) =>
|
||||
PetSummary.fromJson({...samplePetSummaryJson(), ...overrides});
|
||||
|
||||
/// 假仓库:各方法可注入行为;listPets 默认空列表,其余未注入的方法抛
|
||||
/// UnimplementedError(22 号报告 §7:widget 测试注入假仓库的共享实现)。
|
||||
class FakePetsRepository implements PetsRepository {
|
||||
@@ -137,6 +175,16 @@ class FakePetsRepository implements PetsRepository {
|
||||
Future<Pet> Function(String)? getPetHandler;
|
||||
Future<Pet> Function(String, UpdatePetRequest)? updatePetHandler;
|
||||
Future<List<Breed>> Function(PetSpecies?)? listBreedsHandler;
|
||||
Future<CursorPage<WeightRecord>> Function(String, int?, String?)?
|
||||
listWeightsHandler;
|
||||
Future<WeightRecord> Function(String, CreateWeightRequest)?
|
||||
createWeightHandler;
|
||||
Future<List<VaccineCatalogItem>> Function(PetSpecies?)?
|
||||
listVaccineCatalogHandler;
|
||||
Future<List<Vaccination>> Function(String)? listVaccinationsHandler;
|
||||
Future<Vaccination> Function(String, CreateVaccinationRequest)?
|
||||
createVaccinationHandler;
|
||||
Future<PetSummary> Function(String, String?)? getPetSummaryHandler;
|
||||
|
||||
@override
|
||||
Future<List<Pet>> listPets() =>
|
||||
@@ -157,6 +205,53 @@ class FakePetsRepository implements PetsRepository {
|
||||
listBreedsHandler?.call(species) ??
|
||||
Future.value([Breed.fromJson(sampleBreedJson())]);
|
||||
|
||||
@override
|
||||
Future<CursorPage<WeightRecord>> listWeights(
|
||||
String petId, {
|
||||
int? limit,
|
||||
String? cursor,
|
||||
}) => listWeightsHandler!(petId, limit, cursor);
|
||||
|
||||
@override
|
||||
Future<WeightRecord> createWeight(
|
||||
String petId,
|
||||
CreateWeightRequest request,
|
||||
) => createWeightHandler!(petId, request);
|
||||
|
||||
@override
|
||||
Future<List<VaccineCatalogItem>> listVaccineCatalog({PetSpecies? species}) =>
|
||||
listVaccineCatalogHandler?.call(species) ??
|
||||
Future.value([VaccineCatalogItem.fromJson(sampleVaccineCatalogJson())]);
|
||||
|
||||
@override
|
||||
Future<List<Vaccination>> listVaccinations(String petId) =>
|
||||
listVaccinationsHandler!(petId);
|
||||
|
||||
@override
|
||||
Future<Vaccination> createVaccination(
|
||||
String petId,
|
||||
CreateVaccinationRequest request,
|
||||
) => createVaccinationHandler!(petId, request);
|
||||
|
||||
@override
|
||||
Future<PetSummary> 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}');
|
||||
|
||||
Reference in New Issue
Block a user