133 lines
4.4 KiB
Dart
133 lines
4.4 KiB
Dart
// 功能描述:充值配置、创建结果和入账事实的严格类型;版本: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;
|
||
}
|