hl-api-changelog/changelogs/2026-05/07_fix_refund_apply_paidamount_npe.md

2.7 KiB

fix(refund): mp 端 apply 接口 paidAmount=null 必 NPE

PR: #1783 Issue: #1780 合并: 2026-05-07

通知对象: 无前端配合(后端纯内部 fallback 修复)


现象

正式环境 mp 端「申请退款」: 选退款类型 → 选退款原因 → 点「确认申请退款」 → 弹 toast 服务器内部错误 [NullPointerException]。重现率 100%。所有 mp 端用户提交退款申请永远失败。

根因

mp 端 BFF 调 apply 接口的 paidAmount/policyId/departureDate 全链路永远为 null:

  1. MpRefundController.applyRefund Feign 调 orderFeignClient.applyRefund(userId, userName, orderId, body) 不传这 3 个参数
  2. MpOrderFeignClient.applyRefund 签名也不声明这 3 个参数
  3. InternalRefundController.apply 三个 @RequestParam(required=false) 收到 null 后透传 service
  4. RefundApplyService.apply:151 第一句 paidAmount.compareTo(BigDecimal.ZERO) <= 0NPE

PR #1311 当年只补了同文件 previewRefund (line 86-93) 的 fallback,漏了 apply,本次是 #1311 follow-up。

后端修了什么

RefundApplyService.apply 顶部加 14 行 fallback (与 previewRefund 现有模式 100% 镜像):

if (paidAmount == null || policyId == null || departureDate == null) {
    OrderInfo fallbackOrder = orderInfoMapper.selectById(orderId);
    if (fallbackOrder != null) {
        if (paidAmount == null) paidAmount = fallbackOrder.getPaidAmount();
        if (policyId == null) policyId = fallbackOrder.getRefundPolicyId();
        if (departureDate == null) departureDate = fallbackOrder.getDepartureDate();
    }
}
if (paidAmount == null) {
    throw new BusinessException(OrderRefundErrorCode.REFUND_NO_AMOUNT_AVAILABLE);
}

按「金额绝不信前端」原则,三参数始终以订单主表为权威。IDOR 校验(line 156)完整保留在 fallback 之后。

前端无需改动

POST /mp/order/{orderId}/refund 接口签名/请求体/响应 VO 0 改动。前端 UI 不变。

部署依赖

无 nacos / SQL / 第三方配置变更。直接部署即生效。

受影响范围

  • mp 端「申请退款」(POST /mp/order/{orderId}/refund) — 修复后正常落库 + 触发审批
  • 不影响: 退款预览 / 退款详情 / 撤回 / 申诉 / 直接申诉 (这些路径未走 RefundApplyService.apply)

验证

  • 单测 29/29 全绿,新增 2 条覆盖 paidAmount=null 路径(订单存在 → fallback 成功 / 订单不存在 → BusinessException 而非 NPE)
  • CR 通过 (NPE 真消除 / IDOR 不退化 / 字段映射一致)
  • 测试服 dev 部署成功重启 (uptime 0:55, 跑 dev HEAD 含 PR #1783)
  • prod round-trip 等运维部署后用户实测