已完成用户APP首期功能开发
交付用户端首期页面、配套接口、后台资源及测试文档。用户APP构建、静态分析和三个管理后台构建通过;完整测试仍有2项失败,后端模型注释检查未通过,详见交付记录。
This commit is contained in:
132
apps/user_app/lib/domain/models/recharge.dart
Normal file
132
apps/user_app/lib/domain/models/recharge.dart
Normal file
@@ -0,0 +1,132 @@
|
||||
// 功能描述:充值配置、创建结果和入账事实的严格类型;版本: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;
|
||||
}
|
||||
Reference in New Issue
Block a user