Files
patbond-flutter/lib/core/network/api_exception.dart
T
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

90 lines
3.3 KiB
Dart
Raw 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.
/// 服务端错误信封 `{code, message, data}` 的业务码常量(接口契约冻结稿)。
abstract final class ApiCodes {
static const ok = 0;
/// 参数错误。
static const paramError = 40000;
/// 用户名或密码错误。
static const badCredentials = 40100;
/// access token 无效或过期。
static const accessTokenInvalid = 40101;
/// refresh token 已失效。
static const refreshTokenInvalid = 40102;
/// 用户名重复。
static const usernameTaken = 40900;
/// 手机号重复。
static const phoneTaken = 40901;
/// 登录失败次数过多,账号临时锁定(HTTP 423,见 openapi.yaml)。
static const loginLocked = 42300;
// ------ pets 域(契约 v1.2.0 冻结,M2 第二波定型 8 个)------
/// 对可见宠物无相应操作权限(viewer 写记录、caregiver/viewer 改档案)。
static const petAccessDenied = 40300;
/// 宠物不存在 / 已软删 / 与调用者无关系(防枚举,三种情况响应一致)。
static const petNotFound = 40401;
/// 记录不存在或记录所属宠物对调用者不可见(记录级防枚举)。
static const recordNotFound = 40402;
/// 乐观锁版本冲突(提交的 version 已过期;提醒的条件更新守卫落空同码)。
static const versionConflict = 40902;
/// 芯片号已被登记(跨用户唯一)。
static const microchipTaken = 40903;
/// 同宠物同疫苗同系列同剂次的非 cancelled 疫苗记录已存在。
static const vaccinationDoseExists = 40904;
/// 疫苗状态机 / 状态-日期规则违反(HTTP 422)。
static const vaccinationRuleViolation = 42201;
/// 提醒状态机 / completed-completedAt 一致性违反(HTTP 422)。
static const careReminderRuleViolation = 42202;
}
/// API 调用的类型化异常。页面按类型映射为三层错误呈现
/// (字段级 / 表单横幅 / SnackBar,见 12 号组装稿 §4),
/// 服务端原始 message 一律不直接透出给用户。
sealed class ApiException implements Exception {
const ApiException(this.message);
/// 服务端返回的原始 message(仅用于日志排查,不上屏)。
final String message;
@override
String toString() => '$runtimeType: $message';
}
/// 系统错误:超时、断网、5xx、响应无法解析。归瞬态层(SnackBar + 重试)。
final class ApiNetworkException extends ApiException {
const ApiNetworkException([super.message = '网络或服务不可用']);
}
/// 业务错误:错误信封 code != 040000/40100/40900/40901 等)。
/// 领域层可按错误码细分子类型(见 features/pets/pet_exceptions.dart)。
class ApiBusinessException extends ApiException {
const ApiBusinessException({required this.code, required String message})
: super(message);
final int code;
}
/// 限流(HTTP 429):映射为横幅「尝试次数过多,请稍后再试」。
final class ApiRateLimitException extends ApiException {
const ApiRateLimitException([super.message = '请求过于频繁']);
}
/// 会话已失效:refresh token 失效(40102)或刷新重放后仍 401。
/// 抛出前本地会话已被清除,应用会自动回到登录页。
final class SessionExpiredException extends ApiException {
const SessionExpiredException([super.message = '会话已失效']);
}