feat: 重构充值提现功能,添加冷钱包管理
后端改动: - 新增冷钱包管理模块(ColdWallet实体、Mapper、Service、Controller) - 充值流程:创建订单→显示钱包地址→用户确认打款→管理员审核 - 提现流程:用户输入地址和联系方式→冻结余额→管理员审核 - OrderFund新增字段:walletId, walletAddress, withdrawContact, payTime, confirmTime 前端改动(monisuo-admin): - 新增冷钱包管理页面(wallets.vue) - 优化订单管理页面,支持新的状态流转 - 添加调试日志帮助排查登录问题 前端改动(flutter_monisuo): - 更新OrderFund模型支持新字段 - 充值成功后显示钱包地址弹窗 - 提现时收集提现地址和联系方式 - 新增资金订单页面 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -72,25 +72,39 @@ class OrderFund {
|
||||
final int id;
|
||||
final String orderNo;
|
||||
final int userId;
|
||||
final int orderType; // 1=充值, 2=提现
|
||||
final String username;
|
||||
final int type; // 1=充值, 2=提现
|
||||
final String amount;
|
||||
final int status; // 1=待审核, 2=已通过, 3=已拒绝, 4=已取消
|
||||
final int status;
|
||||
// 充值状态: 1=待付款, 2=待确认, 3=已完成, 4=已驳回, 5=已取消
|
||||
// 提现状态: 1=待审批, 2=已完成, 3=已驳回, 4=已取消
|
||||
final int? walletId; // 冷钱包ID(充值)
|
||||
final String? walletAddress; // 钱包地址(充值/提现)
|
||||
final String? withdrawContact; // 提现联系方式
|
||||
final String remark;
|
||||
final String? auditRemark;
|
||||
final String? rejectReason;
|
||||
final String? adminRemark;
|
||||
final DateTime? createTime;
|
||||
final DateTime? auditTime;
|
||||
final DateTime? payTime; // 用户确认打款时间
|
||||
final DateTime? confirmTime; // 管理员确认时间
|
||||
|
||||
OrderFund({
|
||||
required this.id,
|
||||
required this.orderNo,
|
||||
required this.userId,
|
||||
required this.orderType,
|
||||
required this.username,
|
||||
required this.type,
|
||||
required this.amount,
|
||||
required this.status,
|
||||
this.walletId,
|
||||
this.walletAddress,
|
||||
this.withdrawContact,
|
||||
required this.remark,
|
||||
this.auditRemark,
|
||||
this.rejectReason,
|
||||
this.adminRemark,
|
||||
this.createTime,
|
||||
this.auditTime,
|
||||
this.payTime,
|
||||
this.confirmTime,
|
||||
});
|
||||
|
||||
factory OrderFund.fromJson(Map<String, dynamic> json) {
|
||||
@@ -98,42 +112,117 @@ class OrderFund {
|
||||
id: json['id'] as int? ?? 0,
|
||||
orderNo: json['orderNo'] as String? ?? '',
|
||||
userId: json['userId'] as int? ?? 0,
|
||||
orderType: json['orderType'] as int? ?? 1,
|
||||
username: json['username'] as String? ?? '',
|
||||
type: json['type'] as int? ?? 1,
|
||||
amount: json['amount']?.toString() ?? '0.00',
|
||||
status: json['status'] as int? ?? 1,
|
||||
walletId: json['walletId'] as int?,
|
||||
walletAddress: json['walletAddress'] as String?,
|
||||
withdrawContact: json['withdrawContact'] as String?,
|
||||
remark: json['remark']?.toString() ?? '',
|
||||
auditRemark: json['auditRemark']?.toString(),
|
||||
rejectReason: json['rejectReason'] as String?,
|
||||
adminRemark: json['adminRemark'] as String?,
|
||||
createTime: json['createTime'] != null
|
||||
? DateTime.tryParse(json['createTime'])
|
||||
: null,
|
||||
auditTime: json['auditTime'] != null
|
||||
? DateTime.tryParse(json['auditTime'])
|
||||
payTime: json['payTime'] != null
|
||||
? DateTime.tryParse(json['payTime'])
|
||||
: null,
|
||||
confirmTime: json['confirmTime'] != null
|
||||
? DateTime.tryParse(json['confirmTime'])
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// 订单类型文字
|
||||
String get orderTypeText => orderType == 1 ? '充值' : '提现';
|
||||
String get typeText => type == 1 ? '充值' : '提现';
|
||||
|
||||
/// 状态文字
|
||||
/// 状态文字 (根据类型区分)
|
||||
String get statusText {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return '待审核';
|
||||
case 2:
|
||||
return '已通过';
|
||||
case 3:
|
||||
return '已拒绝';
|
||||
case 4:
|
||||
return '已取消';
|
||||
default:
|
||||
return '未知';
|
||||
if (type == 1) {
|
||||
// 充值状态
|
||||
switch (status) {
|
||||
case 1:
|
||||
return '待付款';
|
||||
case 2:
|
||||
return '待确认';
|
||||
case 3:
|
||||
return '已完成';
|
||||
case 4:
|
||||
return '已驳回';
|
||||
case 5:
|
||||
return '已取消';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
} else {
|
||||
// 提现状态
|
||||
switch (status) {
|
||||
case 1:
|
||||
return '待审批';
|
||||
case 2:
|
||||
return '已完成';
|
||||
case 3:
|
||||
return '已驳回';
|
||||
case 4:
|
||||
return '已取消';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 是否为充值
|
||||
bool get isDeposit => orderType == 1;
|
||||
bool get isDeposit => type == 1;
|
||||
|
||||
/// 是否可取消
|
||||
bool get canCancel => status == 1;
|
||||
/// 充值: 仅待付款可取消
|
||||
/// 提现: 仅待审批可取消
|
||||
bool get canCancel {
|
||||
if (type == 1) {
|
||||
return status == 1; // 充值待付款
|
||||
} else {
|
||||
return status == 1; // 提现待审批
|
||||
}
|
||||
}
|
||||
|
||||
/// 是否可确认打款 (仅充值待付款)
|
||||
bool get canConfirmPay => type == 1 && status == 1;
|
||||
}
|
||||
|
||||
/// 冷钱包模型
|
||||
class ColdWallet {
|
||||
final int id;
|
||||
final String name;
|
||||
final String address;
|
||||
final String network;
|
||||
final bool isDefault;
|
||||
final int status; // 0=禁用, 1=启用
|
||||
final DateTime? createTime;
|
||||
|
||||
ColdWallet({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.address,
|
||||
required this.network,
|
||||
required this.isDefault,
|
||||
required this.status,
|
||||
this.createTime,
|
||||
});
|
||||
|
||||
factory ColdWallet.fromJson(Map<String, dynamic> json) {
|
||||
return ColdWallet(
|
||||
id: json['id'] as int? ?? 0,
|
||||
name: json['name'] as String? ?? '',
|
||||
address: json['address'] as String? ?? '',
|
||||
network: json['network'] as String? ?? 'TRC20',
|
||||
isDefault: json['isDefault'] as bool? ?? false,
|
||||
status: json['status'] as int? ?? 1,
|
||||
createTime: json['createTime'] != null
|
||||
? DateTime.tryParse(json['createTime'])
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
bool get isEnabled => status == 1;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,23 @@ class FundService {
|
||||
|
||||
FundService(this._client);
|
||||
|
||||
/// 获取默认钱包地址
|
||||
Future<ApiResponse<ColdWallet>> getDefaultWallet() async {
|
||||
final response = await _client.get<Map<String, dynamic>>(
|
||||
ApiEndpoints.defaultWallet,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(
|
||||
ColdWallet.fromJson(response.data!),
|
||||
response.message,
|
||||
);
|
||||
}
|
||||
return ApiResponse.fail(response.message ?? '获取钱包地址失败');
|
||||
}
|
||||
|
||||
/// 申请充值
|
||||
/// 返回包含 orderNo, amount, status, walletAddress, walletNetwork 的信息
|
||||
Future<ApiResponse<Map<String, dynamic>>> deposit({
|
||||
required String amount,
|
||||
String? remark,
|
||||
@@ -22,15 +38,27 @@ class FundService {
|
||||
);
|
||||
}
|
||||
|
||||
/// 用户确认已打款
|
||||
Future<ApiResponse<void>> confirmPay(String orderNo) async {
|
||||
return _client.post<void>(
|
||||
ApiEndpoints.confirmPay,
|
||||
data: {'orderNo': orderNo},
|
||||
);
|
||||
}
|
||||
|
||||
/// 申请提现
|
||||
Future<ApiResponse<Map<String, dynamic>>> withdraw({
|
||||
required String amount,
|
||||
required String withdrawAddress,
|
||||
String? withdrawContact,
|
||||
String? remark,
|
||||
}) async {
|
||||
return _client.post<Map<String, dynamic>>(
|
||||
ApiEndpoints.withdraw,
|
||||
data: {
|
||||
'amount': amount,
|
||||
'withdrawAddress': withdrawAddress,
|
||||
if (withdrawContact != null) 'withdrawContact': withdrawContact,
|
||||
if (remark != null) 'remark': remark,
|
||||
},
|
||||
);
|
||||
@@ -62,19 +90,9 @@ class FundService {
|
||||
);
|
||||
}
|
||||
|
||||
/// 获取充提订单详情
|
||||
Future<ApiResponse<OrderFund>> getOrderDetail(String orderNo) async {
|
||||
final response = await _client.get<Map<String, dynamic>>(
|
||||
ApiEndpoints.fundOrders,
|
||||
queryParameters: {'orderNo': orderNo},
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
return ApiResponse.success(
|
||||
OrderFund.fromJson(response.data!),
|
||||
response.message,
|
||||
);
|
||||
}
|
||||
return ApiResponse.fail(response.message ?? '获取订单详情失败');
|
||||
/// 解析充提记录列表
|
||||
List<OrderFund> parseOrderList(List<dynamic>? list) {
|
||||
if (list == null) return [];
|
||||
return list.map((e) => OrderFund.fromJson(e as Map<String, dynamic>)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user