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
相关产品推荐
相关产品推荐

