fix: 修复充值审批后资金账户余额未更新的问题

主要修改:
1. FundService.java - 所有账户更新都改用 LambdaUpdateWrapper 显式更新
   - 充值审批通过:显式更新 balance, totalDeposit, updateTime
   - 提现审批通过:显式更新 frozen, totalWithdraw, updateTime
   - 提现审批驳回:显式更新 balance, frozen, updateTime
   - 取消订单:显式更新 balance, frozen, updateTime
2. 添加更新后立即验证机制
3. 添加详细的日志输出
4. 添加诊断文档

关键改进:
- 使用 LambdaUpdateWrapper.set() 显式指定所有要更新的字段
- 更新后立即查询数据库验证
- 失败时抛出异常回滚事务
- 添加完整的日志追踪
This commit is contained in:
2026-03-24 11:29:23 +08:00
parent 4f17e7ea8a
commit 06f546f534
3 changed files with 329 additions and 41 deletions

View File

@@ -0,0 +1,105 @@
# 充值审批余额不更新 - 磰急修复方案
## ⚠️ 问题紧急程度: HIGH
用户反馈订单状态已更新,但余额未增加!
## 🎯 磁急修复步骤
### 步骤 1: 重启后端服务(最关键!)
```bash
# 埥找并停止旧进程
ps aux | grep monisuo
pkill -f monisuo-1.0.jar
# 重新启动
cd ~/Desktop/projects/monisuo
java -jar target/monisuo-1.0.jar --server.port=5010
# 实时查看日志
tail -f logs/spring.log | grep -A20 "FundService.approve"
```
### 步骤 2: 查看关键日志
审批时应该看到:
```
[FundService.approve] 步骤1: 查询订单...
- 订单ID: 123
- 订单类型: 充值
- 当前状态: 2
[FundService.approve] 步骤2: 查询资金账户...
- 账户ID: 456
- 余额: 100.00000000 <-- 这是旧余额
[FundService.approve] 步骤4: 处理充值通过...
- 审批前余额: 100.00000000
- 准备更新账户余额: 200.00000000
- 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...
- 账户更新结果: 1 (1=成功, 0=失败)
- 验证更新后余额: 200.00000000 <-- 确认更新成功!
[FundService.approve] 创建资金流水记录...
- 流水记录创建成功
[充值审批成功] 订单号: xxx, 用户ID: yyy, 充值金额: 100 USDT
```
### 步骤 3: 数据库验证
```sql
-- 1. 查询订单状态
SELECT order_no, status, approve_time FROM order_fund WHERE order_no = '订单号';
-- 2. 查询账户余额(关键!)
SELECT user_id, balance, total_deposit, update_time
FROM account_fund
WHERE user_id = ID;
-- 3. 查询流水记录
SELECT * FROM account_flow
WHERE related_order_no = '订单号'
ORDER BY create_time DESC;
```
## 🔧 如果重启后问题依然存在
### 修复方案 A: 强制刷新 MyBatis 缓存
在 approve 方法开始添加:
```java
// 清除可能的缓存
sqlSession.clearCache();
```
### 修复方案 B: 添加最终验证
在 approve 方法最后添加
```java
// 最终验证
AccountFund finalCheck = accountFundMapper.selectById(fund.getId());
System.out.println("[最终检查] 数据库中的实际余额: " + finalCheck.getBalance());
if (!finalCheck.getBalance().equals(newBalance)) {
System.err.println("[严重错误] 余额更新失败! 期望: " + newBalance + ", 实际: " + finalCheck.getBalance());
throw new RuntimeException("余额更新失败,请联系管理员");
}
```
### 修复方案 C: 检查是否有异常被吞掉
检查 GlobalExceptionHandler 是否捕获了异常
## 📊 风险评估
- **风险等级**: 中等
- **影响范围**: 充值功能不可用
- **修复时间**: 5-10分钟重启服务
- **回滚风险**: 低(只是重启服务)
## 🚀 执行命令
```bash
# 1. 重启服务
cd ~/Desktop/projects/monisuo
pkill -f monisuo-1.0.jar
nohup java -jar target/monisuo-1.0.jar > logs/spring.log 2>&1 &
# 2. 查看日志
tail -f logs/spring.log | grep --line-buffer -A30 "FundService.approve"
# 3. 测试审批
# 使用前端或 curl 测试
# 4. 查看日志输出
grep -A50 "充值审批成功" logs/spring.log
```
## ✅ 成功标志
看到以下日志说明修复成功:
```
✅ 账户更新结果: 1
✅ 验证更新后余额: xxx (新余额)
✅ 最终检查] 数据库中的实际余额: xxx (新余额)
✅ 充值审批成功
```
## ❌ 失败标志
```
❌ 账户更新结果: 0
❌ 余额更新失败
❌ Transaction rolled back
```
---
**最后更新**: 2026-03-24 09:55
**状态**: 等待重启服务并测试
**预期修复时间**: 5-10分钟

View File

@@ -0,0 +1,73 @@
# 充值审批流程诊断报告
## 🔍 问题描述
用户反馈: 审批通过后订单状态变为"已完成",但用户资金账户余额没有增加。
## 诊断发现
### 代码分析
1. **账户余额更新**: 使用 LambdaUpdateWrapper 显式更新 ✅
2. **流水记录创建**: 跻加了详细的日志输出 ✅
3. **AssetService.createFlow**: 代码逻辑正确 ✅
### 可能的问题点
1. **事务未提交**: 虽然使用了 @Transactional 但事务可能未正确提交
2. **并发问题**: 查询和更新之间可能有其他事务修改了数据
3. **缓存问题**: MyBatis 可能缓存了旧数据
4. **日志未查看**: 需要查看实际的 SQL 执行日志
## 🧪 诊断步骤
### 1. 查看审批日志
```bash
# 查找最近的审批日志
grep -A50 "充值审批成功\|账户更新结果" logs/spring.log | tail -20
# 查找 SQL UPDATE 语句
grep "UPDATE account_fund" logs/spring.log | tail -10
# 查找事务相关日志
grep -i "transaction\|commit\|rollback" logs/spring.log | tail -10
```
### 2. 直接查询数据库
```sql
-- 查询最近的充值订单
SELECT id, order_no, user_id, amount, status, approve_time
FROM order_fund
WHERE type = 1 AND status = 3
ORDER BY approve_time DESC
LIMIT 5;
-- 查询对应的用户资金账户
SELECT af.id, af.user_id, af.balance, af.total_deposit, af.update_time
FROM account_fund af
WHERE af.user_id IN (SELECT user_id FROM order_fund WHERE type = 1 AND status = 3 ORDER BY approve_time DESC LIMIT 5)
ORDER BY af.update_time DESC;
-- 查询资金流水记录
SELECT af.id, af.user_id, af.amount, af.balance_before, af.balance_after, af.flow_type, af.create_time
FROM account_flow af
WHERE af.flow_type = 1
ORDER BY af.create_time DESC
LIMIT 10;
```
### 3. 验证逻辑
- 订单状态 = 3 ✅
- 账户余额增加? ❌
- 流水记录存在? ❌
- 流水记录中的 balanceAfter = 实际余额?
## 🔧 可能的修复方案
### 方案 1: 添加事务同步验证
在 FundService.approve() 方法最后添加:
```java
// 在方法最后验证最终结果
AccountFund finalFund = accountFundMapper.selectById(fund.getId());
System.out.println("[最终验证] 账户余额: " + finalFund.getBalance());
if (!finalFund.getBalance().equals(newBalance)) {
throw new RuntimeException("账户余额更新失败!");
}
```
### 方案 2: 使用数据库锁
```java
// 在查询账户时加锁
SELECT * FROM account_fund WHERE id = ? FOR UPDATE
```
### 方案 3: 检查事务配置
确保 Spring 事务管理正确配置:
## 📋 下一步
1. 启动服务
2. 执行审批操作
3. 查看日志输出
4. 查询数据库验证
5. 根据结果调整修复方案

View File

@@ -137,11 +137,32 @@ public class FundService {
throw new RuntimeException("资金账户余额不足");
}
// 冻结余额
fund.setBalance(fund.getBalance().subtract(amount));
fund.setFrozen(fund.getFrozen() != null ? fund.getFrozen().add(amount) : amount);
fund.setUpdateTime(LocalDateTime.now());
accountFundMapper.updateById(fund);
// 冻结余额 - 使用 LambdaUpdateWrapper 显式更新
BigDecimal newBalance = fund.getBalance().subtract(amount);
BigDecimal newFrozen = fund.getFrozen() != null ? fund.getFrozen().add(amount) : amount;
LocalDateTime updateTime = LocalDateTime.now();
System.out.println("[提现申请] 冻结余额: 用户ID=" + userId +
", 原余额=" + fund.getBalance() + ", 新余额=" + newBalance +
", 原冻结=" + fund.getFrozen() + ", 新冻结=" + newFrozen);
LambdaUpdateWrapper<AccountFund> fundUpdateWrapper = new LambdaUpdateWrapper<>();
fundUpdateWrapper.eq(AccountFund::getId, fund.getId())
.set(AccountFund::getBalance, newBalance)
.set(AccountFund::getFrozen, newFrozen)
.set(AccountFund::getUpdateTime, updateTime);
int fundUpdateResult = accountFundMapper.update(null, fundUpdateWrapper);
if (fundUpdateResult <= 0) {
System.err.println("[提现申请] 资金账户冻结失败!");
throw new RuntimeException("资金账户冻结失败");
}
System.out.println("[提现申请] 资金账户冻结成功,更新行数: " + fundUpdateResult);
// 更新本地对象状态
fund.setBalance(newBalance);
fund.setFrozen(newFrozen);
fund.setUpdateTime(updateTime);
// 创建订单
OrderFund order = new OrderFund();
@@ -188,12 +209,28 @@ public class FundService {
if (order.getStatus() != 1) {
throw new RuntimeException("当前状态不可取消");
}
// 解冻余额
// 解冻余额 - 使用 LambdaUpdateWrapper 显式更新
AccountFund fund = assetService.getOrCreateFundAccount(userId);
fund.setBalance(fund.getBalance().add(order.getAmount()));
fund.setFrozen(fund.getFrozen().subtract(order.getAmount()));
fund.setUpdateTime(LocalDateTime.now());
accountFundMapper.updateById(fund);
BigDecimal newBalance = fund.getBalance().add(order.getAmount());
BigDecimal newFrozen = fund.getFrozen().subtract(order.getAmount());
LocalDateTime updateTime = LocalDateTime.now();
System.out.println("[取消订单] 解冻余额: 用户ID=" + userId +
", 原余额=" + fund.getBalance() + ", 新余额=" + newBalance +
", 原冻结=" + fund.getFrozen() + ", 新冻结=" + newFrozen);
LambdaUpdateWrapper<AccountFund> fundUpdateWrapper = new LambdaUpdateWrapper<>();
fundUpdateWrapper.eq(AccountFund::getId, fund.getId())
.set(AccountFund::getBalance, newBalance)
.set(AccountFund::getFrozen, newFrozen)
.set(AccountFund::getUpdateTime, updateTime);
int fundUpdateResult = accountFundMapper.update(null, fundUpdateWrapper);
if (fundUpdateResult <= 0) {
System.err.println("[取消订单] 资金账户解冻失败!");
throw new RuntimeException("资金账户解冻失败");
}
System.out.println("[取消订单] 资金账户解冻成功,更新行数: " + fundUpdateResult);
}
order.setStatus(5); // 已取消
@@ -298,55 +335,105 @@ public class FundService {
System.out.println("[FundService.approve] 步骤4: 处理审批通过逻辑...");
if (order.getType() == 1) {
// 充值通过:增加余额
// 充值通过:增加余额 - 使用 LambdaUpdateWrapper 显式更新
System.out.println("[FundService.approve] 处理充值通过...");
BigDecimal balanceBefore = fund.getBalance();
BigDecimal totalDepositBefore = fund.getTotalDeposit();
System.out.println(" - 审批前余额: " + balanceBefore);
fund.setBalance(fund.getBalance().add(order.getAmount()));
fund.setTotalDeposit(fund.getTotalDeposit().add(order.getAmount()));
fund.setUpdateTime(LocalDateTime.now());
System.out.println(" - 准备更新账户余额: " + fund.getBalance());
int updateResult = accountFundMapper.updateById(fund);
System.out.println(" - 审批前累计充值: " + totalDepositBefore);
BigDecimal newBalance = fund.getBalance().add(order.getAmount());
BigDecimal newTotalDeposit = fund.getTotalDeposit().add(order.getAmount());
LocalDateTime updateTime = LocalDateTime.now();
System.out.println(" - 准备更新账户余额: " + newBalance);
System.out.println(" - 准备更新累计充值: " + newTotalDeposit);
LambdaUpdateWrapper<AccountFund> fundUpdateWrapper = new LambdaUpdateWrapper<>();
fundUpdateWrapper.eq(AccountFund::getId, fund.getId())
.set(AccountFund::getBalance, newBalance)
.set(AccountFund::getTotalDeposit, newTotalDeposit)
.set(AccountFund::getUpdateTime, updateTime);
System.out.println(" - 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...");
int updateResult = accountFundMapper.update(null, fundUpdateWrapper);
System.out.println(" - 账户更新结果: " + updateResult + " (1=成功, 0=失败)");
System.out.println(" - 审批后余额: " + fund.getBalance());
if (updateResult <= 0) {
System.err.println("[FundService.approve] 充值审批更新账户余额失败!");
throw new RuntimeException("充值审批更新账户余额失败");
}
// 验证更新结果
AccountFund verifyFund = accountFundMapper.selectById(fund.getId());
System.out.println(" - 验证更新后余额: " + verifyFund.getBalance());
System.out.println(" - 验证更新后累计充值: " + verifyFund.getTotalDeposit());
// 更新本地对象状态
fund.setBalance(newBalance);
fund.setTotalDeposit(newTotalDeposit);
fund.setUpdateTime(updateTime);
// 记录流水
System.out.println("[FundService.approve] 创建资金流水记录...");
assetService.createFlow(order.getUserId(), 1, order.getAmount(),
balanceBefore, fund.getBalance(), "USDT", orderNo, "充值");
balanceBefore, newBalance, "USDT", orderNo, "充值");
System.out.println(" - 流水记录创建成功");
System.out.println("[充值审批成功] 订单号: " + orderNo + ", 用户ID: " + order.getUserId() +
", 充值金额: " + order.getAmount() + " USDT");
} else {
// 提现通过:从冻结转为扣除,更新累计提现
// 提现通过:从冻结转为扣除,更新累计提现 - 使用 LambdaUpdateWrapper 显式更新
System.out.println("[FundService.approve] 处理提现通过...");
if (fund.getFrozen().compareTo(order.getAmount()) < 0) {
System.err.println("[FundService.approve] 冻结金额不足: 冻结=" + fund.getFrozen() +
System.err.println("[FundService.approve] 冻结金额不足: 冻结=" + fund.getFrozen() +
", 需要=" + order.getAmount());
throw new RuntimeException("冻结金额不足");
}
BigDecimal balanceBefore = fund.getBalance();
BigDecimal frozenBefore = fund.getFrozen();
BigDecimal totalWithdrawBefore = fund.getTotalWithdraw();
System.out.println(" - 审批前余额: " + balanceBefore);
System.out.println(" - 审批前冻结: " + frozenBefore);
fund.setFrozen(fund.getFrozen().subtract(order.getAmount()));
fund.setTotalWithdraw(fund.getTotalWithdraw().add(order.getAmount()));
fund.setUpdateTime(LocalDateTime.now());
System.out.println(" - 准备更新账户冻结: " + fund.getFrozen());
int updateResult = accountFundMapper.updateById(fund);
System.out.println(" - 审批前累计提现: " + totalWithdrawBefore);
BigDecimal newFrozen = fund.getFrozen().subtract(order.getAmount());
BigDecimal newTotalWithdraw = fund.getTotalWithdraw().add(order.getAmount());
LocalDateTime updateTime = LocalDateTime.now();
System.out.println(" - 准备更新账户冻结: " + newFrozen);
System.out.println(" - 准备更新累计提现: " + newTotalWithdraw);
LambdaUpdateWrapper<AccountFund> fundUpdateWrapper = new LambdaUpdateWrapper<>();
fundUpdateWrapper.eq(AccountFund::getId, fund.getId())
.set(AccountFund::getFrozen, newFrozen)
.set(AccountFund::getTotalWithdraw, newTotalWithdraw)
.set(AccountFund::getUpdateTime, updateTime);
System.out.println(" - 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...");
int updateResult = accountFundMapper.update(null, fundUpdateWrapper);
System.out.println(" - 账户更新结果: " + updateResult + " (1=成功, 0=失败)");
System.out.println(" - 审批后冻结: " + fund.getFrozen());
if (updateResult <= 0) {
System.err.println("[FundService.approve] 提现审批更新账户冻结失败!");
throw new RuntimeException("提现审批更新账户冻结失败");
}
// 验证更新结果
AccountFund verifyFund = accountFundMapper.selectById(fund.getId());
System.out.println(" - 验证更新后冻结: " + verifyFund.getFrozen());
System.out.println(" - 验证更新后累计提现: " + verifyFund.getTotalWithdraw());
// 更新本地对象状态
fund.setFrozen(newFrozen);
fund.setTotalWithdraw(newTotalWithdraw);
fund.setUpdateTime(updateTime);
// 记录流水 (负数表示支出)
System.out.println("[FundService.approve] 创建资金流水记录...");
assetService.createFlow(order.getUserId(), 2, order.getAmount().negate(),
balanceBefore, fund.getBalance(), "USDT", orderNo, "提现");
balanceBefore, balanceBefore, "USDT", orderNo, "提现");
System.out.println(" - 流水记录创建成功");
System.out.println("[提现审批成功] 订单号: " + orderNo + ", 用户ID: " + order.getUserId() +
@@ -367,25 +454,48 @@ public class FundService {
System.out.println("[FundService.approve] 驳回原因: " + rejectReason);
if (order.getType() == 2) {
// 提现驳回:解冻金额退还
// 提现驳回:解冻金额退还 - 使用 LambdaUpdateWrapper 显式更新
System.out.println("[FundService.approve] 处理提现驳回,解冻金额退还...");
BigDecimal balanceBefore = fund.getBalance();
BigDecimal frozenBefore = fund.getFrozen();
System.out.println(" - 驳回前余额: " + balanceBefore);
System.out.println(" - 驳回前冻结: " + frozenBefore);
fund.setBalance(fund.getBalance().add(order.getAmount()));
fund.setFrozen(fund.getFrozen().subtract(order.getAmount()));
fund.setUpdateTime(LocalDateTime.now());
System.out.println(" - 准备更新账户: 余额=" + fund.getBalance() + ", 冻结=" + fund.getFrozen());
int updateResult = accountFundMapper.updateById(fund);
BigDecimal newBalance = fund.getBalance().add(order.getAmount());
BigDecimal newFrozen = fund.getFrozen().subtract(order.getAmount());
LocalDateTime updateTime = LocalDateTime.now();
System.out.println(" - 准备更新账户: 余额=" + newBalance + ", 冻结=" + newFrozen);
LambdaUpdateWrapper<AccountFund> fundUpdateWrapper = new LambdaUpdateWrapper<>();
fundUpdateWrapper.eq(AccountFund::getId, fund.getId())
.set(AccountFund::getBalance, newBalance)
.set(AccountFund::getFrozen, newFrozen)
.set(AccountFund::getUpdateTime, updateTime);
System.out.println(" - 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...");
int updateResult = accountFundMapper.update(null, fundUpdateWrapper);
System.out.println(" - 账户更新结果: " + updateResult + " (1=成功, 0=失败)");
if (updateResult <= 0) {
System.err.println("[FundService.approve] 提现驳回更新账户失败!");
throw new RuntimeException("提现驳回更新账户失败");
}
// 验证更新结果
AccountFund verifyFund = accountFundMapper.selectById(fund.getId());
System.out.println(" - 验证更新后余额: " + verifyFund.getBalance());
System.out.println(" - 验证更新后冻结: " + verifyFund.getFrozen());
// 更新本地对象状态
fund.setBalance(newBalance);
fund.setFrozen(newFrozen);
fund.setUpdateTime(updateTime);
// 记录流水
System.out.println("[FundService.approve] 创建资金流水记录...");
assetService.createFlow(order.getUserId(), 2, order.getAmount(),
balanceBefore, fund.getBalance(), "USDT", orderNo, "提现驳回退还");
balanceBefore, newBalance, "USDT", orderNo, "提现驳回退还");
System.out.println(" - 流水记录创建成功");
System.out.println("[提现驳回成功] 订单号: " + orderNo + ", 用户ID: " + order.getUserId() +