From e658b0ef8d5f7400c39373271d9cb15a7dda7c07 Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Mon, 14 Sep 2026 18:42:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E8=87=AA=E5=8A=A8=E6=A0=B8=E9=94=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/order/HardAutoVerifyServiceImpl.java | 38 ++++++++++++++++++- .../order/HardAutoVerifyServiceImplTest.java | 9 +++-- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/order/HardAutoVerifyServiceImpl.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/order/HardAutoVerifyServiceImpl.java index e647737db..634346427 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/order/HardAutoVerifyServiceImpl.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/order/HardAutoVerifyServiceImpl.java @@ -34,6 +34,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; import java.time.LocalDateTime; import java.util.List; @@ -140,7 +142,7 @@ public class HardAutoVerifyServiceImpl implements HardAutoVerifyService { /** * 在任务状态机事务内执行一次自动核销。触发时间、配置开关和订单状态已由任务状态机在订单行锁内预检, - * 本方法只负责核销动作本身和可选退库存;任一远程或本地步骤失败都会随任务事务回滚并进入重试。 + * 本方法只负责核销动作本身;退库存登记为主事务提交后的独立动作,失败降级告警、不回滚已完成的核销。 * * @param task 已通过预检的自动核销任务 */ @@ -169,10 +171,42 @@ public class HardAutoVerifyServiceImpl implements HardAutoVerifyService { // 静默核销:订单行锁、履约校验、二维码消费与状态条件更新与人工核销完全一致,仅标记核销方式为自动。 hardOrderService.autoVerifyReserveOrder(task.getOrderId()); if (Objects.equals(task.getRestoreInventory(), YesNoEnum.YES.getCode())) { - restoreAutoVerifyInventory(task.getOrderId()); + // 退库存必须在主事务提交后独立执行:历史订单菜谱校验失败的异常会穿透 fastenerInventory 的事务代理, + // 把核销主事务标记 rollback-only,导致"核销成功却整体回滚";提交后执行失败只降级告警。 + registerRestoreInventoryAfterCommit(task.getOrderId()); } } + /** + * 注册核销主事务提交后的退库存动作;无事务同步上下文时(单测等场景)直接执行兜底。 + * + * @param orderId 已完成自动核销的订单 ID + */ + private void registerRestoreInventoryAfterCommit(Long orderId) { + // afterCommit 内的异常会外抛到提交调用方,必须整体兜底:退库存是尽力而为的修正动作,任何失败只告警。 + Runnable action = () -> { + try { + restoreAutoVerifyInventory(orderId); + } catch (Exception exception) { + log.warn("{}提交后自动核销退库存失败,已降级跳过,orderId:{},exceptionType:{}", TAG, orderId, + exception.getClass().getSimpleName()); + } + }; + if (!TransactionSynchronizationManager.isSynchronizationActive()) { + action.run(); + return; + } + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + /** + * 核销事务提交完成后按剩余有效数量回补线上池库存,内部自带降级捕获,不影响已提交的核销结果。 + */ + @Override + public void afterCommit() { + action.run(); + } + }); + } + /** * 管理端在自动核销候选列表逐单立即核销。核销仍走自动核销链路(核销方式标记为自动), * 允许提前关闭尚未到期订单;状态机为保留失败日志会吸收业务异常,因此执行后必须复查最终状态。 diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/order/HardAutoVerifyServiceImplTest.java b/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/order/HardAutoVerifyServiceImplTest.java index 82bd9ee0f..f84d3ca16 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/order/HardAutoVerifyServiceImplTest.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/order/HardAutoVerifyServiceImplTest.java @@ -131,9 +131,9 @@ class HardAutoVerifyServiceImplTest { verify(hardOrderService, never()).fastenerInventory(any(), anyList(), any(), any(), anyString()); } - /** 订单商品全部退款后无可恢复库存,核销事务应失败回滚并交由任务重试与人工排查。 */ + /** 订单商品全部退款后无可恢复库存,退库存降级告警即可,核销本身照常完成不阻断。 */ @Test - void completeAutoVerifyShouldFailWhenNoRestorableProduct() { + void completeAutoVerifyShouldDegradeWhenNoRestorableProduct() { HardAutoRefundTaskEntity task = task(YesNoEnum.YES.getCode(), LocalDateTime.now().minusMinutes(1)); when(hardOrderMapper.selectById(1L)).thenReturn(HardOrderEntity.builder().id(1L).build()); HardCarVo fullyRefunded = new HardCarVo(); @@ -142,9 +142,10 @@ class HardAutoVerifyServiceImplTest { fullyRefunded.setEffectiveProductCount(0); when(hardCarService.getByOrderId(1L)).thenReturn(List.of(fullyRefunded)); - RuntimeException exception = assertThrows(RuntimeException.class, () -> service.completeAutoVerify(task)); + service.completeAutoVerify(task); - assertEquals("自动核销订单没有可恢复库存的商品", exception.getMessage()); + verify(hardOrderService).autoVerifyReserveOrder(1L); + verify(hardOrderService, never()).fastenerInventory(any(), anyList(), any(), any(), anyString()); } /** 天数配置非法时重扫直接拒绝,不触碰任何订单和任务。 */