// 功能描述:充值配置、创建结果和入账事实的严格类型;版本:1.0.0。 import 'client_models.dart'; int rechargeCents(Object? value) { if (value is! int || value < 0) throw const FormatException('充值金额格式异常'); return value; } /// 十进制文本直接转换为分,禁止浮点舍入、指数或负数。 int? parseRechargeAmount(String value) { final text = value.trim(); if (!RegExp(r'^\d{1,9}(\.\d{1,2})?$').hasMatch(text)) return null; final parts = text.split('.'); return int.parse(parts[0]) * 100 + (parts.length == 1 ? 0 : int.parse(parts[1].padRight(2, '0'))); } class RechargeOptions { const RechargeOptions({ required this.min, required this.max, required this.channels, this.agreement, }); final int min, max; final List channels; final RechargeAgreement? agreement; factory RechargeOptions.fromJson(Map json) { final min = rechargeCents(json['min_amount']), max = rechargeCents(json['max_amount']); if (min < 1 || max < min || json['channels'] is! List) throw const FormatException('充值配置异常'); return RechargeOptions( min: min, max: max, channels: [ for (final item in json['channels'] as List) RechargeChannel.fromJson(Map.from(item as Map)), ], agreement: json['agreement'] == null ? null : RechargeAgreement.fromJson(Map.from(json['agreement'] as Map)), ); } } class RechargeChannel { const RechargeChannel(this.name, {required this.app, required this.wap}); final String name; final bool app, wap; String get title => name == 'wechat' ? '微信支付' : '支付宝'; factory RechargeChannel.fromJson(Map json) { if (!['wechat', 'alipay'].contains(json['channel']) || json['app'] is! bool || json['wap'] is! bool) { throw const FormatException('支付方式异常'); } return RechargeChannel( json['channel'] as String, app: json['app'] as bool, wap: json['wap'] as bool, ); } } class RechargeAgreement { const RechargeAgreement(this.identity, this.version, this.title, this.body); final String identity, title, body; final int version; factory RechargeAgreement.fromJson(Map json) { if (json['identity'] is! String || json['body'] is! String || json['version'] is! int || (json['identity'] as String).trim().isEmpty || (json['body'] as String).trim().isEmpty || (json['version'] as int) < 1) { throw const FormatException('充值协议不完整'); } return RechargeAgreement( json['identity'] as String, json['version'] as int, json['title'] as String? ?? '充值协议', json['body'] as String, ); } } /// 只有充值状态23表示钱包已经入账;渠道关闭或SDK返回都不能替代这一事实。 class RechargeRecord { const RechargeRecord({ required this.identity, required this.number, required this.amount, required this.status, required this.channel, required this.createdAt, this.paymentStatus, }); final String identity, number, channel; final int amount, status; final int? paymentStatus; final DateTime createdAt; bool get credited => status == 23; String get statusText => credited ? '已到账' : paymentStatus == 30 ? '支付已关闭,未入账' : status == 10 ? '待确认到账' : '状态待确认'; String get amountText => moneyText(amount); factory RechargeRecord.fromJson(Map json) { final date = DateTime.tryParse(json['created_at'] as String? ?? ''); if ((json['identity'] as String? ?? '').isEmpty || json['recharge_status'] is! int || date == null) { throw const FormatException('充值记录不完整'); } return RechargeRecord( identity: json['identity'] as String, number: json['recharge_no'] as String? ?? '', amount: rechargeCents(json['amount']), status: json['recharge_status'] as int, channel: json['channel'] as String? ?? '', createdAt: date.toLocal(), paymentStatus: json['payment_status'] as int?, ); } } class RechargeRecordPage { const RechargeRecordPage(this.items, this.nextCursor); final List items; final String nextCursor; }