Files
platforms/apps/user_app/lib/domain/models/recharge.dart

133 lines
4.4 KiB
Dart
Raw Normal View History

// 功能描述充值配置、创建结果和入账事实的严格类型版本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<RechargeChannel> channels;
final RechargeAgreement? agreement;
factory RechargeOptions.fromJson(Map<String, Object?> 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<String, Object?>.from(item as Map)),
],
agreement: json['agreement'] == null
? null
: RechargeAgreement.fromJson(Map<String, Object?>.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<String, Object?> 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<String, Object?> 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<String, Object?> 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<RechargeRecord> items;
final String nextCursor;
}