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

Spring开发ATM机出钞功能超库存支取钞票问题排查求助

问题根源

  • 核心逻辑完全没有校验当前面值可支取的张数上限:你仅通过CheckTakeNote()判断当前面值库存是否大于0,但没有考虑你计算出的需要支取的张数是否超过实际库存。比如案例中50元面值库存只有2张,你直接取了4张,自然会出现库存为负的情况。
  • 定义的TakeNotes()校验方法全程没有被调用:你绕过了自带的库存扣减校验逻辑,直接修改teller对象的面值数量后更新数据库,相当于主动跳过了库存校验步骤。
  • 贪心算法逻辑错误:要实现最少张数出钞,每个面值最多可取的张数应该是 min(剩余金额/当前面值, 当前面值剩余库存),你直接取了剩余金额/当前面值的数值,完全没有代入库存限制。
  • 存在死循环风险:如果剩余金额无法被当前所有面值组合凑出,你的while(amount>0)会无限循环执行。

修复方案

1. 调整出钞核心逻辑

你需要在计算每个面值的支取张数时,优先和当前库存取最小值,再调用TakeNotes()方法确认扣减成功后再更新数据库,扣减失败则调整剩余金额继续试下一个面值。

2. 修复后的核心代码示例

public String dispense(@RequestBody Teller teller, @PathVariable Integer amount) {
    // 建议金额用整数处理,避免浮点精度问题
    StringBuilder notes = new StringBuilder();
    ArrayList<INoteBucket> noteBuckets = new ArrayList<>();
    noteBuckets.add(new NoteBucket(50, teller.getFifty()));
    noteBuckets.add(new NoteBucket(20, teller.getTwenty()));
    noteBuckets.add(new NoteBucket(10, teller.getTen()));
    noteBuckets.add(new NoteBucket(5, teller.getFive()));

    int remainingAmount = amount;
    for (INoteBucket noteBucket : noteBuckets) {
        if (remainingAmount <= 0) break;
        int denom = noteBucket.GetDemon();
        // 计算当前面值最多能取多少张:取金额可兑张数和库存的最小值
        int maxPossible = remainingAmount / denom;
        if (maxPossible == 0) continue;
        // 调用TakeNotes校验并扣减库存
        if (noteBucket.TakeNotes(maxPossible)) {
            // 扣减成功
            notes.append("面值").append(denom).append("元:").append(maxPossible).append("张\n");
            remainingAmount -= maxPossible * denom;
            // 更新对应面值的库存
            if (denom == 50) teller.setFifty(teller.getFifty() - maxPossible);
            else if (denom == 20) teller.setTwenty(teller.getTwenty() - maxPossible);
            else if (denom == 10) teller.setTen(teller.getTen() - maxPossible);
            else if (denom == 5) teller.setFive(teller.getFive() - maxPossible);
        } else {
            // 库存不足,取所有可用的当前面值
            // 注意需要给NoteBucket类新增getAmountOfNotes方法获取当前库存
            int available = ((NoteBucket) noteBucket).getAmountOfNotes();
            if (available > 0) {
                noteBucket.TakeNotes(available);
                notes.append("面值").append(denom).append("元:").append(available).append("张\n");
                remainingAmount -= available * denom;
                if (denom == 50) teller.setFifty(0);
                else if (denom == 20) teller.setTwenty(0);
                else if (denom == 10) teller.setTen(0);
                else if (denom == 5) teller.setFive(0);
            }
        }
    }
    // 最后判断是否凑齐金额
    if (remainingAmount > 0) {
        return "当前ATM库存不足,无法支取指定金额";
    }
    // 统一更新一次teller即可,不需要每次循环更新
    tellerService.updateTeller(teller);
    return notes.toString();
}

其他优化建议

  • 金额不要用Double类型,避免浮点运算精度误差,建议统一用分为单位的整数,或者直接用整数表示元。
  • 不需要在每次修改面值后都调用updateTeller,所有计算完成后统一更新一次即可,减少数据库IO。
  • 可以给NoteBucket类加一个获取当前库存的getAmountOfNotes()方法,避免硬编码判断面值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 10:09:00