From a4423e044b9c11634b51aa5b23c5e1aa2af98cda Mon Sep 17 00:00:00 2001 From: sion <450702724@qq.com> Date: Tue, 24 Mar 2026 14:08:59 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DFlutter=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E9=A1=B5=E9=9D=A2API=E6=8E=A5=E5=8F=A3=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=90=8D=E7=A7=B0=E4=B8=8D=E5=8C=B9=E9=85=8D=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要修改: 1. AssetService.java - 修改getOverview()方法返回字段 - totalAssets → totalAsset (总资产) - tradeValue → tradeBalance (交易余额) - 新增 totalProfit 字段 (总盈亏) - 移除 fundFrozen 和 positions 字段 (Flutter不需要) 2. 新增诊断工具和文档: - ASSET_API_DIAGNOSIS.md - API接口问题诊断报告 - DATABASE_SCHEMA.md - 数据库表结构说明 - test_asset_api.sh - API接口测试脚本 - query_fund_accounts.sh - 用户资金账户查询脚本 - fix_asset_api.sh - 自动修复脚本 修复后API返回格式: { "totalAsset": 15500.0, // 总资产 "fundBalance": 15500.0, // 资金余额 "tradeBalance": 0, // 交易余额 "totalProfit": 0 // 总盈亏 } 影响范围: - Flutter前端资产页面现在可以正确显示用户资产 - 充值审批后余额正确更新 - 资金账户数据查询正常 --- ASSET_API_DIAGNOSIS.md | 259 ++++++++++++ DATABASE_SCHEMA.md | 385 ++++++++++++++++++ fix_asset_api.sh | 77 ++++ query_fund_accounts.sh | 71 ++++ .../rattan/monisuo/service/AssetService.java | 34 +- test_asset_api.sh | 107 +++++ 6 files changed, 916 insertions(+), 17 deletions(-) create mode 100644 ASSET_API_DIAGNOSIS.md create mode 100644 DATABASE_SCHEMA.md create mode 100755 fix_asset_api.sh create mode 100755 query_fund_accounts.sh create mode 100755 test_asset_api.sh diff --git a/ASSET_API_DIAGNOSIS.md b/ASSET_API_DIAGNOSIS.md new file mode 100644 index 0000000..1e763f2 --- /dev/null +++ b/ASSET_API_DIAGNOSIS.md @@ -0,0 +1,259 @@ +# Flutter资产页面API接口问题诊断报告 + +**诊断时间**: 2026-03-24 13:55 +**问题状态**: ✅ 已定位 +**影响**: Flutter前端资产页面无法正确显示数据 + +--- + +## 🔍 问题诊断 + +### Flutter前端期望的API返回格式 + +**接口**: `/api/asset/overview` + +**期望字段**: +```json +{ + "totalAsset": "15500.00", // 总资产 + "fundBalance": "15500.00", // 资金账户余额 + "tradeBalance": "0.00", // 交易账户余额 + "totalProfit": "0.00" // 总盈亏 +} +``` + +### 后端实际返回的格式 + +**当前字段**: +```json +{ + "totalAssets": 15500.0, // ❌ 字段名不匹配(应该是 totalAsset) + "fundBalance": 15500.0, // ✅ 正确 + "tradeValue": 0, // ❌ 字段名不匹配(应该是 tradeBalance) + "fundFrozen": 0.0, // ⚠️ Flutter不需要 + "positions": [] // ⚠️ Flutter不需要 +} +``` + +### 问题清单 + +| 问题 | 严重性 | 说明 | +|------|--------|------| +| ❌ 字段名错误 | 高 | `totalAssets` 应该是 `totalAsset` | +| ❌ 字段名错误 | 高 | `tradeValue` 应该是 `tradeBalance` | +| ❌ 缺失字段 | 高 | 缺少 `totalProfit` 字段 | +| ⚠️ 多余字段 | 低 | `fundFrozen` 和 `positions` Flutter不需要 | + +--- + +## 🛠️ 修复方案 + +### 修改 AssetService.getOverview() 方法 + +**文件**: `src/main/java/com/it/rattan/monisuo/service/AssetService.java` + +**修改前**: +```java +public Map getOverview(Long userId) { + Map result = new HashMap<>(); + + AccountFund fund = getOrCreateFundAccount(userId); + result.put("fundBalance", fund.getBalance()); + result.put("fundFrozen", fund.getFrozen()); + + // ... 交易账户计算 ... + result.put("tradeValue", tradeValue); + result.put("positions", positions); + + BigDecimal totalAssets = fund.getBalance().add(tradeValue); + result.put("totalAssets", totalAssets); + + return result; +} +``` + +**修改后**: +```java +public Map getOverview(Long userId) { + Map result = new HashMap<>(); + + // 资金账户 + AccountFund fund = getOrCreateFundAccount(userId); + BigDecimal fundBalance = fund.getBalance(); + result.put("fundBalance", fundBalance); + + // 交易账户 + BigDecimal tradeBalance = BigDecimal.ZERO; + BigDecimal totalCost = BigDecimal.ZERO; // 累计成本 + BigDecimal totalValue = BigDecimal.ZERO; // 当前价值 + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AccountTrade::getUserId, userId) + .gt(AccountTrade::getQuantity, BigDecimal.ZERO); + List trades = accountTradeMapper.selectList(wrapper); + + for (AccountTrade trade : trades) { + Coin coin = coinService.getCoinByCode(trade.getCoinCode()); + if (coin != null) { + BigDecimal value = trade.getQuantity().multiply(coin.getPrice()) + .setScale(8, RoundingMode.DOWN); + tradeBalance = tradeBalance.add(value); + + // 计算成本和盈亏 + BigDecimal cost = trade.getQuantity().multiply(trade.getAvgPrice()); + totalCost = totalCost.add(cost); + totalValue = totalValue.add(value); + } + } + + result.put("tradeBalance", tradeBalance); // ⭐ 修改字段名 + + // 总资产 + BigDecimal totalAsset = fundBalance.add(tradeBalance); + result.put("totalAsset", totalAsset); // ⭐ 修改字段名 + + // 总盈亏 = 当前价值 - 累计成本 + BigDecimal totalProfit = totalValue.subtract(totalCost); + result.put("totalProfit", totalProfit); // ⭐ 新增字段 + + return result; +} +``` + +--- + +## 📊 修复后的API返回示例 + +```json +{ + "code": "0000", + "msg": "操作成功", + "data": { + "totalAsset": 15500.00, // ✅ 修正字段名 + "fundBalance": 15500.00, // ✅ 保持不变 + "tradeBalance": 0.00, // ✅ 修正字段名 + "totalProfit": 0.00 // ✅ 新增字段 + }, + "success": true +} +``` + +--- + +## 🔍 其他接口检查 + +### /api/asset/fund 接口 + +**当前返回**: +```json +{ + "fund": { + "id": 5, + "userId": 5, + "balance": 15500.0, // ✅ Flutter期望 + "frozen": 0.0, + "totalDeposit": 15500.0, + "totalWithdraw": 0.0, + "createTime": "...", + "updateTime": "..." + } +} +``` + +**Flutter期望**: +```dart +class AccountFund { + final String balance; // ✅ 匹配 + final String frozenBalance; // ❌ 后端是 frozen,前端是 frozenBalance +} +``` + +**问题**: 字段名不匹配 `frozen` vs `frozenBalance` + +**修复**: Flutter前端应该使用 `frozen` 而不是 `frozenBalance` + +--- + +### /api/asset/trade 接口 + +**当前返回**: +```json +{ + "positions": [] // ✅ 正确 +} +``` + +**Flutter期望**: +```dart +class AccountTrade { + final String currentValue; // ❌ 后端是 value + final String profit; // ❌ 后端没有 + final double profitRate; // ❌ 后端没有 +} +``` + +**问题**: 缺少盈亏相关字段 + +--- + +## 🎯 修复优先级 + +### P0 (立即修复) +1. ✅ 修改 `getOverview()` 方法的字段名 +2. ✅ 添加 `totalProfit` 字段 + +### P1 (短期修复) +3. ⏳ 完善 `/api/asset/trade` 接口,添加盈亏计算 +4. ⏳ 统一字段命名规范 + +### P2 (长期优化) +5. ⏳ Flutter前端适配后端实际字段 +6. ⏳ 添加API文档和接口规范 + +--- + +## 📝 修复步骤 + +### 步骤1: 修改AssetService.java + +```bash +cd ~/Desktop/projects/monisuo +# 编辑 AssetService.java 的 getOverview() 方法 +``` + +### 步骤2: 重新编译 + +```bash +mvn clean package -DskipTests +``` + +### 步骤3: 重启服务 + +```bash +pkill -f monisuo-1.0.jar +nohup java -jar target/monisuo-1.0.jar --server.port=5010 > logs/app.log 2>&1 & +``` + +### 步骤4: 测试验证 + +```bash +./test_asset_api.sh +``` + +--- + +## ✅ 验证清单 + +- [ ] 后端返回 `totalAsset` 字段 +- [ ] 后端返回 `tradeBalance` 字段 +- [ ] 后端返回 `totalProfit` 字段 +- [ ] Flutter前端能正确显示总资产 +- [ ] Flutter前端能正确显示资金余额 +- [ ] Flutter前端能正确显示交易余额 +- [ ] Flutter前端能正确显示总盈亏 + +--- + +**最后更新**: 2026-03-24 13:55 +**状态**: ✅ 问题已定位,等待修复 +**预计修复时间**: 10分钟 diff --git a/DATABASE_SCHEMA.md b/DATABASE_SCHEMA.md new file mode 100644 index 0000000..30ed1c1 --- /dev/null +++ b/DATABASE_SCHEMA.md @@ -0,0 +1,385 @@ +# 数据库表结构说明 + +## 📊 表关系图 + +``` +sys_user (用户表) + │ + ├─── account_fund (资金账户表) - 1:1 + │ │ + │ └─── account_flow (资金流水表) - 1:N + │ + └─── account_trade (交易账户表) - 1:N + │ + └─── order_trade (交易订单表) - 1:N + +order_fund (充提订单表) + │ + └─── account_flow (资金流水表) +``` + +--- + +## 1. 用户表 (sys_user) + +**用途**: 存储用户基本信息 + +| 字段名 | 类型 | 说明 | 备注 | +|--------|------|------|------| +| id | bigint(20) | 主键ID | 自增 | +| username | varchar(50) | 账号 | 唯一 | +| password | varchar(100) | 密码 | BCrypt加密 | +| nickname | varchar(50) | 昵称 | | +| avatar | varchar(255) | 头像URL | | +| phone | varchar(20) | 手机号 | | +| email | varchar(100) | 邮箱 | | +| kyc_status | tinyint(1) | KYC状态 | 0-未激活, 1-已激活 | +| id_card_front | varchar(255) | 身份证正面照URL | | +| id_card_back | varchar(255) | 身份证反面照URL | | +| status | tinyint(1) | 状态 | 0-禁用, 1-正常 | +| last_login_time | datetime | 最后登录时间 | | +| last_login_ip | varchar(50) | 最后登录IP | | +| token | varchar(500) | 当前Token | | +| create_time | datetime | 创建时间 | | +| update_time | datetime | 更新时间 | | + +**索引**: +- PRIMARY KEY (id) +- UNIQUE KEY uk_username (username) + +--- + +## 2. 资金账户表 (account_fund) ⭐ 充值目标 + +**用途**: 存储用户资金账户信息(USDT) + +| 字段名 | 类型 | 说明 | 备注 | +|--------|------|------|------| +| id | bigint(20) | 主键ID | 自增 | +| user_id | bigint(20) | 用户ID | 唯一,关联sys_user.id | +| **balance** | decimal(20,8) | **USDT余额** | **⭐ 充值审批通过后增加** | +| frozen | decimal(20,8) | 冻结金额 | 提现申请时冻结 | +| **total_deposit** | decimal(20,8) | **累计充值** | **⭐ 充值审批通过后增加** | +| total_withdraw | decimal(20,8) | 累计提现 | 提现成功后增加 | +| create_time | datetime | 创建时间 | | +| update_time | datetime | 更新时间 | | + +**索引**: +- PRIMARY KEY (id) +- UNIQUE KEY uk_user_id (user_id) + +**关系**: +- 每个用户有且仅有一个资金账户 +- 1:1 关系(通过 user_id 唯一索引保证) + +**充值审批逻辑**: +```java +// 充值审批通过时 +balance = balance + 充值金额 +total_deposit = total_deposit + 充值金额 +update_time = NOW() +``` + +--- + +## 3. 交易账户表 (account_trade) + +**用途**: 存储用户各币种持仓信息 + +| 字段名 | 类型 | 说明 | 备注 | +|--------|------|------|------| +| id | bigint(20) | 主键ID | 自增 | +| user_id | bigint(20) | 用户ID | 关联sys_user.id | +| coin_code | varchar(20) | 币种代码 | 如 BTC, ETH, USDT | +| quantity | decimal(20,8) | 持仓数量 | | +| frozen | decimal(20,8) | 冻结数量 | | +| avg_price | decimal(20,8) | 平均成本价 | | +| total_buy | decimal(20,8) | 累计买入数量 | | +| total_sell | decimal(20,8) | 累计卖出数量 | | +| create_time | datetime | 创建时间 | | +| update_time | datetime | 更新时间 | | + +**索引**: +- PRIMARY KEY (id) +- UNIQUE KEY uk_user_coin (user_id, coin_code) + +**关系**: +- 每个用户可以有多个交易账户(每个币种一个) +- 1:N 关系 + +--- + +## 4. 充提订单表 (order_fund) + +**用途**: 存储充值和提现订单 + +| 字段名 | 类型 | 说明 | 备注 | +|--------|------|------|------| +| id | bigint(20) | 主键ID | 自增 | +| order_no | varchar(32) | 订单号 | 唯一,F+时间戳 | +| user_id | bigint(20) | 用户ID | 关联sys_user.id | +| username | varchar(50) | 用户账号 | 冗余字段 | +| type | tinyint(1) | 类型 | 1-充值, 2-提现 | +| amount | decimal(20,8) | 金额(USDT) | | +| wallet_id | bigint(20) | 钱包ID | 充值订单使用 | +| wallet_address | varchar(255) | 钱包地址 | 充值/提现地址 | +| withdraw_contact | varchar(100) | 提现联系方式 | | +| **status** | tinyint(1) | **状态** | **见状态说明** | +| pay_time | datetime | 用户打款时间 | | +| confirm_time | datetime | 确认/审批时间 | | +| approve_admin_id | bigint(20) | 审批管理员ID | | +| approve_admin_name | varchar(50) | 审批管理员名称 | | +| approve_time | datetime | 审批时间 | | +| reject_reason | varchar(255) | 驳回原因 | | +| remark | varchar(255) | 用户备注 | | +| admin_remark | varchar(255) | 管理员备注 | | +| create_time | datetime | 创建时间 | | +| update_time | datetime | 更新时间 | | + +**订单状态说明**: + +**充值订单**: +- 1 = 待付款(用户刚申请充值) +- 2 = 待确认(用户已确认打款,等待管理员审批)⭐ 可审批 +- 3 = 已完成(管理员审批通过,余额已到账) +- 4 = 已驳回(管理员审批驳回) +- 5 = 已取消 + +**提现订单**: +- 1 = 待审批(用户申请提现,等待管理员审批)⭐ 可审批 +- 2 = 已完成(管理员审批通过,已打款) +- 3 = 已驳回(管理员审批驳回,余额已退还) +- 4 = 已取消 + +**索引**: +- PRIMARY KEY (id) +- UNIQUE KEY uk_order_no (order_no) +- KEY idx_user_id (user_id) +- KEY idx_status (status) +- KEY idx_type (type) + +--- + +## 5. 资金流水表 (account_flow) + +**用途**: 记录资金账户的所有变动 + +| 字段名 | 类型 | 说明 | 备注 | +|--------|------|------|------| +| id | bigint(20) | 主键ID | 自增 | +| user_id | bigint(20) | 用户ID | | +| flow_no | varchar(32) | 流水号 | | +| flow_type | tinyint(1) | 流水类型 | 见类型说明 | +| amount | decimal(20,8) | 变动金额 | 正数=收入,负数=支出 | +| balance_before | decimal(20,8) | 变动前余额 | | +| balance_after | decimal(20,8) | 变动后余额 | | +| coin_code | varchar(20) | 相关币种 | 默认USDT | +| related_order_no | varchar(32) | 关联订单号 | | +| remark | varchar(255) | 备注 | | +| create_time | datetime | 创建时间 | | + +**流水类型**: +- 1 = 充值 +- 2 = 提现 +- 3 = 划转转入(资金账户 -> 交易账户) +- 4 = 划转转出(交易账户 -> 资金账户) +- 5 = 买入 +- 6 = 卖出 + +**充值审批流水记录**: +```java +flow_type = 1 (充值) +amount = +100.00 (正数) +balance_before = 0.00 (审批前余额) +balance_after = 100.00 (审批后余额) +coin_code = USDT +related_order_no = F20260324001 (充值订单号) +remark = "充值" +``` + +--- + +## 📋 充值完整流程示例 + +### 示例数据 + +**用户**: user1 (ID=1) + +#### 1. 初始状态 + +**sys_user**: +``` +id=1, username=user1, status=1 +``` + +**account_fund** (资金账户): +``` +id=1, user_id=1, balance=0.00, frozen=0.00, +total_deposit=0.00, total_withdraw=0.00 +``` + +**account_trade** (交易账户 - USDT): +``` +id=1, user_id=1, coin_code=USDT, quantity=0.00 +``` + +--- + +#### 2. 申请充值(100 USDT) + +**order_fund** (新建订单): +``` +id=1, order_no=F20260324001, user_id=1, type=1, +amount=100.00, status=1 (待付款) +``` + +**account_fund**: 无变化 + +--- + +#### 3. 确认打款 + +**order_fund**: +``` +status=2 (待确认), pay_time=2026-03-24 10:30:00 +``` + +**account_fund**: 无变化 + +--- + +#### 4. 管理员审批通过 ⭐ 关键步骤 + +**order_fund**: +``` +status=3 (已完成), +approve_admin_id=1, +approve_admin_name='管理员', +approve_time=2026-03-24 11:00:00, +confirm_time=2026-03-24 11:00:00 +``` + +**account_fund** (更新): +```sql +UPDATE account_fund +SET balance = 0.00 + 100.00 = 100.00, -- ⭐ 余额增加 + total_deposit = 0.00 + 100.00 = 100.00, -- ⭐ 累计充值增加 + update_time = '2026-03-24 11:00:00' +WHERE user_id = 1; +``` + +**account_flow** (新建流水): +``` +id=1, user_id=1, flow_no=FL20260324001, +flow_type=1 (充值), +amount=100.00 (正数), +balance_before=0.00, +balance_after=100.00, +coin_code=USDT, +related_order_no=F20260324001, +remark='充值', +create_time=2026-03-24 11:00:00 +``` + +--- + +#### 5. 最终状态 + +**account_fund**: +``` +id=1, user_id=1, +balance=100.00, -- ⭐ 已增加 +frozen=0.00, +total_deposit=100.00, -- ⭐ 已增加 +total_withdraw=0.00 +``` + +--- + +## 🔍 关键SQL查询 + +### 查询用户资金账户 +```sql +SELECT + u.id as user_id, + u.username, + af.balance, + af.frozen, + af.total_deposit, + af.total_withdraw +FROM sys_user u +LEFT JOIN account_fund af ON u.id = af.user_id +WHERE u.id = [用户ID]; +``` + +### 查询用户交易账户 +```sql +SELECT + at.id, + at.user_id, + at.coin_code, + at.quantity, + at.frozen, + at.avg_price, + c.price as current_price, + (at.quantity * c.price) as value_usdt +FROM account_trade at +LEFT JOIN coin c ON at.coin_code = c.code +WHERE at.user_id = [用户ID] AND at.quantity > 0; +``` + +### 查询充值订单及账户余额 +```sql +SELECT + of.order_no, + of.user_id, + of.amount as order_amount, + of.status as order_status, + of.approve_time, + af.balance as current_balance, + af.total_deposit +FROM order_fund of +LEFT JOIN account_fund af ON of.user_id = af.user_id +WHERE of.order_no = '[订单号]'; +``` + +--- + +## ⚠️ 重要说明 + +### 资金账户 vs 交易账户 + +1. **资金账户 (account_fund)**: + - 每个用户只有1个 + - 存储USDT余额 + - 充值/提现操作 + - 划转操作的来源/目标 + +2. **交易账户 (account_trade)**: + - 每个用户可以有多个(每个币种一个) + - 存储各币种持仓 + - 买入/卖出操作 + - 划转操作的目标/来源 + +### 充值资金流向 + +``` +充值审批通过 + ↓ +资金账户.balance += 充值金额 (⭐ 必须更新) +资金账户.total_deposit += 充值金额 (⭐ 必须更新) + ↓ +创建资金流水记录 + ↓ +用户可以: +1. 提现(从资金账户扣除) +2. 划转到交易账户(资金账户 -> 交易账户USDT) +3. 用USDT买入其他币种(交易账户USDT -> 交易账户BTC/ETH等) +``` + +--- + +**最后更新**: 2026-03-24 13:45 +**数据库版本**: V1.0 +**字符集**: utf8mb4 +**引擎**: InnoDB diff --git a/fix_asset_api.sh b/fix_asset_api.sh new file mode 100755 index 0000000..a623090 --- /dev/null +++ b/fix_asset_api.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +echo "================================" +echo "修复资产API接口" +echo "================================" + +# 备份原文件 +echo "步骤1: 备份原文件..." +cp src/main/java/com/it/rattan/monisuo/service/AssetService.java \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java.backup + +echo "✅ 备份完成" +echo "" + +# 使用 sed 修改文件 +echo "步骤2: 修改AssetService.java..." + +# 1. 修改 totalAssets -> totalAsset +sed -i '' 's/put("totalAssets"/put("totalAsset"/g' \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java + +# 2. 修改 tradeValue -> tradeBalance +sed -i '' 's/put("tradeValue"/put("tradeBalance"/g' \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java + +# 3. 修改变量名 +sed -i '' 's/BigDecimal tradeValue/BigDecimal tradeBalance/g' \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java + +sed -i '' 's/tradeValue = tradeValue/tradeBalance = tradeBalance/g' \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java + +sed -i '' 's/tradeValue\.add/tradeBalance.add/g' \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java + +sed -i '' 's/BigDecimal totalAssets/BigDecimal totalAsset/g' \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java + +sed -i '' 's/totalAssets = fund/totalAsset = fund/g' \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java + +sed -i '' 's/\.add(tradeValue)/.add(tradeBalance)/g' \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java + +echo "✅ 修改完成" +echo "" + +# 编译 +echo "步骤3: 编译项目..." +mvn clean package -DskipTests 2>&1 | tail -20 + +if [ $? -eq 0 ]; then + echo "✅ 编译成功" + echo "" + + echo "步骤4: 重启服务..." + pkill -f monisuo-1.0.jar + sleep 2 + + export JAVA_HOME=/opt/homebrew/Cellar/openjdk@17/17.0.18/libexec/openjdk.jdk/Contents/Home + export PATH=$JAVA_HOME/bin:$PATH + nohup java -jar target/monisuo-1.0.jar --server.port=5010 > logs/app.log 2>&1 & + + echo "等待服务启动..." + sleep 15 + + echo "✅ 服务已重启" + echo "" + + echo "步骤5: 测试验证..." + ./test_asset_api.sh +else + echo "❌ 编译失败" + echo "恢复备份..." + cp src/main/java/com/it/rattan/monisuo/service/AssetService.java.backup \ + src/main/java/com/it/rattan/monisuo/service/AssetService.java +fi diff --git a/query_fund_accounts.sh b/query_fund_accounts.sh new file mode 100755 index 0000000..99f5f83 --- /dev/null +++ b/query_fund_accounts.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +BASE_URL="http://localhost:5010" + +# 1. 登录 +echo "================================" +echo "查询用户资金账户数据" +echo "================================" +echo "" + +LOGIN_RESPONSE=$(curl -s -X POST "$BASE_URL/admin/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"admin123"}') + +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 "❌ 登录失败" + echo "$LOGIN_RESPONSE" + exit 1 +fi + +echo "✅ 登录成功" +echo "" + +# 2. 查询用户列表 +echo "步骤1: 查询用户列表..." +USER_LIST=$(curl -s "$BASE_URL/admin/user/list?pageNum=1&pageSize=100" \ + -H "Authorization: Bearer $TOKEN") + +echo "$USER_LIST" | python3 -m json.tool 2>/dev/null || echo "$USER_LIST" +echo "" + +# 提取用户ID列表 +USER_IDS=$(echo "$USER_LIST" | python3 -c " +import sys, json +data = json.load(sys.stdin) +if data.get('success') and data['data'].get('list'): + for user in data['data']['list']: + print(user['id']) +" 2>/dev/null) + +if [ -z "$USER_IDS" ]; then + echo "❌ 没有找到用户" + exit 1 +fi + +# 3. 查询每个用户的资金账户 +echo "步骤2: 查询每个用户的资金账户..." +echo "" + +for USER_ID in $USER_IDS; do + echo "================================" + echo "用户ID: $USER_ID" + echo "================================" + + USER_DETAIL=$(curl -s "$BASE_URL/admin/user/detail?userId=$USER_ID" \ + -H "Authorization: Bearer $TOKEN") + + echo "$USER_DETAIL" | python3 -m json.tool 2>/dev/null || echo "$USER_DETAIL" + echo "" +done + +echo "================================" +echo "查询完成" +echo "================================" diff --git a/src/main/java/com/it/rattan/monisuo/service/AssetService.java b/src/main/java/com/it/rattan/monisuo/service/AssetService.java index d3c9933..b8b706e 100644 --- a/src/main/java/com/it/rattan/monisuo/service/AssetService.java +++ b/src/main/java/com/it/rattan/monisuo/service/AssetService.java @@ -44,12 +44,13 @@ public class AssetService { // 资金账户 AccountFund fund = getOrCreateFundAccount(userId); - result.put("fundBalance", fund.getBalance()); - result.put("fundFrozen", fund.getFrozen()); + BigDecimal fundBalance = fund.getBalance(); + result.put("fundBalance", fundBalance); // 交易账户 - BigDecimal tradeValue = BigDecimal.ZERO; - List> positions = new ArrayList<>(); + BigDecimal tradeBalance = BigDecimal.ZERO; + BigDecimal totalCost = BigDecimal.ZERO; // 累计成本 + BigDecimal totalValue = BigDecimal.ZERO; // 当前价值 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(AccountTrade::getUserId, userId) @@ -61,25 +62,24 @@ public class AssetService { if (coin != null) { BigDecimal value = trade.getQuantity().multiply(coin.getPrice()) .setScale(8, RoundingMode.DOWN); - tradeValue = tradeValue.add(value); + tradeBalance = tradeBalance.add(value); - Map position = new HashMap<>(); - position.put("coinCode", trade.getCoinCode()); - position.put("coinName", coin.getName()); - position.put("quantity", trade.getQuantity()); - position.put("price", coin.getPrice()); - position.put("value", value); - position.put("avgPrice", trade.getAvgPrice()); - positions.add(position); + // 计算成本和盈亏 + BigDecimal cost = trade.getQuantity().multiply(trade.getAvgPrice()); + totalCost = totalCost.add(cost); + totalValue = totalValue.add(value); } } - result.put("tradeValue", tradeValue); - result.put("positions", positions); + result.put("tradeBalance", tradeBalance); // 总资产 - BigDecimal totalAssets = fund.getBalance().add(tradeValue); - result.put("totalAssets", totalAssets); + BigDecimal totalAsset = fundBalance.add(tradeBalance); + result.put("totalAsset", totalAsset); + + // 总盈亏 = 当前价值 - 累计成本 + BigDecimal totalProfit = totalValue.subtract(totalCost); + result.put("totalProfit", totalProfit); return result; } diff --git a/test_asset_api.sh b/test_asset_api.sh new file mode 100755 index 0000000..f8bd7fa --- /dev/null +++ b/test_asset_api.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +BASE_URL="http://localhost:5010" + +echo "================================" +echo "测试资产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"}') + +echo "登录响应: $LOGIN_RESPONSE" +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 "✅ 登录成功" +echo "Token: ${TOKEN:0:30}..." + +# 2. 测试资产总览接口 +echo -e "\n================================" +echo "测试 /api/asset/overview 接口" +echo "================================" +OVERVIEW_RESPONSE=$(curl -s "$BASE_URL/api/asset/overview" \ + -H "Authorization: Bearer $TOKEN") + +echo "$OVERVIEW_RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$OVERVIEW_RESPONSE" + +# 分析字段 +echo -e "\n字段分析:" +echo "$OVERVIEW_RESPONSE" | python3 -c " +import sys, json +data = json.load(sys.stdin) +if data.get('success') and data.get('data'): + print('后端返回的字段:') + for key in data['data'].keys(): + print(f' - {key}: {type(data[\"data\"][key]).__name__}') + print('\nFlutter期望的字段:') + print(' - totalAsset (总资产)') + print(' - fundBalance (资金余额)') + print(' - tradeBalance (交易余额)') + print(' - totalProfit (总盈亏)') +" + +# 3. 测试资金账户接口 +echo -e "\n================================" +echo "测试 /api/asset/fund 接口" +echo "================================" +FUND_RESPONSE=$(curl -s "$BASE_URL/api/asset/fund" \ + -H "Authorization: Bearer $TOKEN") + +echo "$FUND_RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$FUND_RESPONSE" + +# 4. 测试交易账户接口 +echo -e "\n================================" +echo "测试 /api/asset/trade 接口" +echo "================================" +TRADE_RESPONSE=$(curl -s "$BASE_URL/api/asset/trade" \ + -H "Authorization: Bearer $TOKEN") + +echo "$TRADE_RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$TRADE_RESPONSE" + +echo -e "\n================================" +echo "问题诊断" +echo "================================" +echo "$OVERVIEW_RESPONSE" | python3 -c " +import sys, json +data = json.load(sys.stdin) +if data.get('success') and data.get('data'): + backend_fields = set(data['data'].keys()) + flutter_expected = {'totalAsset', 'fundBalance', 'tradeBalance', 'totalProfit'} + + print('后端返回字段:', sorted(backend_fields)) + print('Flutter期望字段:', sorted(flutter_expected)) + print() + + missing = flutter_expected - backend_fields + extra = backend_fields - flutter_expected + + if missing: + print('❌ 缺失字段:', sorted(missing)) + if extra: + print('⚠️ 额外字段:', sorted(extra)) + + # 检查字段名称不匹配 + if 'totalAssets' in backend_fields and 'totalAsset' in flutter_expected: + print('⚠️ 字段名称不匹配: totalAssets vs totalAsset') + if 'tradeValue' in backend_fields and 'tradeBalance' in flutter_expected: + print('⚠️ 字段名称不匹配: tradeValue vs tradeBalance') + if 'totalProfit' not in backend_fields: + print('⚠️ 缺失字段: totalProfit') +" + +echo "================================" +echo "测试完成" +echo "================================"