diff --git a/APPROVAL_BALANCE_FIX_ACTION.md b/APPROVAL_BALANCE_FIX_ACTION.md new file mode 100644 index 0000000..27d4cb7 --- /dev/null +++ b/APPROVAL_BALANCE_FIX_ACTION.md @@ -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分钟 diff --git a/DIAGNOSTIC_APPROVAL_FLOW.md b/DIAGNOSTIC_APPROVAL_FLOW.md new file mode 100644 index 0000000..16d4e6d --- /dev/null +++ b/DIAGNOSTIC_APPROVAL_FLOW.md @@ -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. 根据结果调整修复方案 diff --git a/src/main/java/com/it/rattan/monisuo/service/FundService.java b/src/main/java/com/it/rattan/monisuo/service/FundService.java index a3277ef..dd64077 100644 --- a/src/main/java/com/it/rattan/monisuo/service/FundService.java +++ b/src/main/java/com/it/rattan/monisuo/service/FundService.java @@ -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 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 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 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 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 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() +