333 lines
9.3 KiB
Dart
333 lines
9.3 KiB
Dart
import 'dart:async';
|
||
import 'package:flutter/material.dart';
|
||
import '../core/network/api_response.dart';
|
||
import '../core/event/app_event_bus.dart';
|
||
import '../data/models/account_models.dart';
|
||
import '../data/models/order_models.dart';
|
||
import '../data/services/asset_service.dart';
|
||
import '../data/services/fund_service.dart';
|
||
import '../data/services/trade_service.dart';
|
||
|
||
/// 資產狀態管理
|
||
class AssetProvider extends ChangeNotifier {
|
||
final AssetService _assetService;
|
||
final FundService _fundService;
|
||
final TradeService _tradeService;
|
||
final AppEventBus _eventBus;
|
||
|
||
AssetOverview? _overview;
|
||
AccountFund? _fundAccount;
|
||
List<AccountTrade> _tradeAccounts = [];
|
||
List<AccountFlow> _flows = [];
|
||
List<OrderFund> _fundOrders = [];
|
||
List<OrderTrade> _tradeOrders = [];
|
||
bool _isLoading = false;
|
||
bool _isLoadingFlows = false;
|
||
bool _isLoadingOrders = false;
|
||
String? _error;
|
||
|
||
// 加載狀態標記,防止重複加載
|
||
bool _overviewLoaded = false;
|
||
bool _fundAccountLoaded = false;
|
||
bool _tradeAccountLoaded = false;
|
||
|
||
// 去重:防止并发调用同一 API
|
||
Completer<void>? _refreshAllCompleter;
|
||
|
||
AssetProvider(this._assetService, this._fundService, this._tradeService, this._eventBus);
|
||
|
||
// Getters
|
||
AssetOverview? get overview => _overview;
|
||
AccountFund? get fundAccount => _fundAccount;
|
||
List<AccountTrade> get tradeAccounts => _tradeAccounts;
|
||
List<AccountTrade> get holdings => _tradeAccounts;
|
||
List<AccountFlow> get flows => _flows;
|
||
List<OrderFund> get fundOrders => _fundOrders;
|
||
List<OrderTrade> get tradeOrders => _tradeOrders;
|
||
bool get isLoading => _isLoading;
|
||
bool get isLoadingFlows => _isLoadingFlows;
|
||
bool get isLoadingOrders => _isLoadingOrders;
|
||
String? get error => _error;
|
||
|
||
/// 加載資產總覽
|
||
Future<void> loadOverview({bool force = false}) async {
|
||
// 如果已經加載過且不是強制刷新,則跳過
|
||
if (_overviewLoaded && !force && _overview != null) {
|
||
return;
|
||
}
|
||
|
||
_isLoading = true;
|
||
_error = null;
|
||
notifyListeners();
|
||
|
||
try {
|
||
final response = await _assetService.getOverview();
|
||
if (response.success) {
|
||
_overview = response.data;
|
||
_overviewLoaded = true;
|
||
} else {
|
||
_error = response.message;
|
||
}
|
||
} catch (e) {
|
||
_error = '加載失敗: $e';
|
||
}
|
||
|
||
_isLoading = false;
|
||
notifyListeners();
|
||
}
|
||
|
||
/// 加載資金賬戶
|
||
Future<void> loadFundAccount({bool force = false}) async {
|
||
// 如果已經加載過且不是強制刷新,則跳過
|
||
if (_fundAccountLoaded && !force && _fundAccount != null) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
final response = await _assetService.getFundAccount();
|
||
if (response.success) {
|
||
_fundAccount = response.data;
|
||
_fundAccountLoaded = true;
|
||
notifyListeners();
|
||
}
|
||
} catch (_) {
|
||
// 忽略錯誤
|
||
}
|
||
}
|
||
|
||
/// 加載交易賬戶
|
||
Future<void> loadTradeAccount({bool force = false}) async {
|
||
// 如果已經加載過且不是強制刷新,則跳過
|
||
if (_tradeAccountLoaded && !force && _tradeAccounts.isNotEmpty) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
final response = await _assetService.getTradeAccount();
|
||
if (response.success) {
|
||
_tradeAccounts = response.data ?? [];
|
||
_tradeAccountLoaded = true;
|
||
notifyListeners();
|
||
}
|
||
} catch (_) {
|
||
// 忽略錯誤
|
||
}
|
||
}
|
||
|
||
/// 加載資金流水
|
||
Future<void> loadFlows({int? flowType, int pageNum = 1}) async {
|
||
_isLoadingFlows = true;
|
||
notifyListeners();
|
||
|
||
try {
|
||
final response = await _assetService.getFlow(
|
||
flowType: flowType,
|
||
pageNum: pageNum,
|
||
);
|
||
if (response.success && response.data != null) {
|
||
final list = response.data!['list'] as List?;
|
||
_flows = list?.map((e) => AccountFlow.fromJson(e as Map<String, dynamic>)).toList() ?? [];
|
||
}
|
||
} catch (_) {
|
||
// 忽略錯誤
|
||
}
|
||
|
||
_isLoadingFlows = false;
|
||
notifyListeners();
|
||
}
|
||
|
||
/// 劃轉資金
|
||
Future<ApiResponse<void>> transfer({
|
||
required int direction,
|
||
required String amount,
|
||
}) async {
|
||
try {
|
||
final response = await _assetService.transfer(
|
||
direction: direction,
|
||
amount: amount,
|
||
);
|
||
if (response.success) {
|
||
// 強制刷新數據
|
||
await loadOverview(force: true);
|
||
await loadFundAccount(force: true);
|
||
await loadTradeAccount(force: true);
|
||
_eventBus.fire(AppEventType.assetChanged);
|
||
}
|
||
return response;
|
||
} catch (e) {
|
||
return ApiResponse.fail('劃轉失敗: $e');
|
||
}
|
||
}
|
||
|
||
/// 充值 - 返回訂單詳情包含錢包地址
|
||
Future<ApiResponse<Map<String, dynamic>>> deposit({required String amount, String? remark}) async {
|
||
try {
|
||
final response = await _fundService.deposit(amount: amount, remark: remark);
|
||
if (response.success) {
|
||
await loadOverview(force: true);
|
||
await loadFundAccount(force: true);
|
||
_eventBus.fire(AppEventType.orderChanged);
|
||
}
|
||
return response;
|
||
} catch (e) {
|
||
return ApiResponse.fail('充值申請失敗: $e');
|
||
}
|
||
}
|
||
|
||
/// 確認已打款
|
||
Future<ApiResponse<void>> confirmPay(String orderNo) async {
|
||
try {
|
||
final response = await _fundService.confirmPay(orderNo);
|
||
if (response.success) {
|
||
await loadFundOrders();
|
||
_eventBus.fire(AppEventType.orderChanged);
|
||
}
|
||
return response;
|
||
} catch (e) {
|
||
return ApiResponse.fail('確認打款失敗: $e');
|
||
}
|
||
}
|
||
|
||
/// 提現
|
||
Future<ApiResponse<Map<String, dynamic>>> withdraw({
|
||
required String amount,
|
||
required String withdrawAddress,
|
||
String? network,
|
||
String? withdrawContact,
|
||
String? remark,
|
||
}) async {
|
||
try {
|
||
final response = await _fundService.withdraw(
|
||
amount: amount,
|
||
withdrawAddress: withdrawAddress,
|
||
network: network,
|
||
withdrawContact: withdrawContact,
|
||
remark: remark,
|
||
);
|
||
if (response.success) {
|
||
await loadOverview(force: true);
|
||
await loadFundAccount(force: true);
|
||
_eventBus.fire(AppEventType.assetChanged);
|
||
_eventBus.fire(AppEventType.orderChanged);
|
||
}
|
||
return response;
|
||
} catch (e) {
|
||
return ApiResponse.fail('提現申請失敗: $e');
|
||
}
|
||
}
|
||
|
||
/// 加載充提訂單
|
||
Future<void> loadFundOrders({int? type, int pageNum = 1, int pageSize = 20}) async {
|
||
_isLoadingOrders = true;
|
||
notifyListeners();
|
||
|
||
try {
|
||
final response = await _fundService.getOrders(
|
||
type: type,
|
||
pageNum: pageNum,
|
||
pageSize: pageSize,
|
||
);
|
||
if (response.success && response.data != null) {
|
||
final list = response.data!['list'] as List?;
|
||
_fundOrders = _fundService.parseOrderList(list);
|
||
}
|
||
} catch (_) {
|
||
// 忽略錯誤
|
||
}
|
||
|
||
_isLoadingOrders = false;
|
||
notifyListeners();
|
||
}
|
||
|
||
/// 加載交易記錄
|
||
Future<void> loadTradeOrders({int? direction, int pageNum = 1, int pageSize = 20}) async {
|
||
try {
|
||
final response = await _tradeService.getOrders(
|
||
direction: direction,
|
||
pageNum: pageNum,
|
||
pageSize: pageSize,
|
||
);
|
||
if (response.success && response.data != null) {
|
||
final list = response.data!['list'] as List?;
|
||
_tradeOrders = list?.map((e) => OrderTrade.fromJson(e as Map<String, dynamic>)).toList() ?? [];
|
||
notifyListeners();
|
||
}
|
||
} catch (_) {}
|
||
}
|
||
|
||
/// 取消訂單
|
||
Future<ApiResponse<void>> cancelOrder(String orderNo) async {
|
||
try {
|
||
final response = await _fundService.cancelOrder(orderNo);
|
||
if (response.success) {
|
||
await loadFundOrders();
|
||
await loadFundAccount(force: true);
|
||
_eventBus.fire(AppEventType.assetChanged);
|
||
_eventBus.fire(AppEventType.orderChanged);
|
||
}
|
||
return response;
|
||
} catch (e) {
|
||
return ApiResponse.fail('取消訂單失敗: $e');
|
||
}
|
||
}
|
||
|
||
/// 獲取可用提現網絡列表
|
||
Future<List<String>> getWalletNetworks() async {
|
||
try {
|
||
final response = await _fundService.getWalletNetworks();
|
||
if (response.success) {
|
||
return response.data ?? [];
|
||
}
|
||
return [];
|
||
} catch (_) {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/// 提现前检查(45天规则)
|
||
Future<Map<String, dynamic>?> withdrawCheck() async {
|
||
try {
|
||
final response = await _fundService.withdrawCheck();
|
||
if (response.success && response.data != null) {
|
||
return response.data;
|
||
}
|
||
} catch (_) {}
|
||
return null;
|
||
}
|
||
|
||
/// 刷新所有資產數據(带去重)
|
||
Future<void> refreshAll({bool force = false}) async {
|
||
if (_refreshAllCompleter != null && !_refreshAllCompleter!.isCompleted) {
|
||
return _refreshAllCompleter!.future;
|
||
}
|
||
_refreshAllCompleter = Completer<void>();
|
||
try {
|
||
await Future.wait([
|
||
loadOverview(force: force),
|
||
loadFundAccount(force: force),
|
||
loadTradeAccount(force: force),
|
||
]);
|
||
_refreshAllCompleter!.complete();
|
||
} catch (e) {
|
||
if (!_refreshAllCompleter!.isCompleted) _refreshAllCompleter!.completeError(e);
|
||
} finally {
|
||
_refreshAllCompleter = null;
|
||
}
|
||
}
|
||
|
||
/// 重置加載狀態(用於退出登錄時)
|
||
void resetLoadState() {
|
||
_overviewLoaded = false;
|
||
_fundAccountLoaded = false;
|
||
_tradeAccountLoaded = false;
|
||
_overview = null;
|
||
_fundAccount = null;
|
||
_tradeAccounts = [];
|
||
_flows = [];
|
||
_fundOrders = [];
|
||
_tradeOrders = [];
|
||
_error = null;
|
||
notifyListeners();
|
||
}
|
||
}
|