diff --git a/ACCOUNT_SYSTEM_DESIGN.md b/ACCOUNT_SYSTEM_DESIGN.md new file mode 100644 index 0000000..0ef0881 --- /dev/null +++ b/ACCOUNT_SYSTEM_DESIGN.md @@ -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 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 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 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 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 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 tradeWrapper = new LambdaUpdateWrapper<>(); + tradeWrapper.eq(AccountTrade::getId, usdtTrade.getId()) + .set(AccountTrade::getQuantity, newTradeQuantity) + .set(AccountTrade::getUpdateTime, LocalDateTime.now()); + accountTradeMapper.update(null, tradeWrapper); + + // 更新资金账户 + LambdaUpdateWrapper 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 usdtWrapper = new LambdaUpdateWrapper<>(); +usdtWrapper.eq(AccountTrade::getId, usdtTrade.getId()) + .set(AccountTrade::getQuantity, newUsdtQuantity) + .set(AccountTrade::getUpdateTime, LocalDateTime.now()); +accountTradeMapper.update(null, usdtWrapper); + +// 更新BTC账户 +LambdaUpdateWrapper 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 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 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 +**状态**: 待测试验证 diff --git a/flutter_monisuo/check_flutter_data.md b/flutter_monisuo/check_flutter_data.md new file mode 100644 index 0000000..8ce1bd0 --- /dev/null +++ b/flutter_monisuo/check_flutter_data.md @@ -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().refreshAll(); +} +``` + +### 2. 检查Provider更新 +在 `AssetProvider` 的 `loadFundAccount()` 方法中添加日志: +```dart +Future 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().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` 正确包裹了UI组件: +```dart +Consumer( + 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', + ... +) +``` diff --git a/flutter_monisuo/lib/ui/pages/asset/asset_page.dart b/flutter_monisuo/lib/ui/pages/asset/asset_page.dart index 34d9401..082ca4b 100644 --- a/flutter_monisuo/lib/ui/pages/asset/asset_page.dart +++ b/flutter_monisuo/lib/ui/pages/asset/asset_page.dart @@ -28,11 +28,13 @@ class _AssetPageState extends State with AutomaticKeepAliveClientMixi @override void initState() { super.initState(); + // 强制刷新数据,确保加载最新数据 WidgetsBinding.instance.addPostFrameCallback((_) => _loadData()); } void _loadData() { - context.read().refreshAll(); + // 强制刷新,不使用缓存 + context.read().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, diff --git a/test_flutter_asset.sh b/test_flutter_asset.sh new file mode 100755 index 0000000..45daae0 --- /dev/null +++ b/test_flutter_asset.sh @@ -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 "================================"