fix: 完善资金充值/提现逻辑

- 添加交易账户余额检查
- 添加用户端订单管理页面
- 更新测试报告
This commit is contained in:
2026-03-23 21:25:37 +08:00
parent c294f66e1c
commit 5c8df495c3
16 changed files with 1014 additions and 347 deletions

View File

@@ -0,0 +1,199 @@
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../data/models/order_models.dart';
class _FundOrderCard extends StatelessWidget {
final OrderFund order;
const _FundOrderCard({required this.order});
Color _getStatusColor(int status, bool isDeposit) {
if (isDeposit) {
// 充值状态: 1=待付款, 2=待确认, 3=已完成, 4=已驳回, 5=已取消
switch (status) {
case 1:
return AppColorScheme.warning;
case 2:
return AppColorScheme.info;
case 3:
return AppColorScheme.success;
case 4:
return AppColorScheme.error;
case 5:
return AppColorScheme.muted;
default:
return AppColorScheme.muted;
}
} else {
// 提现状态: 1=待审批, 2=已完成, 3=已驳回, 4=已取消
switch (status) {
case 1:
return AppColorScheme.warning;
case 2:
return AppColorScheme.success;
case 3:
return AppColorScheme.error;
case 4:
return AppColorScheme.muted;
default:
return AppColorScheme.muted;
}
}
}
String _getStatusText(int status, bool isDeposit) {
if (isDeposit) {
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 '未知';
}
}
}
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
final isDeposit = order.type == 1;
final statusColor = _getStatusColor(order.status, isDeposit);
return ShadCard(
padding: AppSpacing.cardPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${isDeposit ? '+' : '-'}${order.amount} USDT',
style: theme.textTheme.large.copyWith(
color: statusColor,
fontWeight: FontWeight.bold,
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
_getStatusText(order.status, isDeposit),
style: theme.textTheme.small.copyWith(color: statusColor),
),
),
],
),
SizedBox(height: AppSpacing.sm),
Row(
children: [
Text('订单号: ', style: theme.textTheme.muted),
Text(order.orderNo, style: theme.textTheme.small),
],
),
SizedBox(height: AppSpacing.xs),
Row(
children: [
Text('创建时间: ', style: theme.textTheme.muted),
Text(
order.createTime ?? '',
style: theme.textTheme.small,
),
],
),
if (order.rejectReason != null && order.rejectReason!.isNotEmpty) ...[
SizedBox(height: AppSpacing.xs),
Row(
children: [
Text('驳回原因: ', style: theme.textTheme.muted),
Expanded(
child: Text(
order.rejectReason!,
style: theme.textTheme.small.copyWith(color: AppColorScheme.error),
),
),
],
),
],
if (order.status == 1 && isDeposit) ...[
SizedBox(height: AppSpacing.md),
Row(
children: [
Expanded(
child: ShadButton.outline(
onPressed: () => _handleConfirmPay(context),
child: const Text('已打款'),
),
),
SizedBox(width: AppSpacing.sm),
Expanded(
child: ShadButton.outline(
onPressed: () => _handleCancel(context),
child: const Text('取消订单'),
),
),
],
),
],
],
),
);
}
Future<void> _handleConfirmPay(BuildContext context) async {
final response = await context.read<AssetProvider>().confirmPay(order.orderNo);
if (context.mounted) {
if (response.success) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: const Text('已确认打款,请等待审核')),
);
context.read<AssetProvider>().refreshFundOrders();
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(response.message ?? '确认失败')),
);
}
}
}
Future<void> _handleCancel(BuildContext context) async {
final response = await context.read<AssetProvider>().cancelOrder(order.orderNo);
if (context.mounted) {
if (response.success) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: const Text('订单已取消')),
);
context.read<AssetProvider>().refreshFundOrders();
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(response.message ?? '取消失败')),
);
}
}
}
}

View File

@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../providers/asset_provider.dart';
import '../../../data/models/order_models.dart';
class _FundOrdersList extends StatelessWidget {
final AssetProvider provider;
const _FundOrdersList({required this.provider});
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
final orders = provider.fundOrders;
if (orders.isEmpty) {
return const _EmptyState(
icon: LucideIcons.receipt,
message: '暂无充提记录',
);
}
return RefreshIndicator(
onRefresh: () => provider.refreshFundOrders(),
color: theme.colorScheme.primary,
child: ListView.separated(
physics: const AlwaysScrollableScrollPhysics(),
padding: AppSpacing.pagePadding,
itemCount: orders.length,
separatorBuilder: (_, __) => Divider(color: theme.colorScheme.border, height: 1),
itemBuilder: (context, index) {
final order = orders[index];
return _FundOrderCard(order: order);
},
),
);
}
}

View File

@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:provider/provider.dart';
import '../../../core/constants/app_colors.dart';
import '../../../core/theme/app_color_scheme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../providers/asset_provider.dart';
import '../home/home_page.dart';
/// 订单管理页面
class OrdersPage extends StatefulWidget {
const OrdersPage({super.key});
@override
State<OrdersPage> createState() => _OrdersPageState();
}
class _OrdersPageState extends State<OrdersPage> with AutomaticKeepAliveClientMixin {
int _activeTab = 0;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _loadData());
}
void _loadData() {
context.read<AssetProvider>().refreshAll();
}
@override
Widget build(BuildContext context) {
super.build(context);
final theme = ShadTheme.of(context);
return Scaffold(
backgroundColor: theme.colorScheme.background,
body: Consumer<AssetProvider>(
builder: (context, provider) {
return RefreshIndicator(
onRefresh: () => provider.refreshAll(force: true),
color: theme.colorScheme.primary,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding: AppSpacing.pagePadding,
child: Column(
children: [
_TabSelector(
tabs: const ['充提记录', '交易记录'],
selectedIndex: _activeTab,
onChanged: (index) => setState(() => _activeTab = index),
),
SizedBox(height: AppSpacing.md),
_activeTab == 0
? _FundOrdersList(provider: provider)
: _TradeOrdersList(provider: provider),
],
),
),
),
),
),
);
}
}