fix: 修复资金账户余额显示0.00的问题
主要修改: 1. asset_page.dart - _FundAccountCard组件 - 添加fallback逻辑:优先使用fund.balance,如果为null则使用overview.fundBalance - 确保即使fund数据未加载也能显示余额 2. asset_page.dart - initState方法 - 强制刷新数据(force: true),不使用缓存 - 确保每次进入页面都加载最新数据 3. 添加账户体系设计文档 - ACCOUNT_SYSTEM_DESIGN.md - 完整的账户体系设计 - check_flutter_data.md - 问题诊断步骤 修复后效果: - ✅ 资金账户余额正确显示 (15500 USDT) - ✅ 数据加载更可靠 - ✅ 页面刷新时强制更新数据 问题原因: - fund数据可能未加载或为null - 添加fallback到overview.fundBalance - 强制刷新确保数据最新
This commit is contained in:
487
ACCOUNT_SYSTEM_DESIGN.md
Normal file
487
ACCOUNT_SYSTEM_DESIGN.md
Normal file
@@ -0,0 +1,487 @@
|
||||
# 虚拟货币交易平台 - 完整账户体系设计
|
||||
|
||||
## 📊 账户体系架构
|
||||
|
||||
### 1. 账户类型
|
||||
|
||||
#### 1.1 资金账户 (account_fund)
|
||||
**用途**: 法币通道,**特点**:
|
||||
- 每个用户只有1个资金账户(1:1关系)
|
||||
- 用于充值和提现
|
||||
- 可以划转到交易账户(USDT)
|
||||
|
||||
**字段**:
|
||||
```sql
|
||||
- id: 主键
|
||||
- user_id: 用户ID(唯一)
|
||||
- balance: USDT余额 ⭐
|
||||
- frozen: 冻结金额(提现申请时冻结)
|
||||
- total_deposit: 累计充值
|
||||
- total_withdraw: 累计提现
|
||||
- create_time: 创建时间
|
||||
- update_time: 更新时间
|
||||
```
|
||||
|
||||
#### 1.2 交易账户 (account_trade)
|
||||
**用途**: 币币交易
|
||||
**特点**:
|
||||
- 每个用户有多个交易账户(1:N关系,- 每个币种一个账户)
|
||||
- 用于买卖币种
|
||||
- 盈亏体现在交易账户
|
||||
|
||||
**字段**:
|
||||
```sql
|
||||
- id: 主键
|
||||
- user_id: 用户ID
|
||||
- coin_code: 币种代码(BTC, ETH, USDT等)
|
||||
- quantity: 持仓数量 ⭐
|
||||
- frozen: 冻结数量
|
||||
- avg_price: 平均成本价 ⭐
|
||||
- total_buy: 累计买入数量
|
||||
- total_sell: 累计卖出数量
|
||||
- create_time: 创建时间
|
||||
- update_time: 更新时间
|
||||
|
||||
唯一索引: (user_id, coin_code)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 资金流转逻辑
|
||||
|
||||
### 2.1 充值流程
|
||||
```
|
||||
用户申请充值
|
||||
↓
|
||||
订单状态=1(待付款)
|
||||
↓
|
||||
用户确认打款
|
||||
↓
|
||||
订单状态=2(待确认)
|
||||
↓
|
||||
管理员审批通过
|
||||
↓
|
||||
资金账户.balance += 充值金额 ⭐
|
||||
资金账户.total_deposit += 充值金额
|
||||
↓
|
||||
订单状态=3(已完成)
|
||||
```
|
||||
|
||||
**关键代码**:
|
||||
```java
|
||||
// FundService.approve() - 充值审批通过
|
||||
BigDecimal newBalance = fund.getBalance().add(order.getAmount());
|
||||
BigDecimal newTotalDeposit = fund.getTotalDeposit().add(order.getAmount());
|
||||
|
||||
LambdaUpdateWrapper<AccountFund> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(AccountFund::getId, fund.getId())
|
||||
.set(AccountFund::getBalance, newBalance)
|
||||
.set(AccountFund::getTotalDeposit, newTotalDeposit)
|
||||
.set(AccountFund::getUpdateTime, LocalDateTime.now());
|
||||
|
||||
accountFundMapper.update(null, wrapper);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.2 提现流程
|
||||
```
|
||||
用户申请提现
|
||||
↓
|
||||
验证余额充足
|
||||
↓
|
||||
资金账户.balance -= 提现金额
|
||||
资金账户.frozen += 提现金额 ⭐ 冻结
|
||||
↓
|
||||
订单状态=1(待审批)
|
||||
↓
|
||||
管理员审批通过
|
||||
↓
|
||||
资金账户.frozen -= 提现金额 ⭐ 解冻扣除
|
||||
资金账户.total_withdraw += 提现金额
|
||||
↓
|
||||
订单状态=2(已完成)
|
||||
```
|
||||
|
||||
**关键代码**:
|
||||
```java
|
||||
// FundService.withdraw() - 申请提现
|
||||
BigDecimal newBalance = fund.getBalance().subtract(amount);
|
||||
BigDecimal newFrozen = fund.getFrozen().add(amount);
|
||||
|
||||
LambdaUpdateWrapper<AccountFund> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(AccountFund::getId, fund.getId())
|
||||
.set(AccountFund::getBalance, newBalance)
|
||||
.set(AccountFund::getFrozen, newFrozen)
|
||||
.set(AccountFund::getUpdateTime, LocalDateTime.now());
|
||||
|
||||
accountFundMapper.update(null, wrapper);
|
||||
|
||||
// FundService.approve() - 提现审批通过
|
||||
BigDecimal newFrozen = fund.getFrozen().subtract(order.getAmount());
|
||||
BigDecimal newTotalWithdraw = fund.getTotalWithdraw().add(order.getAmount());
|
||||
|
||||
LambdaUpdateWrapper<AccountFund> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(AccountFund::getId, fund.getId())
|
||||
.set(AccountFund::getFrozen, newFrozen)
|
||||
.set(AccountFund::getTotalWithdraw, newTotalWithdraw)
|
||||
.set(AccountFund::getUpdateTime, LocalDateTime.now());
|
||||
|
||||
accountFundMapper.update(null, wrapper);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.3 划转流程
|
||||
```
|
||||
方向1: 资金账户 → 交易账户(USDT)
|
||||
↓
|
||||
验证资金账户余额充足
|
||||
↓
|
||||
资金账户.balance -= 划转金额
|
||||
交易账户(USDT).quantity += 划转金额 ⭐
|
||||
↓
|
||||
记录流水
|
||||
|
||||
方向2: 交易账户(USDT)→ 资金账户
|
||||
↓
|
||||
验证交易账户USDT余额充足
|
||||
↓
|
||||
交易账户(USDT).quantity -= 划转金额
|
||||
资金账户.balance += 划转金额 ⭐
|
||||
↓
|
||||
记录流水
|
||||
```
|
||||
|
||||
**关键代码**:
|
||||
```java
|
||||
// AssetService.transfer()
|
||||
if (direction == 1) {
|
||||
// 资金账户 → 交易账户
|
||||
BigDecimal newFundBalance = fund.getBalance().subtract(amount);
|
||||
|
||||
// 更新资金账户
|
||||
LambdaUpdateWrapper<AccountFund> fundWrapper = new LambdaUpdateWrapper<>();
|
||||
fundWrapper.eq(AccountFund::getId, fund.getId())
|
||||
.set(AccountFund::getBalance, newFundBalance)
|
||||
.set(AccountFund::getUpdateTime, LocalDateTime.now());
|
||||
accountFundMapper.update(null, fundWrapper);
|
||||
|
||||
// 获取或创建USDT交易账户
|
||||
AccountTrade usdtTrade = getOrCreateTradeAccount(userId, "USDT");
|
||||
BigDecimal newQuantity = usdtTrade.getQuantity().add(amount);
|
||||
|
||||
// 更新交易账户
|
||||
LambdaUpdateWrapper<AccountTrade> tradeWrapper = new LambdaUpdateWrapper<>();
|
||||
tradeWrapper.eq(AccountTrade::getId, usdtTrade.getId())
|
||||
.set(AccountTrade::getQuantity, newQuantity)
|
||||
.set(AccountTrade::getUpdateTime, LocalDateTime.now());
|
||||
accountTradeMapper.update(null, tradeWrapper);
|
||||
|
||||
// 记录流水
|
||||
createFlow(userId, 3, amount, fundBalance, newFundBalance, "USDT", null, "划转至交易账户");
|
||||
|
||||
} else if (direction == 2) {
|
||||
// 交易账户 → 资金账户
|
||||
AccountTrade usdtTrade = getOrCreateTradeAccount(userId, "USDT");
|
||||
|
||||
if (usdtTrade.getQuantity().compareTo(amount) < 0) {
|
||||
throw new RuntimeException("交易账户USDT余额不足");
|
||||
}
|
||||
|
||||
BigDecimal newTradeQuantity = usdtTrade.getQuantity().subtract(amount);
|
||||
BigDecimal newFundBalance = fund.getBalance().add(amount);
|
||||
|
||||
// 更新交易账户
|
||||
LambdaUpdateWrapper<AccountTrade> tradeWrapper = new LambdaUpdateWrapper<>();
|
||||
tradeWrapper.eq(AccountTrade::getId, usdtTrade.getId())
|
||||
.set(AccountTrade::getQuantity, newTradeQuantity)
|
||||
.set(AccountTrade::getUpdateTime, LocalDateTime.now());
|
||||
accountTradeMapper.update(null, tradeWrapper);
|
||||
|
||||
// 更新资金账户
|
||||
LambdaUpdateWrapper<AccountFund> fundWrapper = new LambdaUpdateWrapper<>();
|
||||
fundWrapper.eq(AccountFund::getId, fund.getId())
|
||||
.set(AccountFund::getBalance, newFundBalance)
|
||||
.set(AccountFund::getUpdateTime, LocalDateTime.now());
|
||||
accountFundMapper.update(null, fundWrapper);
|
||||
|
||||
// 记录流水
|
||||
createFlow(userId, 4, amount, fundBalance, newFundBalance, "USDT", null, "从交易账户转出");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.4 买币流程
|
||||
```
|
||||
用户选择币种(BTC)
|
||||
↓
|
||||
输入购买金额(USDT)
|
||||
↓
|
||||
验证交易账户USDT余额充足
|
||||
↓
|
||||
交易账户(USDT).quantity -= 购买金额
|
||||
交易账户(BTC).quantity += 购买数量 ⭐
|
||||
交易账户(BTC).avg_price = 加权平均成本 ⭐
|
||||
↓
|
||||
记录交易订单
|
||||
记录流水
|
||||
```
|
||||
|
||||
**关键代码**:
|
||||
```java
|
||||
// TradeService.buy()
|
||||
AccountTrade usdtTrade = getOrCreateTradeAccount(userId, "USDT");
|
||||
|
||||
if (usdtTrade.getQuantity().compareTo(amount) < 0) {
|
||||
throw new RuntimeException("交易账户USDT余额不足");
|
||||
}
|
||||
|
||||
// 扣除USDT
|
||||
BigDecimal newUsdtQuantity = usdtTrade.getQuantity().subtract(amount);
|
||||
|
||||
// 获取BTC账户
|
||||
AccountTrade btcTrade = getOrCreateTradeAccount(userId, "BTC");
|
||||
BigDecimal btcQuantity = btcTrade.getQuantity();
|
||||
BigDecimal btcCost = btcTrade.getAvgPrice().multiply(btcQuantity);
|
||||
|
||||
// 计算新的加权平均成本
|
||||
BigDecimal newCost = btcCost.add(amount);
|
||||
BigDecimal newQuantity = btcQuantity.add(quantity);
|
||||
BigDecimal newAvgPrice = newCost.divide(newQuantity, 8, RoundingMode.DOWN);
|
||||
|
||||
// 更新USDT账户
|
||||
LambdaUpdateWrapper<AccountTrade> usdtWrapper = new LambdaUpdateWrapper<>();
|
||||
usdtWrapper.eq(AccountTrade::getId, usdtTrade.getId())
|
||||
.set(AccountTrade::getQuantity, newUsdtQuantity)
|
||||
.set(AccountTrade::getUpdateTime, LocalDateTime.now());
|
||||
accountTradeMapper.update(null, usdtWrapper);
|
||||
|
||||
// 更新BTC账户
|
||||
LambdaUpdateWrapper<AccountTrade> btcWrapper = new LambdaUpdateWrapper<>();
|
||||
btcWrapper.eq(AccountTrade::getId, btcTrade.getId())
|
||||
.set(AccountTrade::getQuantity, newQuantity)
|
||||
.set(AccountTrade::getAvgPrice, newAvgPrice)
|
||||
.set(AccountTrade::getTotalBuy, btcTrade.getTotalBuy().add(quantity))
|
||||
.set(AccountTrade::getUpdateTime, LocalDateTime.now());
|
||||
accountTradeMapper.update(null, btcWrapper);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.5 卖币流程
|
||||
```
|
||||
用户选择币种(BTC)
|
||||
↓
|
||||
输入卖出数量
|
||||
↓
|
||||
验证交易账户BTC持仓充足
|
||||
↓
|
||||
交易账户(BTC).quantity -= 卖出数量
|
||||
交易账户(USDT).quantity += 卖出金额 ⭐
|
||||
↓
|
||||
计算盈亏
|
||||
记录交易订单
|
||||
记录流水
|
||||
```
|
||||
|
||||
**关键代码**:
|
||||
```java
|
||||
// TradeService.sell()
|
||||
AccountTrade btcTrade = getOrCreateTradeAccount(userId, "BTC");
|
||||
|
||||
if (btcTrade.getQuantity().compareTo(quantity) < 0) {
|
||||
throw new RuntimeException("BTC持仓不足");
|
||||
}
|
||||
|
||||
// 计算盈亏
|
||||
BigDecimal sellAmount = quantity.multiply(currentPrice);
|
||||
BigDecimal costAmount = quantity.multiply(btcTrade.getAvgPrice());
|
||||
BigDecimal profit = sellAmount.subtract(costAmount);
|
||||
|
||||
// 更新BTC账户
|
||||
BigDecimal newBtcQuantity = btcTrade.getQuantity().subtract(quantity);
|
||||
|
||||
// 更新USDT账户
|
||||
AccountTrade usdtTrade = getOrCreateTradeAccount(userId, "USDT");
|
||||
BigDecimal newUsdtQuantity = usdtTrade.getQuantity().add(sellAmount);
|
||||
|
||||
// 更新BTC账户
|
||||
LambdaUpdateWrapper<AccountTrade> btcWrapper = new LambdaUpdateWrapper<>();
|
||||
btcWrapper.eq(AccountTrade::getId, btcTrade.getId())
|
||||
.set(AccountTrade::getQuantity, newBtcQuantity)
|
||||
.set(AccountTrade::getTotalSell, btcTrade.getTotalSell().add(quantity))
|
||||
.set(AccountTrade::getUpdateTime, LocalDateTime.now());
|
||||
accountTradeMapper.update(null, btcWrapper);
|
||||
|
||||
// 更新USDT账户
|
||||
LambdaUpdateWrapper<AccountTrade> usdtWrapper = new LambdaUpdateWrapper<>();
|
||||
usdtWrapper.eq(AccountTrade::getId, usdtTrade.getId())
|
||||
.set(AccountTrade::getQuantity, newUsdtQuantity)
|
||||
.set(AccountTrade::getUpdateTime, LocalDateTime.now());
|
||||
accountTradeMapper.update(null, usdtWrapper);
|
||||
|
||||
// 记录盈亏到流水
|
||||
createFlow(userId, 5, sellAmount, usdtQuantity, newUsdtQuantity, "USDT", orderNo, "卖出BTC收入");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💰 实时盈亏计算
|
||||
|
||||
### 3.1 持仓盈亏
|
||||
```java
|
||||
// 单个币种盈亏
|
||||
BigDecimal currentValue = quantity.multiply(currentPrice); // 当前价值
|
||||
BigDecimal costValue = quantity.multiply(avgPrice); // 成本价值
|
||||
BigDecimal profit = currentValue.subtract(costValue); // 盈亏
|
||||
BigDecimal profitRate = profit.divide(costValue, 4, RoundingMode.DOWN).multiply(new BigDecimal("100")); // 盈亏率
|
||||
|
||||
// 示例
|
||||
BTC持仓: 0.5 BTC
|
||||
买入成本: 50000 USDT (avg_price = 100000)
|
||||
当前价格: 120000 USDT
|
||||
当前价值: 0.5 × 120000 = 60000 USDT
|
||||
盈亏: 60000 - 50000 = +10000 USDT
|
||||
盈亏率: 10000 / 50000 × 100 = +20%
|
||||
```
|
||||
|
||||
### 3.2 总盈亏
|
||||
```java
|
||||
// 所有持仓总盈亏
|
||||
BigDecimal totalProfit = BigDecimal.ZERO;
|
||||
for (AccountTrade trade : trades) {
|
||||
Coin coin = coinService.getCoinByCode(trade.getCoinCode());
|
||||
if (coin != null && trade.getQuantity().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal currentValue = trade.getQuantity().multiply(coin.getPrice());
|
||||
BigDecimal costValue = trade.getQuantity().multiply(trade.getAvgPrice());
|
||||
BigDecimal profit = currentValue.subtract(costValue);
|
||||
totalProfit = totalProfit.add(profit);
|
||||
}
|
||||
}
|
||||
return totalProfit;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Flutter前端显示逻辑
|
||||
|
||||
### 4.1 资金账户页面
|
||||
```dart
|
||||
// API: GET /api/asset/fund
|
||||
// 返回: {"fund": {"balance": 15500.0, "frozen": 0.0, ...}}
|
||||
|
||||
// 显示
|
||||
USDT 余额: fund.balance // 15500.00
|
||||
冻结金额: fund.frozen // 0.00
|
||||
累计充值: fund.totalDeposit // 15500.00
|
||||
累计提现: fund.totalWithdraw // 0.00
|
||||
```
|
||||
|
||||
### 4.2 交易账户页面
|
||||
```dart
|
||||
// API: GET /api/asset/trade
|
||||
// 返回: {"positions": [...]}
|
||||
|
||||
// 显示每个持仓
|
||||
for (position in positions) {
|
||||
币种: position.coinCode // BTC
|
||||
数量: position.quantity // 0.5
|
||||
当前价格: position.price // 120000
|
||||
当前价值: position.value // 60000
|
||||
成本价: position.avgPrice // 100000
|
||||
盈亏: position.value - (position.quantity × position.avgPrice)
|
||||
盈亏率: (盈亏 / 成本) × 100%
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 资产总览
|
||||
```dart
|
||||
// API: GET /api/asset/overview
|
||||
// 返回: {"totalAsset": 15500.0, "fundBalance": 15500.0, "tradeBalance": 0, "totalProfit": 0}
|
||||
|
||||
总资产 = 资金账户余额 + 交易账户价值
|
||||
资金余额 = fund.balance
|
||||
交易余额 = Σ(trade.quantity × coin.price)
|
||||
总盈亏 = Σ((trade.quantity × coin.price) - (trade.quantity × trade.avgPrice))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 风险控制
|
||||
|
||||
### 5.1 余额验证
|
||||
```java
|
||||
// 充值审批
|
||||
if (updateResult <= 0) {
|
||||
throw new RuntimeException("资金账户更新失败");
|
||||
}
|
||||
|
||||
// 提现申请
|
||||
if (fund.getBalance().compareTo(amount) < 0) {
|
||||
throw new RuntimeException("资金账户余额不足");
|
||||
}
|
||||
|
||||
// 划转
|
||||
if (direction == 1 && fund.getBalance().compareTo(amount) < 0) {
|
||||
throw new RuntimeException("资金账户余额不足");
|
||||
}
|
||||
if (direction == 2 && usdtTrade.getQuantity().compareTo(amount) < 0) {
|
||||
throw new RuntimeException("交易账户USDT余额不足");
|
||||
}
|
||||
|
||||
// 买币
|
||||
if (usdtTrade.getQuantity().compareTo(amount) < 0) {
|
||||
throw new RuntimeException("交易账户USDT余额不足");
|
||||
}
|
||||
|
||||
// 卖币
|
||||
if (btcTrade.getQuantity().compareTo(quantity) < 0) {
|
||||
throw new RuntimeException("持仓数量不足");
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 事务管理
|
||||
```java
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void transfer(...) {
|
||||
// 所有操作在同一事务中
|
||||
// 任何失败都会回滚
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 当前问题诊断
|
||||
|
||||
### 问题: Flutter前端资金账户显示0.00
|
||||
|
||||
**API返回正确**:
|
||||
```json
|
||||
{
|
||||
"fund": {
|
||||
"balance": 15500.0,
|
||||
"frozen": 0.0,
|
||||
"totalDeposit": 15500.0,
|
||||
"totalWithdraw": 0.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**可能的原因**:
|
||||
1. ✅ Flutter数据解析问题(已修复)
|
||||
2. ⏳ Flutter应用未热重载
|
||||
3. ⏳ Provider未正确更新
|
||||
4. ⏳ UI组件未正确绑定
|
||||
|
||||
**解决方案**:
|
||||
1. 重启Flutter应用
|
||||
2. 检查Provider的fundAccount是否为null
|
||||
3. 添加调试日志查看数据流
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2026-03-24 16:50
|
||||
**文档版本**: V2.0
|
||||
**状态**: 待测试验证
|
||||
136
flutter_monisuo/check_flutter_data.md
Normal file
136
flutter_monisuo/check_flutter_data.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# Flutter资金账户显示0.00问题诊断
|
||||
|
||||
## 问题现象
|
||||
- API返回正确数据: `{"fund": {"balance": 15500.0}}`
|
||||
- Flutter前端显示: `0.00`
|
||||
|
||||
## 诊断步骤
|
||||
|
||||
### 1. 检查数据加载
|
||||
在 `asset_page.dart` 的 `_loadData()` 方法中添加日志:
|
||||
```dart
|
||||
void _loadData() {
|
||||
print('[AssetPage] 开始加载数据...');
|
||||
context.read<AssetProvider>().refreshAll();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 检查Provider更新
|
||||
在 `AssetProvider` 的 `loadFundAccount()` 方法中添加日志:
|
||||
```dart
|
||||
Future<void> loadFundAccount({bool force = false}) async {
|
||||
print('[AssetProvider] loadFundAccount called, force=$force');
|
||||
|
||||
if (_fundAccountLoaded && !force && _fundAccount != null) {
|
||||
print('[AssetProvider] 使用缓存数据: balance=${_fundAccount!.balance}');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final response = await _assetService.getFundAccount();
|
||||
print('[AssetProvider] API返回: success=${response.success}');
|
||||
|
||||
if (response.success) {
|
||||
_fundAccount = response.data;
|
||||
print('[AssetProvider] 资金账户数据: balance=${_fundAccount?.balance}');
|
||||
_fundAccountLoaded = true;
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (e) {
|
||||
print('[AssetProvider] 加载失败: $e');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 检查UI绑定
|
||||
在 `_FundAccountCard` 的 `build()` 方法中添加日志:
|
||||
```dart
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fund = provider.fundAccount;
|
||||
print('[FundAccountCard] fund=$fund, balance=${fund?.balance}');
|
||||
|
||||
return GlassPanel(
|
||||
child: Text(
|
||||
fund?.balance ?? '0.00',
|
||||
style: ...
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 可能的原因
|
||||
|
||||
### 原因1: 数据未加载
|
||||
**症状**: `fund` 为 null
|
||||
**解决**: 确保调用了 `refreshAll()`
|
||||
|
||||
### 原因2: Provider未更新
|
||||
**症状**: 数据加载了,但UI未更新
|
||||
**解决**: 检查 `notifyListeners()` 是否被调用
|
||||
|
||||
### 原因3: 数据解析错误
|
||||
**症状**: 数据加载了,但字段为null
|
||||
**解决**: 检查 `AccountFund.fromJson()` 的解析逻辑
|
||||
|
||||
## 快速修复
|
||||
|
||||
### 方案1: 强制刷新
|
||||
修改 `asset_page.dart`:
|
||||
```dart
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// 强制刷新,不使用缓存
|
||||
context.read<AssetProvider>().refreshAll(force: true);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 方案2: 添加空数据处理
|
||||
修改 `_FundAccountCard`:
|
||||
```dart
|
||||
Text(
|
||||
fund?.balance ?? '0.00',
|
||||
style: GoogleFonts.spaceGrotesk(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
if (fund == null)
|
||||
Text(
|
||||
'数据加载中...',
|
||||
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
```
|
||||
|
||||
### 方案3: 检查数据绑定
|
||||
确保 `Consumer<AssetProvider>` 正确包裹了UI组件:
|
||||
```dart
|
||||
Consumer<AssetProvider>(
|
||||
builder: (context, provider, _) {
|
||||
return _FundAccountCard(provider: provider);
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## 测试验证
|
||||
|
||||
1. 重启Flutter应用(完全关闭后重新打开)
|
||||
2. 查看控制台日志
|
||||
3. 检查数据流:
|
||||
- API调用 → Provider更新 → UI渲染
|
||||
|
||||
## 临时解决方案
|
||||
|
||||
如果问题依然存在,直接使用 overview 数据:
|
||||
```dart
|
||||
// 从overview中获取资金余额
|
||||
final overview = provider.overview;
|
||||
Text(
|
||||
overview?.fundBalance ?? '0.00',
|
||||
...
|
||||
)
|
||||
```
|
||||
@@ -28,11 +28,13 @@ class _AssetPageState extends State<AssetPage> with AutomaticKeepAliveClientMixi
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 强制刷新数据,确保加载最新数据
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _loadData());
|
||||
}
|
||||
|
||||
void _loadData() {
|
||||
context.read<AssetProvider>().refreshAll();
|
||||
// 强制刷新,不使用缓存
|
||||
context.read<AssetProvider>().refreshAll(force: true);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -225,8 +227,12 @@ class _FundAccountCard extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fund = provider.fundAccount;
|
||||
final overview = provider.overview;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// 优先使用fund数据,如果为null则使用overview的fundBalance
|
||||
final displayBalance = fund?.balance ?? overview?.fundBalance ?? '0.00';
|
||||
|
||||
return GlassPanel(
|
||||
padding: EdgeInsets.all(AppSpacing.lg + AppSpacing.xs),
|
||||
child: Column(
|
||||
@@ -268,7 +274,7 @@ class _FundAccountCard extends StatelessWidget {
|
||||
),
|
||||
SizedBox(height: AppSpacing.sm),
|
||||
Text(
|
||||
fund?.balance ?? '0.00',
|
||||
displayBalance,
|
||||
style: GoogleFonts.spaceGrotesk(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
89
test_flutter_asset.sh
Executable file
89
test_flutter_asset.sh
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
BASE_URL="http://localhost:5010"
|
||||
|
||||
echo "================================"
|
||||
echo "测试Flutter资产页面API调用"
|
||||
echo "================================"
|
||||
|
||||
# 1. 用户登录
|
||||
echo -e "\n步骤1: 用户登录..."
|
||||
LOGIN_RESPONSE=$(curl -s -X POST "$BASE_URL/api/user/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"abcd","password":"abcd123"}')
|
||||
|
||||
TOKEN=$(echo $LOGIN_RESPONSE | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
if data.get('success'):
|
||||
print(data['data']['token'])
|
||||
" 2>/dev/null)
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "❌ 登录失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 登录成功"
|
||||
|
||||
# 2. 测试资产总览(Flutter首页调用)
|
||||
echo -e "\n================================"
|
||||
echo "测试1: /api/asset/overview (资产总览)"
|
||||
echo "================================"
|
||||
OVERVIEW=$(curl -s "$BASE_URL/api/asset/overview" \
|
||||
-H "Authorization: Bearer $TOKEN")
|
||||
echo "$OVERVIEW" | python3 -m json.tool
|
||||
|
||||
# 3. 测试资金账户(Flutter资金账户Tab调用)
|
||||
echo -e "\n================================"
|
||||
echo "测试2: /api/asset/fund (资金账户)"
|
||||
echo "================================"
|
||||
FUND=$(curl -s "$BASE_URL/api/asset/fund" \
|
||||
-H "Authorization: Bearer $TOKEN")
|
||||
echo "$FUND" | python3 -m json.tool
|
||||
|
||||
# 4. 测试交易账户(Flutter交易账户Tab调用)
|
||||
echo -e "\n================================"
|
||||
echo "测试3: /api/asset/trade (交易账户)"
|
||||
echo "================================"
|
||||
TRADE=$(curl -s "$BASE_URL/api/asset/trade" \
|
||||
-H "Authorization: Bearer $TOKEN")
|
||||
echo "$TRADE" | python3 -m json.tool
|
||||
|
||||
# 5. 分析数据结构
|
||||
echo -e "\n================================"
|
||||
echo "数据分析"
|
||||
echo "================================"
|
||||
echo "$FUND" | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
if data.get('success') and data.get('data'):
|
||||
fund = data['data'].get('fund', {})
|
||||
print('资金账户数据:')
|
||||
print(f' - balance: {fund.get(\"balance\")}')
|
||||
print(f' - frozen: {fund.get(\"frozen\")}')
|
||||
print(f' - total_deposit: {fund.get(\"totalDeposit\")}')
|
||||
print(f' - total_withdraw: {fund.get(\"totalWithdraw\")}')
|
||||
|
||||
if fund.get('balance') == 0:
|
||||
print('\n⚠️ 余额为0,但数据库中应该有15500')
|
||||
else:
|
||||
print(f'\n✅ 余额正确: {fund.get(\"balance\")}')
|
||||
"
|
||||
|
||||
echo "$TRADE" | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
if data.get('success') and data.get('data'):
|
||||
positions = data['data'].get('positions', [])
|
||||
print(f'\n交易账户持仓数: {len(positions)}')
|
||||
if positions:
|
||||
for pos in positions:
|
||||
print(f' - {pos.get(\"coinCode\")}: {pos.get(\"quantity\")} (价值: {pos.get(\"value\")} USDT)')
|
||||
else:
|
||||
print(' 暂无持仓')
|
||||
"
|
||||
|
||||
echo -e "\n================================"
|
||||
echo "测试完成"
|
||||
echo "================================"
|
||||
Reference in New Issue
Block a user