feat(routing): 客户端 model + AccountApi + AsyncNotifier provider

- RoutingProfile/RoutingRule/Builtin 手写 fromJson/toJson/copyWith,对齐服务端
  GET/POST /v1/me/routing 契约;enabled 缺省容错默认 true,toJson 恒输出该字段
- AccountApi.routingProfile()/saveRoutingProfile() 封装两端点
- RoutingProfileNotifier(AsyncNotifier):addRule/removeRule/updateRule/reorder/
  setMode/setBuiltin 均本地乐观更新后落盘;save() 失败整体回退到变更前 state
  并 rethrow(不静默吞,交调用方处理)——Riverpod asyncTransition 的 seamless
  copyWithPrevious 会用当前已存 state 覆盖手动挂的错误值,故不用 AsyncError
  路径,改走显式回退
This commit is contained in:
wangjia
2026-07-28 08:27:43 +08:00
parent 82b3988974
commit 057c11eee2
5 changed files with 439 additions and 0 deletions
+12
View File
@@ -6,6 +6,7 @@ import '../models/device.dart';
import '../models/device_usage.dart';
import '../models/me.dart';
import '../models/plan.dart';
import '../models/routing_profile.dart';
import '../models/usage_point.dart';
import 'api_client.dart';
@@ -107,6 +108,17 @@ class AccountApi {
minutesRemaining: (body['minutes_remaining'] as num?)?.toInt() ?? 0,
);
}
/// GET /v1/me/routing — 可配置分流档案(无档案时服务端返回默认)。
Future<RoutingProfile> routingProfile() async =>
RoutingProfile.fromJson(await _c.getJson('/v1/me/routing'));
/// POST /v1/me/routing — 保存分流档案。非法规则由服务端 400 拒绝
/// (body: `{code,message_zh,message_en,errors:[{index,field,reason}]}`,经 ApiClient
/// 统一包装为 AuthApiException)。
Future<void> saveRoutingProfile(RoutingProfile p) async {
await _c.postJson('/v1/me/routing', p.toJson());
}
}
/// 看广告加时结果(POST /v1/ads/unlock 响应)。