class LicenseInfo { final int id; final String type; // trial | monthly | annual | lifetime final bool isActive; final int maxDevices; final DateTime? expiresAt; final String phase; // normal | grace | readonly | locked const LicenseInfo({ required this.id, required this.type, required this.isActive, required this.maxDevices, required this.phase, this.expiresAt, }); factory LicenseInfo.fromJson(Map json) { return LicenseInfo( id: (json['id'] as num?)?.toInt() ?? 0, type: json['type'] as String? ?? 'trial', isActive: json['is_active'] as bool? ?? false, maxDevices: (json['max_devices'] as num?)?.toInt() ?? 3, expiresAt: json['expires_at'] != null ? DateTime.tryParse(json['expires_at'] as String) : null, phase: json['phase'] as String? ?? 'normal', ); } String get typeLabel { switch (type) { case 'monthly': return '月度授权'; case 'annual': return '年度授权'; case 'lifetime': return '永久授权'; default: return '试用版'; } } bool get isExpired => expiresAt != null && DateTime.now().isAfter(expiresAt!); int? get daysRemaining { if (expiresAt == null) return null; final diff = expiresAt!.difference(DateTime.now()).inDays; return diff < 0 ? 0 : diff; } bool get isReadOnlyPhase => phase == 'readonly' || phase == 'locked'; bool get isLockedPhase => phase == 'locked'; bool get needsAttention => phase == 'grace' || phase == 'readonly' || phase == 'locked'; }