fix: 修复Flutter资产页面API接口字段名称不匹配问题
主要修改:
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前端资产页面现在可以正确显示用户资产
- 充值审批后余额正确更新
- 资金账户数据查询正常
This commit is contained in:
259
ASSET_API_DIAGNOSIS.md
Normal file
259
ASSET_API_DIAGNOSIS.md
Normal file
@@ -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<String, Object> getOverview(Long userId) {
|
||||
Map<String, Object> 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<String, Object> getOverview(Long userId) {
|
||||
Map<String, Object> 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<AccountTrade> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AccountTrade::getUserId, userId)
|
||||
.gt(AccountTrade::getQuantity, BigDecimal.ZERO);
|
||||
List<AccountTrade> 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分钟
|
||||
385
DATABASE_SCHEMA.md
Normal file
385
DATABASE_SCHEMA.md
Normal file
@@ -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
|
||||
77
fix_asset_api.sh
Executable file
77
fix_asset_api.sh
Executable file
@@ -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
|
||||
71
query_fund_accounts.sh
Executable file
71
query_fund_accounts.sh
Executable file
@@ -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 "================================"
|
||||
@@ -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<Map<String, Object>> positions = new ArrayList<>();
|
||||
BigDecimal tradeBalance = BigDecimal.ZERO;
|
||||
BigDecimal totalCost = BigDecimal.ZERO; // 累计成本
|
||||
BigDecimal totalValue = BigDecimal.ZERO; // 当前价值
|
||||
|
||||
LambdaQueryWrapper<AccountTrade> 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<String, Object> 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;
|
||||
}
|
||||
|
||||
107
test_asset_api.sh
Executable file
107
test_asset_api.sh
Executable file
@@ -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 "================================"
|
||||
Reference in New Issue
Block a user