2.7 KiB
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:
MpRefundController.applyRefundFeign 调orderFeignClient.applyRefund(userId, userName, orderId, body)不传这 3 个参数MpOrderFeignClient.applyRefund签名也不声明这 3 个参数InternalRefundController.apply三个@RequestParam(required=false)收到 null 后透传 serviceRefundApplyService.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% 镜像):
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 等运维部署后用户实测