Files
lixi 7fb9031f8d
CI / flutter-gates (push) Successful in 1m12s
新增:pets feature 数据层(T2-11,契约 v1.2.0 全 18 操作)
- 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>
2026-09-08 11:22:24 +08:00

84 lines
3.5 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:patbond_flutter/core/network/api_exception.dart';
/// pets 域类型化业务异常(契约 v1.2.0 定型的 8 个新错误码,
/// 21 号收口报告 §1)。全部继承 [ApiBusinessException]
/// 既有按基类捕获的通用错误处理不受影响。
/// 40300:对可见宠物无相应操作权限(viewer 写记录、caregiver/viewer 改档案)。
final class PetAccessDeniedException extends ApiBusinessException {
const PetAccessDeniedException({required super.message})
: super(code: ApiCodes.petAccessDenied);
}
/// 40401:宠物不存在 / 已软删 / 与调用者无关系(防枚举,三种情况响应一致)。
final class PetNotFoundException extends ApiBusinessException {
const PetNotFoundException({required super.message})
: super(code: ApiCodes.petNotFound);
}
/// 40402:记录不存在或记录所属宠物对调用者不可见(记录级防枚举)。
final class PetRecordNotFoundException extends ApiBusinessException {
const PetRecordNotFoundException({required super.message})
: super(code: ApiCodes.recordNotFound);
}
/// 40902:乐观锁版本冲突(version 过期;提醒的条件更新守卫落空同码)。
/// 客户端处理:刷新取新数据后重提。
final class PetVersionConflictException extends ApiBusinessException {
const PetVersionConflictException({required super.message})
: super(code: ApiCodes.versionConflict);
}
/// 40903:芯片号已被登记(跨用户唯一)。
final class MicrochipTakenException extends ApiBusinessException {
const MicrochipTakenException({required super.message})
: super(code: ApiCodes.microchipTaken);
}
/// 40904:同宠物同疫苗同系列同剂次的非 cancelled 疫苗记录已存在
/// (cancel 后同剂次可重新登记)。
final class VaccinationDoseExistsException extends ApiBusinessException {
const VaccinationDoseExistsException({required super.message})
: super(code: ApiCodes.vaccinationDoseExists);
}
/// 42201:疫苗状态机非法迁移或状态-日期规则违反(HTTP 422)。
final class VaccinationRuleException extends ApiBusinessException {
const VaccinationRuleException({required super.message})
: super(code: ApiCodes.vaccinationRuleViolation);
}
/// 42202:提醒状态机非法迁移或 completed-completedAt 一致性违反(HTTP 422)。
final class CareReminderRuleException extends ApiBusinessException {
const CareReminderRuleException({required super.message})
: super(code: ApiCodes.careReminderRuleViolation);
}
/// 把通用业务异常按 pets 域错误码升格为类型化异常;
/// 未覆盖的码(40000 参数错误等)原样返回,沿用通用处理。
ApiBusinessException mapPetBusinessException(ApiBusinessException error) {
return switch (error.code) {
ApiCodes.petAccessDenied => PetAccessDeniedException(
message: error.message,
),
ApiCodes.petNotFound => PetNotFoundException(message: error.message),
ApiCodes.recordNotFound => PetRecordNotFoundException(
message: error.message,
),
ApiCodes.versionConflict => PetVersionConflictException(
message: error.message,
),
ApiCodes.microchipTaken => MicrochipTakenException(message: error.message),
ApiCodes.vaccinationDoseExists => VaccinationDoseExistsException(
message: error.message,
),
ApiCodes.vaccinationRuleViolation => VaccinationRuleException(
message: error.message,
),
ApiCodes.careReminderRuleViolation => CareReminderRuleException(
message: error.message,
),
_ => error,
};
}