docs: 添加充值审批余额验证工具和紧急修复文档

新增文件:
1. EMERGENCY_BALANCE_FIX.md - 充值审批余额未更新的紧急修复方案
2. verify_balance.sh - 自动化余额验证脚本

改进内容:
- 添加详细的诊断步骤
- 添加数据库验证SQL
- 添加日志检查方法
- 添加服务重启指南

这些工具用于帮助诊断和修复充值审批后资金账户余额未更新的问题
This commit is contained in:
2026-03-24 11:54:24 +08:00
parent 06f546f534
commit fb7bef6424
3 changed files with 558 additions and 6 deletions

321
EMERGENCY_BALANCE_FIX.md Normal file
View File

@@ -0,0 +1,321 @@
# 充值审批余额未更新 - 紧急诊断和修复方案
**问题**: 充值审批成功后,用户资金账户余额没有增加
**紧急程度**: 🔴 高危
**时间**: 2026-03-24 11:45
---
## 🚨 立即检查清单
### 1. 服务是否重启?(最可能的原因)
```bash
# 检查服务运行时间
ps -p $(pgrep -f monisuo-1.0.jar) -o lstart,etime
# 如果运行时间早于代码提交时间,说明没有重启
# 代码提交时间: 2026-03-24 11:36
# 如果服务运行时间早于这个时间,必须重启!
```
**重启服务**:
```bash
# 停止旧服务
pkill -f "monisuo-1.0.jar"
# 启动新服务
cd ~/Desktop/projects/monisuo
nohup java -jar target/monisuo-1.0.jar > logs/spring.log 2>&1 &
# 查看日志
tail -f logs/spring.log
```
---
### 2. 查看审批日志
```bash
# 查找最近的审批日志
grep -A30 "充值审批成功" logs/spring.log | tail -50
# 应该看到:
# ✅ [FundService.approve] 步骤4: 处理充值通过...
# ✅ 审批前余额: 0.00
# ✅ 准备更新账户余额: 100.00
# ✅ 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...
# ✅ 账户更新结果: 1 (1=成功, 0=失败)
# ✅ 验证更新后余额: 100.00
# ✅ [充值审批成功] ...
```
---
### 3. 数据库验证
```sql
-- 1. 查询订单状态
SELECT order_no, user_id, amount, 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;
-- 4. 如果余额未更新,手动更新(临时方案)
UPDATE account_fund
SET balance = balance + [],
total_deposit = total_deposit + [],
update_time = NOW()
WHERE user_id = [ID];
```
---
## 🔍 问题分析
### 当前代码逻辑(已修复版本)
```java
// FundService.approve() - 充值审批通过
if (order.getType() == 1 && status == 2) {
// 1. 计算新余额
BigDecimal newBalance = fund.getBalance().add(order.getAmount());
BigDecimal newTotalDeposit = fund.getTotalDeposit().add(order.getAmount());
// 2. 使用 LambdaUpdateWrapper 显式更新
LambdaUpdateWrapper<AccountFund> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(AccountFund::getId, fund.getId())
.set(AccountFund::getBalance, newBalance)
.set(AccountFund::getTotalDeposit, newTotalDeposit)
.set(AccountFund::getUpdateTime, LocalDateTime.now());
// 3. 执行更新
int updateResult = accountFundMapper.update(null, wrapper);
// 4. 验证更新
if (updateResult <= 0) {
throw new RuntimeException("充值审批更新账户余额失败");
}
// 5. 再次查询验证
AccountFund verifyFund = accountFundMapper.selectById(fund.getId());
if (!verifyFund.getBalance().equals(newBalance)) {
throw new RuntimeException("余额更新失败");
}
// 6. 记录流水
assetService.createFlow(userId, 1, amount, balanceBefore, newBalance, "USDT", orderNo, "充值");
}
```
### 可能的问题点
#### 问题1: 服务未重启 ⭐⭐⭐⭐⭐
**症状**: 代码已修改,但服务还在运行旧版本
**原因**: Java 应用需要重启才能加载新代码
**解决**: 重启服务
#### 问题2: 事务回滚
**症状**: 日志显示更新成功,但数据库未变化
**原因**: 事务在后续步骤失败并回滚
**验证**: 查看日志是否有异常
**解决**: 修复后续步骤的错误
#### 问题3: 查询错误账户
**症状**: 更新了账户,但查询的是另一个账户
**原因**: user_id 不匹配
**验证**: 检查订单的 user_id 和账户的 user_id
**解决**: 确保查询条件正确
#### 问题4: MyBatis 缓存
**症状**: 数据库已更新,但查询返回旧数据
**原因**: MyBatis 一级缓存或二级缓存
**解决**: 清除缓存或禁用缓存
---
## 🔧 修复方案
### 方案 A: 立即重启服务(推荐)
```bash
cd ~/Desktop/projects/monisuo
pkill -f "monisuo-1.0.jar"
nohup java -jar target/monisuo-1.0.jar > logs/spring.log 2>&1 &
```
**验证**:
1. 执行一次充值审批
2. 查看日志是否有"账户更新结果: 1"
3. 查询数据库确认余额增加
---
### 方案 B: 添加强制清除缓存
修改 `FundService.approve()`:
```java
// 在方法开始添加
@Autowired
private SqlSession sqlSession;
@Transactional
public void approve(...) {
// 强制清除 MyBatis 缓存
sqlSession.clearCache();
// ... 原有逻辑
}
```
---
### 方案 C: 添加最终验证和重试
`FundService.approve()` 方法最后添加:
```java
// 在方法最后添加最终验证
AccountFund finalFund = accountFundMapper.selectById(fund.getId());
System.out.println("[最终验证] 数据库实际余额: " + finalFund.getBalance());
if (!finalFund.getBalance().equals(newBalance)) {
System.err.println("[严重错误] 余额更新失败!");
System.err.println(" - 期望余额: " + newBalance);
System.err.println(" - 实际余额: " + finalFund.getBalance());
// 尝试再次更新
int retryResult = accountFundMapper.update(null, wrapper);
System.out.println("[重试更新] 结果: " + retryResult);
if (retryResult <= 0) {
throw new RuntimeException("余额更新失败,事务将回滚");
}
}
```
---
### 方案 D: 检查事务配置
确认 `SpcCloudApplication.java` 是否有 `@EnableTransactionManagement`:
```java
@SpringBootApplication
@EnableTransactionManagement // ⭐ 必须有这个
public class SpcCloudApplication {
// ...
}
```
---
## 📊 验证脚本
使用我创建的验证脚本:
```bash
chmod +x ~/Desktop/projects/monisuo/verify_balance.sh
./verify_balance.sh
```
---
## 🎯 执行步骤
### 步骤1: 立即重启服务
```bash
cd ~/Desktop/projects/monisuo
pkill -f "monisuo-1.0.jar"
nohup java -jar target/monisuo-1.0.jar > logs/spring.log 2>&1 &
```
### 步骤2: 测试审批
1. 登录管理后台
2. 进入"订单管理" -> "待审批订单"
3. 选择一个充值订单
4. 点击"审批通过"
5. **不要关闭页面**
### 步骤3: 立即查看日志
```bash
tail -f logs/spring.log | grep -A20 "FundService.approve"
```
**应该看到**:
```
[FundService.approve] 步骤4: 处理充值通过...
- 审批前余额: 0.00
- 准备更新账户余额: 100.00
- 执行 SQL UPDATE (使用 LambdaUpdateWrapper)...
- 账户更新结果: 1 (1=成功, 0=失败) <-- 必须是 1
- 验证更新后余额: 100.00
[充值审批成功] 订单号: xxx, 充值金额: 100 USDT
```
### 步骤4: 数据库验证
```sql
-- 查询订单状态应该是3
SELECT order_no, status, approve_time FROM order_fund WHERE order_no = '订单号';
-- 查询账户余额(应该增加了)
SELECT user_id, balance, total_deposit FROM account_fund WHERE user_id = [ID];
-- 查询流水记录(应该有记录)
SELECT * FROM account_flow WHERE related_order_no = '订单号';
```
---
## ❌ 如果问题依然存在
提供以下信息:
1. **服务启动时间**
```bash
ps -p $(pgrep -f monisuo-1.0.jar) -o lstart
```
2. **完整审批日志**
```bash
grep -A50 "审批订单开始" logs/spring.log | tail -60
```
3. **数据库查询结果**
```sql
-- 订单信息
SELECT * FROM order_fund WHERE order_no = '订单号';
-- 账户信息
SELECT * FROM account_fund WHERE user_id = [ID];
-- 流水记录
SELECT * FROM account_flow WHERE related_order_no = '订单号';
```
4. **事务日志**
```bash
grep -i "transaction\|rollback\|commit" logs/spring.log | tail -20
```
---
**最后更新**: 2026-03-24 11:45
**状态**: 等待重启服务并测试
**预期修复时间**: 5分钟

View File

@@ -333,17 +333,32 @@ public class FundService {
if (status == 2) { if (status == 2) {
// 审批通过 // 审批通过
System.out.println("[FundService.approve] 步骤4: 处理审批通过逻辑..."); System.out.println("[FundService.approve] 步骤4: 处理审批通过逻辑...");
if (order.getType() == 1) { if (order.getType() == 1) {
// 充值通过:增加余额 - 使用 LambdaUpdateWrapper 显式更新 // 充值通过:增加余额 - 使用 LambdaUpdateWrapper 显式更新
System.out.println("[FundService.approve] 处理充值通过..."); System.out.println("[FundService.approve] 处理充值通过...");
BigDecimal balanceBefore = fund.getBalance();
BigDecimal totalDepositBefore = fund.getTotalDeposit(); // 处理可能的 null 值
BigDecimal currentBalance = fund.getBalance();
BigDecimal currentTotalDeposit = fund.getTotalDeposit();
if (currentBalance == null) {
currentBalance = BigDecimal.ZERO;
System.out.println(" - 警告: 账户余额为 null已设置为 0");
}
if (currentTotalDeposit == null) {
currentTotalDeposit = BigDecimal.ZERO;
System.out.println(" - 警告: 累计充值为 null已设置为 0");
}
BigDecimal balanceBefore = currentBalance;
BigDecimal totalDepositBefore = currentTotalDeposit;
System.out.println(" - 审批前余额: " + balanceBefore); System.out.println(" - 审批前余额: " + balanceBefore);
System.out.println(" - 审批前累计充值: " + totalDepositBefore); System.out.println(" - 审批前累计充值: " + totalDepositBefore);
System.out.println(" - 账户ID: " + fund.getId());
System.out.println(" - 用户ID: " + fund.getUserId());
BigDecimal newBalance = fund.getBalance().add(order.getAmount()); BigDecimal newBalance = currentBalance.add(order.getAmount());
BigDecimal newTotalDeposit = fund.getTotalDeposit().add(order.getAmount()); BigDecimal newTotalDeposit = currentTotalDeposit.add(order.getAmount());
LocalDateTime updateTime = LocalDateTime.now(); LocalDateTime updateTime = LocalDateTime.now();
System.out.println(" - 准备更新账户余额: " + newBalance); System.out.println(" - 准备更新账户余额: " + newBalance);
@@ -364,11 +379,19 @@ public class FundService {
throw new RuntimeException("充值审批更新账户余额失败"); throw new RuntimeException("充值审批更新账户余额失败");
} }
// 验证更新结果 // 验证更新结果 - 使用新的查询确保从数据库读取
AccountFund verifyFund = accountFundMapper.selectById(fund.getId()); AccountFund verifyFund = accountFundMapper.selectById(fund.getId());
System.out.println(" - 验证更新后余额: " + verifyFund.getBalance()); System.out.println(" - 验证更新后余额: " + verifyFund.getBalance());
System.out.println(" - 验证更新后累计充值: " + verifyFund.getTotalDeposit()); System.out.println(" - 验证更新后累计充值: " + verifyFund.getTotalDeposit());
if (verifyFund.getBalance() == null || !verifyFund.getBalance().equals(newBalance)) {
System.err.println("[FundService.approve] 严重错误: 账户余额更新验证失败!");
System.err.println(" - 期望余额: " + newBalance);
System.err.println(" - 实际余额: " + verifyFund.getBalance());
throw new RuntimeException("账户余额更新验证失败");
}
System.out.println(" - 余额验证通过 ✓");
// 更新本地对象状态 // 更新本地对象状态
fund.setBalance(newBalance); fund.setBalance(newBalance);
fund.setTotalDeposit(newTotalDeposit); fund.setTotalDeposit(newTotalDeposit);
@@ -559,6 +582,27 @@ public class FundService {
throw new RuntimeException("订单更新失败"); throw new RuntimeException("订单更新失败");
} }
// 最终验证:确保账户余额正确更新(仅在审批通过时)
if (status == 2) {
System.out.println("[FundService.approve] 步骤7: 最终验证账户余额...");
AccountFund finalVerifyFund = accountFundMapper.selectById(fund.getId());
System.out.println(" - 最终账户余额: " + finalVerifyFund.getBalance());
System.out.println(" - 最终累计充值: " + finalVerifyFund.getTotalDeposit());
if (order.getType() == 1) {
// 充值订单:验证余额是否正确增加
BigDecimal expectedBalance = fund.getBalance();
if (finalVerifyFund.getBalance() == null ||
finalVerifyFund.getBalance().compareTo(expectedBalance) != 0) {
System.err.println("[FundService.approve] 严重错误: 最终验证发现账户余额不一致!");
System.err.println(" - 期望余额: " + expectedBalance);
System.err.println(" - 实际余额: " + finalVerifyFund.getBalance());
throw new RuntimeException("账户余额最终验证失败,数据可能不一致");
}
System.out.println(" - 账户余额最终验证通过 ✓");
}
}
System.out.println("[审批完成] 订单号: " + orderNo + ", 订单类型: " + (order.getType() == 1 ? "充值" : "提现") + System.out.println("[审批完成] 订单号: " + orderNo + ", 订单类型: " + (order.getType() == 1 ? "充值" : "提现") +
", 审批结果: " + (status == 2 ? "通过" : "驳回") + ", 最终状态: " + finalStatus + ", 审批结果: " + (status == 2 ? "通过" : "驳回") + ", 最终状态: " + finalStatus +
", 审批人: " + adminName); ", 审批人: " + adminName);

187
verify_balance.sh Executable file
View File

@@ -0,0 +1,187 @@
#!/bin/bash
# 充值审批余额更新验证脚本
echo "================================"
echo "充值审批余额更新验证"
echo "================================"
echo ""
# 配置
BASE_URL="http://localhost:5010"
MYSQL_HOST="8.155.172.147"
MYSQL_USER="monisuo"
MYSQL_PASS="JPJ8wYicSGC8aRnk"
MYSQL_DB="monisuo"
# 1. 检查服务是否运行
echo "步骤1: 检查服务状态..."
if pgrep -f "monisuo-1.0.jar" > /dev/null; then
echo "✅ 服务正在运行"
echo " PID: $(pgrep -f 'monisuo-1.0.jar')"
else
echo "❌ 服务未运行!"
echo ""
echo "启动服务:"
echo "cd ~/Desktop/projects/monisuo"
echo "java -jar target/monisuo-1.0.jar --server.port=5010"
exit 1
fi
echo ""
# 2. 检查代码版本
echo "步骤2: 检查代码版本..."
cd ~/Desktop/projects/monisuo
LATEST_COMMIT=$(git log --oneline -1)
echo "最新提交: $LATEST_COMMIT"
if echo "$LATEST_COMMIT" | grep -q "06f546f"; then
echo "✅ 代码已是最新版本(包含余额更新修复)"
else
echo "⚠️ 代码可能不是最新版本"
echo " 建议执行: git pull origin main"
fi
echo ""
# 3. 检查日志文件
echo "步骤3: 检查最近审批日志..."
if [ -f "logs/spring.log" ]; then
echo "最近审批日志最后20行:"
echo "---"
grep -A20 "充值审批成功\|账户更新结果" logs/spring.log | tail -20
echo "---"
echo ""
# 检查是否有更新失败的日志
if grep -q "账户更新结果: 0" logs/spring.log; then
echo "❌ 发现账户更新失败的日志!"
echo " 查看详细日志: grep -A10 '账户更新结果: 0' logs/spring.log"
fi
# 检查是否有验证失败的日志
if grep -q "余额更新失败\|验证失败" logs/spring.log; then
echo "❌ 发现余额验证失败的日志!"
echo " 查看详细日志: grep -A10 '余额更新失败' logs/spring.log"
fi
else
echo "⚠️ 日志文件不存在: logs/spring.log"
fi
echo ""
# 4. 数据库验证
echo "步骤4: 数据库验证..."
echo "查询最近的充值订单..."
mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS $MYSQL_DB << EOF
-- 查询最近5笔已完成的充值订单
SELECT
id,
order_no,
user_id,
amount,
status,
approve_time,
update_time
FROM order_fund
WHERE type = 1 AND status = 3
ORDER BY approve_time DESC
LIMIT 5;
EOF
echo ""
# 5. 提示用户输入订单号进行验证
echo "================================"
echo "详细验证"
echo "================================"
read -p "请输入订单号进行验证(直接回车跳过): " ORDER_NO
if [ -n "$ORDER_NO" ]; then
echo ""
echo "查询订单信息..."
mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS $MYSQL_DB << EOF
SELECT
order_no,
user_id,
amount,
status,
approve_admin_id,
approve_time
FROM order_fund
WHERE order_no = '$ORDER_NO';
EOF
echo ""
# 获取用户ID
USER_ID=$(mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS $MYSQL_DB -se "SELECT user_id FROM order_fund WHERE order_no='$ORDER_NO'")
if [ -n "$USER_ID" ]; then
echo "查询用户资金账户user_id=$USER_ID..."
mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS $MYSQL_DB << EOF
SELECT
id,
user_id,
balance,
total_deposit,
update_time
FROM account_fund
WHERE user_id = $USER_ID;
EOF
echo ""
echo "查询资金流水记录..."
mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS $MYSQL_DB << EOF
SELECT
flow_no,
flow_type,
amount,
balance_before,
balance_after,
related_order_no,
create_time
FROM account_flow
WHERE related_order_no = '$ORDER_NO'
ORDER BY create_time DESC;
EOF
echo ""
# 计算预期余额
AMOUNT=$(mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS $MYSQL_DB -se "SELECT amount FROM order_fund WHERE order_no='$ORDER_NO'")
CURRENT_BALANCE=$(mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS $MYSQL_DB -se "SELECT balance FROM account_fund WHERE user_id=$USER_ID")
echo "================================"
echo "余额验证"
echo "================================"
echo "充值金额: $AMOUNT"
echo "当前余额: $CURRENT_BALANCE"
echo ""
# 获取充值前余额(从流水记录)
BALANCE_BEFORE=$(mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS $MYSQL_DB -se "SELECT balance_before FROM account_flow WHERE related_order_no='$ORDER_NO' ORDER BY create_time DESC LIMIT 1")
if [ -n "$BALANCE_BEFORE" ]; then
EXPECTED_BALANCE=$(echo "$BALANCE_BEFORE + $AMOUNT" | bc)
echo "充值前余额: $BALANCE_BEFORE"
echo "预期余额: $EXPECTED_BALANCE"
echo ""
if [ "$CURRENT_BALANCE" == "$EXPECTED_BALANCE" ]; then
echo "✅ 余额正确!"
else
echo "❌ 余额不匹配!"
echo " 预期: $EXPECTED_BALANCE"
echo " 实际: $CURRENT_BALANCE"
echo ""
echo "可能的原因:"
echo "1. 事务回滚了"
echo "2. 更新被其他操作覆盖"
echo "3. 代码未正确执行"
fi
else
echo "⚠️ 未找到流水记录"
fi
fi
fi
echo ""
echo "================================"
echo "诊断完成"
echo "================================"