ba53a0d478
- api_config.dart:API 基址单源(消除各处重复 _kApiUrl) - api_client.dart:统一鉴权 HTTP 客户端,401→AuthNotifier.refresh→重试一次, 错误统一 AuthApiException;+ 单测覆盖 401 刷新重试 - 模型 me/device/plan/usage_point(对齐后端 snake_case 契约) - account_api.dart:/v1/me、/v1/plans、/v1/me/devices(列表+删)、/v1/usage、 /v1/redeem、/v1/ads/unlock 封装 - auth_provider:注入 AuthApi + refresh();account_providers 装配 ApiClient/AccountApi - 顺带修复 onboarding 提交遗留的测试 stub(_NullTokenStore 缺 isOnboarded/markOnboarded) flutter analyze 0 error;114 tests passed。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
55 lines
1.6 KiB
Dart
55 lines
1.6 KiB
Dart
// plan.dart — 套餐定义(GET /v1/plans)。
|
|
//
|
|
// 价格字段(price_cents/currency/period)由后端 P2 新增;旧响应缺失时降级为 0。
|
|
import '../l10n/app_text.dart';
|
|
|
|
class Plan {
|
|
const Plan({
|
|
required this.code,
|
|
required this.nameZh,
|
|
required this.nameEn,
|
|
this.dailyMinutes,
|
|
this.adGate = false,
|
|
this.priceCents = 0,
|
|
this.currency = 'CNY',
|
|
this.period = 'month',
|
|
});
|
|
|
|
/// 'free' | 'pro' | 'team'。
|
|
final String code;
|
|
final String nameZh;
|
|
final String nameEn;
|
|
|
|
/// 每日额度(分钟);null = 不限。
|
|
final int? dailyMinutes;
|
|
final bool adGate;
|
|
|
|
/// 价格(分)。0 = 免费。
|
|
final int priceCents;
|
|
final String currency;
|
|
|
|
/// 计费周期:'month' | 'year'。
|
|
final String period;
|
|
|
|
String localizedName(AppLang lang) => lang == AppLang.zh ? nameZh : nameEn;
|
|
|
|
/// 价格展示:¥0 / ¥25 …(整数分转元,去尾零)。
|
|
String priceLabel() {
|
|
final symbol = currency == 'CNY' ? '¥' : (currency == 'USD' ? '\$' : '');
|
|
final yuan = priceCents / 100.0;
|
|
final s = yuan == yuan.roundToDouble() ? yuan.toStringAsFixed(0) : yuan.toStringAsFixed(2);
|
|
return '$symbol$s';
|
|
}
|
|
|
|
factory Plan.fromJson(Map<String, dynamic> m) => Plan(
|
|
code: m['code'] as String? ?? '',
|
|
nameZh: m['name_zh'] as String? ?? '',
|
|
nameEn: m['name_en'] as String? ?? '',
|
|
dailyMinutes: (m['daily_minutes'] as num?)?.toInt(),
|
|
adGate: m['ad_gate'] as bool? ?? false,
|
|
priceCents: (m['price_cents'] as num?)?.toInt() ?? 0,
|
|
currency: m['currency'] as String? ?? 'CNY',
|
|
period: m['period'] as String? ?? 'month',
|
|
);
|
|
}
|