f20eddad55
- latency_probe.dart:并行 TCP 握手实测各节点 per-client RTT(服务端无法代知)。 - nodes_provider:解析 /v1/nodes 的 host/port,后台测延迟回填 Node.ping;智能选择 按实测最小;去掉 8 个伪造演示节点(空列表用中性占位,不再伪造服务器)。 - Node:+host/port/copyWith/pingLabel(未测显示 —);各端 ping 显示改 pingLabel。 - account_screens:PlansScreen 接 /v1/plans(价格 priceLabel + me 标当前档); DevicesScreen 接 /v1/me/devices + 移除 DELETE + last_seen;RedeemScreen 接 /v1/redeem(成功刷新 me,失败显示后端文案)。 - stats_page:周柱接 me.weekly_gb,本月流量/时长接 usage(30),延迟用生效节点实测。 - 顺带补登记早前 log_time.dart 的删除(log.dart 重构遗漏 stage)。 flutter analyze 0 error;115 tests passed;受影响 4 golden 重生成。 本机设备高亮 + 真实 device id 随 P6(device_info_plus)落地。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
81 lines
2.9 KiB
Dart
81 lines
2.9 KiB
Dart
// account_providers.dart — 账户域 Riverpod 装配。
|
|
//
|
|
// 此处只做依赖装配:把 authProvider 的令牌/刷新接到统一 ApiClient,
|
|
// 再产出 AccountApi。具体数据 provider(meProvider/devicesProvider/usage…)
|
|
// 在后续阶段加入本文件。
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../models/device.dart';
|
|
import '../models/me.dart';
|
|
import '../models/plan.dart';
|
|
import '../models/usage_point.dart';
|
|
import '../services/account_api.dart';
|
|
import '../services/api_client.dart';
|
|
import '../services/api_config.dart';
|
|
import 'auth_provider.dart';
|
|
|
|
/// 统一鉴权 HTTP 客户端:令牌取自 authProvider,401 时触发 AuthNotifier.refresh。
|
|
final apiClientProvider = Provider<ApiClient>((ref) {
|
|
return ApiClient(
|
|
baseUrl: kApiBaseUrl,
|
|
getToken: () => ref.read(authProvider).accessToken,
|
|
refresh: () => ref.read(authProvider.notifier).refresh(),
|
|
);
|
|
});
|
|
|
|
/// 账户域端点封装。
|
|
final accountApiProvider = Provider<AccountApi>(
|
|
(ref) => AccountApi(ref.watch(apiClientProvider)),
|
|
);
|
|
|
|
// ── 数据 provider ───────────────────────────────────────────────────────────
|
|
|
|
/// 当前账户聚合视图(GET /v1/me)。登录态变化自动重取;未登录返回默认免费档。
|
|
class MeNotifier extends AsyncNotifier<Me> {
|
|
@override
|
|
Future<Me> build() async {
|
|
final auth = ref.watch(authProvider);
|
|
if (!auth.isLoggedIn) return const Me(email: '', plan: 'free');
|
|
return ref.read(accountApiProvider).me();
|
|
}
|
|
|
|
/// 手动刷新(如连接/兑换后)。
|
|
Future<void> refresh() async {
|
|
state = const AsyncLoading();
|
|
state = await AsyncValue.guard(() => ref.read(accountApiProvider).me());
|
|
}
|
|
}
|
|
|
|
final meProvider = AsyncNotifierProvider<MeNotifier, Me>(MeNotifier.new);
|
|
|
|
/// 套餐定义(GET /v1/plans)。未登录返回空。
|
|
final plansProvider = FutureProvider<List<Plan>>((ref) async {
|
|
if (!ref.watch(authProvider).isLoggedIn) return const [];
|
|
return ref.read(accountApiProvider).plans();
|
|
});
|
|
|
|
/// 最近 N 天用量(GET /v1/usage?days=N)。未登录返回空。
|
|
final usageProvider = FutureProvider.family<List<UsagePoint>, int>((ref, days) async {
|
|
if (!ref.watch(authProvider).isLoggedIn) return const [];
|
|
return ref.read(accountApiProvider).usage(days: days);
|
|
});
|
|
|
|
/// 已登录设备列表(GET /v1/me/devices)。
|
|
class DevicesNotifier extends AsyncNotifier<List<Device>> {
|
|
@override
|
|
Future<List<Device>> build() async {
|
|
if (!ref.watch(authProvider).isLoggedIn) return const [];
|
|
return ref.read(accountApiProvider).devices();
|
|
}
|
|
|
|
/// 移除设备(DELETE)后刷新列表。
|
|
Future<void> remove(String uuid) async {
|
|
await ref.read(accountApiProvider).removeDevice(uuid);
|
|
ref.invalidateSelf();
|
|
await future;
|
|
}
|
|
}
|
|
|
|
final devicesProvider =
|
|
AsyncNotifierProvider<DevicesNotifier, List<Device>>(DevicesNotifier.new);
|