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

Discord机器人MongoDB经济系统存款数值拼接而非算术相加问题

Discord 机器人经济系统存款逻辑异常排查

问题现象

具备JavaScript开发经验,开发基于MongoDB存储的Discord机器人经济系统时,存款指令出现异常:预期逻辑为从用户钱包扣除指定存款金额,对应金额累加至用户银行账户;实际运行时钱包扣减逻辑完全正常,但银行账户未执行算术加法,而是将存款数字直接拼接在原有余额末尾,例如原有银行余额为7、存入5后,余额变为75而非预期的12。程序无任何报错,尝试使用+=运算符实现累加后问题依旧。

现有实现代码

存款指令逻辑

const balanceProfile = await client.createBalance(message.member);
const amount = args[0];
if(isNaN(args[0])) return message.reply(`Specify an amount to deposit.`);
if(amount > balanceProfile.wallet) return message.reply(`Too much money to deposit.`);

try {
    await Balance.findOneAndUpdate({
        _id: balanceProfile.id, 
        wallet: balanceProfile.wallet - amount, 
        bank: balanceProfile.bank + amount
    });
} catch (error) {
    console.log(error);
}
message.reply(`Successfully deposited ${amount} moneyz to da bank!`)

MongoDB Balance Schema 定义

const mongoose = require('mongoose');
const balanceSchema = new mongoose.Schema({
    _id: mongoose.Schema.Types.ObjectId,
    memberId: String,
    wallet: { type: Number, default: 0 },
    bank: { type: Number, default: 0 },
    daily: { type: Number }
});

module.exports = mongoose.model('Balance', balanceSchema, 'balances');

根因分析

问题由两个独立bug共同导致:

  • 参数未做显式类型转换,加法触发字符串拼接
    从Discord消息解析得到的args数组元素默认是字符串类型,isNaN(args[0])仅能校验值是否可转换为数字,不会修改原始值的类型。
    JavaScript运算符隐式转换规则是核心诱因:
    • 减法运算符-会强制将两侧操作数转为数字后再做算术运算,因此钱包扣减逻辑表现正常
    • 加法运算符+只要任意一侧操作数为字符串,就会优先执行字符串拼接逻辑,因此balanceProfile.bank + amount实际执行的是拼接操作:例如银行余额为数字7、amount为字符串"5"时,运算结果为字符串"75",写入MongoDB时被Mongoose按Schema规则转为数字75,和故障表现完全一致。这也是替换为+=运算符无效的原因——只要amount仍是字符串类型,加法永远走拼接逻辑。
  • findOneAndUpdate参数结构错误
    findOneAndUpdate的标准语法为findOneAndUpdate(查询条件, 更新规则, 配置项),当前代码把wallet、bank的更新值全部写在了查询条件位置,相当于要求匹配「钱包余额等于扣减后值、银行余额等于拼接后值」的文档才会执行更新,并发场景下大概率匹配不到目标文档,会出现偶发更新失效的问题。

修复方案

  1. 对输入金额做显式数字类型转换,从根源避免字符串拼接问题
  2. 修正findOneAndUpdate参数结构,使用MongoDB原子更新符$inc做余额增减,规避并发场景下的数据不一致问题

修复后的核心代码如下:

const balanceProfile = await client.createBalance(message.member);
// 显式将输入参数转为数字,整数场景可替换为parseInt(args[0], 10),需支持小数用parseFloat
const amount = Number(args[0]);
// 补充金额合法性校验
if(isNaN(amount) || amount <= 0) return message.reply(`Specify a valid positive amount to deposit.`);
if(amount > balanceProfile.wallet) return message.reply(`Too much money to deposit.`);

try {
    // 第一个参数传查询条件,第二个参数用$inc做原子增减:钱包减amount,银行加amount
    await Balance.findOneAndUpdate(
        { _id: balanceProfile.id },
        {
            $inc: {
                wallet: -amount,
                bank: amount
            }
        }
    );
} catch (error) {
    console.log(error);
    return message.reply(`Deposit failed, please try again later.`);
}
message.reply(`Successfully deposited ${amount} moneyz to da bank!`)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 00:21:14