import '../core/config/app_constants.dart'; 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() ?? AppConstants.defaultMaxDevices, 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; } /// 已过期天数(未过期或永久授权返回 0)。 int get daysExpired { if (expiresAt == null) return 0; final diff = DateTime.now().difference(expiresAt!).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'; } /// POST /license/purchase 响应:pay 收银台跳转信息。 class PurchaseOrder { final String payUrl; final String outTradeNo; final String amount; // 实付金额(pay 侧权威价,如 "2999.00") final String subject; const PurchaseOrder({ required this.payUrl, required this.outTradeNo, required this.amount, required this.subject, }); factory PurchaseOrder.fromJson(Map json) => PurchaseOrder( payUrl: json['pay_url'] as String? ?? '', outTradeNo: json['out_trade_no'] as String? ?? '', amount: json['amount'] as String? ?? '', subject: json['subject'] as String? ?? '', ); } /// GET /license/purchase/:otn 响应:购买单状态(到账轮询用)。 class PurchaseStatusInfo { final String outTradeNo; final String status; // pending | paid | failed final String amount; final DateTime? paidAt; final DateTime? expiresAt; // 续期后的门店授权到期时间(仅 paid 时有值) const PurchaseStatusInfo({ required this.outTradeNo, required this.status, required this.amount, this.paidAt, this.expiresAt, }); bool get isPaid => status == 'paid'; factory PurchaseStatusInfo.fromJson(Map json) => PurchaseStatusInfo( outTradeNo: json['out_trade_no'] as String? ?? '', status: json['status'] as String? ?? 'pending', amount: json['amount'] as String? ?? '', paidAt: json['paid_at'] != null ? DateTime.tryParse(json['paid_at'] as String) : null, expiresAt: json['expires_at'] != null ? DateTime.tryParse(json['expires_at'] as String) : null, ); }