Skip to content

fix(core): 修复入参金额元转分被向零截断导致少 1 分 - #151

Open
rwzdk wants to merge 1 commit into
jeequan:devfrom
rwzdk:fix/core-amount-l-round-half-up
Open

rwzdk wants to merge 1 commit into
jeequan:devfrom
rwzdk:fix/core-amount-l-round-half-up

Conversation

@rwzdk

@rwzdk rwzdk commented Oct 27, 2026

Copy link
Copy Markdown

问题与验收标准

  • 关联 Issue:无(源码静态审查发现,已确认 dev 分支当前仍存在)。

  • 现象 / 预期:AbstractCtrl 里三个"元转分"的入参换算方法把不足 1 分的部分静默截断了:

    Long amountL = new BigDecimal(amountStr.trim()).multiply(new BigDecimal(100)).longValue(); // // 转成分

    BigDecimal#longValue() 是向零截断(truncate),不是四舍五入。前端只要传入超过两位小数的金额,就会直接掉分:

    入参(元) 当前结果(分) 期望(分)
    1.005 100 101
    0.09999 9 10
    -1.005 -100 -101

    并且截断方向是"向零",正数少收、负数少扣,退款 / 转账这类场景下误差方向还不一致。

  • 受影响入口:这三个方法是 getRequiredAmountL / getAmountL / handleParamAmount,仓库内的调用点(grep 结果)为:

    • jeepay-merchant/src/main/java/com/jeequan/jeepay/mch/ctrl/order/PayOrderController.java(商户端订单金额入参)
    • jeepay-merchant/src/main/java/com/jeequan/jeepay/mch/ctrl/transfer/MchTransferController.java(商户提现 / 转账金额)
    • jeepay-merchant/src/main/java/com/jeequan/jeepay/mch/ctrl/paytest/PaytestController.java(支付测试金额)
    • jeepay-manager/src/main/java/com/jeequan/jeepay/mgr/ctrl/order/PayOrderController.java(运营端订单)
    • jeepay-payment/src/main/java/com/jeequan/jeepay/pay/ctrl/qr/QrCashierController.java(收银台金额)

    金额最终单位是"分"(整型),所以这里少 1 分就是实收实付少 1 分。

修改内容

jeepay-core/src/main/java/com/jeequan/jeepay/core/ctrls/AbstractCtrl.java 的三处换算统一加上显式舍入。取整到"分"在本仓库里已有约定就是 ROUND_HALF_UP(见 AmountUtil.java:134 的 calPercentageFee(amount, rate, BigDecimal.ROUND_HALF_UP) 与 AmountUtil.java:149 的 setScale(0, mode);另外 convertCent2Dollar 用的是 setScale(2, BigDecimal.ROUND_HALF_UP)),因此这里按同一规则补齐:

Long amountL = new BigDecimal(amountStr.trim()).multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).longValue(); // 转成分(四舍五入)

行为兼容性:两位小数及以内的常规入参(也就是前端正常输入的格式)结果完全不变,本次只影响超过两位小数的入参——原实现会静默丢掉这部分,现在按四舍五入计入。没有改动方法签名、返回类型或任何配置项。

验证

新增 jeepay-core/src/test/java/com/jeequan/jeepay/core/ctrls/AbstractCtrlAmountTest.java,用一个只覆写 getReqParamJSON() 的测试子类驱动三个方法,不依赖 Servlet 上下文、数据库或网络。

改动前(dev @ 最新提交):

mvn -pl jeepay-core -Dtest=AbstractCtrlAmountTest test
[ERROR] Tests run: 3, Failures: 3, Errors: 0, Skipped: 0
[ERROR]   AbstractCtrlAmountTest.requiredAmountShouldRoundHalfUp:54 expected: <101> but was: <100>
[ERROR]   AbstractCtrlAmountTest.optionalAmountShouldRoundHalfUp:66 expected: <101> but was: <100>
[ERROR]   AbstractCtrlAmountTest.handleParamAmountShouldRoundHalfUp:76 expected: <101> but was: <100>

改动后,同一个类:

mvn -pl jeepay-core -Dtest=AbstractCtrlAmountTest test
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

整个 jeepay-core 模块(含模块内既有测试,无回归):

mvn -pl jeepay-core test
[INFO] Tests run: 13, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

Prepared with an AI coding agent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant