You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React数据库更新异常:JWT认证转账时用户余额未同步至数据库

转账余额未同步数据库的解决方法

问题根源分析

  • 关联账单未单独保存:你修改了user.bills[0].amount后仅调用user.save(),但user.bills是通过populate获取的关联Bill文档,修改关联文档的属性后,必须单独保存该Bill实例,否则数据库不会同步更新。
  • 异步回调错误捕获失效:jwt.verify的异步回调中使用async/await,外层try/catch无法捕获回调内的错误,导致部分异常无法被处理。
  • 缺少关键校验:未检查转账金额合法性、用户余额是否充足,存在逻辑漏洞。
  • 浮点精度隐患:parseFloat处理金额易引发精度误差,比如0.1 + 0.2的计算结果不符合预期。

修复后的代码

const SendMoney = async (req, res) => {
  try {
    const { token } = req.cookies;
    const { amount, billnumber } = req.body;

    // 改用Promise封装jwt.verify,确保try/catch能捕获所有错误
    const decodedToken = await new Promise((resolve, reject) => {
      jwt.verify(token, process.env.JWT_SECRET, {}, (err, decoded) => {
        err ? reject(err) : resolve(decoded);
      });
    });

    const user = await User.findById(decodedToken.id).populate("bills");
    const targetBill = await Bill.findOne({ billnumber });
    
    if (!targetBill) {
      return res.json({ error: "无效的账单号!" });
    }

    const transferAmount = Number(amount);
    // 校验金额是否合法
    if (isNaN(transferAmount) || transferAmount <= 0) {
      return res.json({ error: "金额必须是正数!" });
    }

    const userBill = user.bills[0];
    if (!userBill) {
      return res.json({ error: "用户无可用账单!" });
    }

    // 校验余额是否充足
    if (userBill.amount < transferAmount) {
      return res.json({ error: "余额不足!" });
    }

    // 执行余额变更
    targetBill.amount += transferAmount;
    userBill.amount -= transferAmount;

    // 并行保存两个账单的修改,提升效率
    await Promise.all([targetBill.save(), userBill.save()]);

    // 返回数据库中最新的用户数据
    return res.json({
      success: "转账成功!",
      user: await User.findById(decodedToken.id).populate("bills"),
    });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      error: "服务器错误!",
    });
  }
};

核心修复说明

  1. 保存关联账单:修改用户账单余额后,调用userBill.save()同步到数据库,配合Promise.all并行处理两个账单的保存操作。
  2. 统一错误捕获:用Promise封装jwt.verify,让所有异步逻辑都处于外层try/catch的覆盖范围内,避免遗漏错误。
  3. 完善校验逻辑:新增金额合法性、余额充足性检查,防止非法转账操作。
  4. 优化金额处理:用Number替代parseFloat减少精度问题,更严谨的方案是将金额以“分”为单位存储(整数类型),彻底避免浮点误差。
  5. 返回最新数据:重新查询用户并关联账单,确保响应内容是数据库的最新状态。

内容的提问来源于stack exchange,提问作者Bence Duba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 13:05:31