From 90ab128348cd86abc1ada3f2c72989eb71202a5a Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Thu, 7 May 2026 11:45:52 +0800 Subject: [PATCH] =?UTF-8?q?fix(refund):=20mp=20=E7=AB=AF=20apply=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=20paidAmount=3Dnull=20=E5=BF=85=20NPE=20(#17?= =?UTF-8?q?83,=20#1780)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../07_fix_refund_apply_paidamount_npe.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 changelogs/2026-05/07_fix_refund_apply_paidamount_npe.md diff --git a/changelogs/2026-05/07_fix_refund_apply_paidamount_npe.md b/changelogs/2026-05/07_fix_refund_apply_paidamount_npe.md new file mode 100644 index 0000000..4a772f2 --- /dev/null +++ b/changelogs/2026-05/07_fix_refund_apply_paidamount_npe.md @@ -0,0 +1,62 @@ +# fix(refund): mp 端 apply 接口 paidAmount=null 必 NPE + +**PR**: [#1783](https://git.1814.love:8443/wx/HL/pulls/1783) **Issue**: [#1780](https://git.1814.love:8443/wx/HL/issues/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) <= 0` → **NPE** + +PR #1311 当年只补了同文件 `previewRefund` (line 86-93) 的 fallback,漏了 `apply`,本次是 #1311 follow-up。 + +## 后端修了什么 + +`RefundApplyService.apply` 顶部加 14 行 fallback (与 `previewRefund` 现有模式 100% 镜像): + +```java +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 等运维部署后用户实测