// me.dart — 当前账户聚合视图(GET /v1/me)。 // // 一发覆盖账号/套餐/配额/今日+周流量,对齐后端契约 // (server/internal/httpapi/account.go + web/usercenter http.ts)。 /// 账户档案。字段对齐后端 snake_case JSON。 class Me { const Me({ required this.email, required this.plan, this.expiresAt, this.devicesUsed = 0, this.devicesMax = 1, this.quotaTodayMin, this.dataTodayGb = 0, this.weeklyGb = const [], this.totpEnabled = false, }); final String email; /// 'free' | 'pro' | 'team'。 final String plan; /// 订阅到期(UTC);免费版/无订阅为 null。 final DateTime? expiresAt; final int devicesUsed; final int devicesMax; /// 今日**剩余**额度(分钟);null = 不限(pro/team)。 /// 后端已算好 = 套餐每日上限 − 今日已用。 final int? quotaTodayMin; /// 今日已用流量(GB)。 final double dataTodayGb; /// 近 7 天每日流量(GB),旧→新。 final List weeklyGb; final bool totpEnabled; bool get isFree => plan == 'free'; factory Me.fromJson(Map m) { final exp = (m['expires_at'] ?? m['expire_at']) as String?; return Me( email: m['email'] as String? ?? '', plan: m['plan'] as String? ?? 'free', expiresAt: (exp != null && exp.isNotEmpty) ? DateTime.tryParse(exp) : null, devicesUsed: (m['devices_used'] as num?)?.toInt() ?? 0, devicesMax: (m['devices_max'] as num?)?.toInt() ?? 1, quotaTodayMin: (m['quota_today_min'] as num?)?.toInt(), dataTodayGb: (m['data_today_gb'] as num?)?.toDouble() ?? 0, weeklyGb: ((m['weekly_gb'] as List?) ?? const []) .map((e) => (e as num).toDouble()) .toList(), totpEnabled: m['totp_enabled'] as bool? ?? false, ); } }