Merge remote-tracking branch 'origin/master'

This commit is contained in:
zjf committed 2026-09-14 20:31:33 +08:00
commit 20e36d6d86
2 files changed
+41 -6

No files matched your search

@@ -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();
}
});
}
/**
* 管理端在自动核销候选列表逐单立即核销。核销仍走自动核销链路(核销方式标记为自动),
* 允许提前关闭尚未到期订单;状态机为保留失败日志会吸收业务异常,因此执行后必须复查最终状态。
@@ -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());
}
/** 天数配置非法时重扫直接拒绝,不触碰任何订单和任务。 */