fix(firstleg): avoid shared lock upgrades when initializing customs

This commit is contained in:
2026-10-05 17:03:01 +08:00
parent 261c720c77
commit 4bde1a8352
3 changed files with 13 additions and 4 deletions
@@ -88,9 +88,10 @@ public interface ContainerCustomsMapper {
Long completedPlanForOrder(@Param("tenantId") long tenantId, @Param("orderId") long orderId);
@Insert("""
INSERT IGNORE INTO fl_customs_container
INSERT INTO fl_customs_container
(id,loading_plan_id,container_booking_id,status,created_at,updated_at,version)
VALUES (#{id},#{planId},#{containerBookingId},#{status},CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0)
ON DUPLICATE KEY UPDATE id=id
""")
int insertContainer(@Param("id") long id,@Param("planId") long planId,
@Param("containerBookingId") long containerBookingId,@Param("status") String status);
@@ -563,9 +563,10 @@ public class ContainerCustomsService {
if (!"COMPLETED".equals(plan.loadingPlanStatus())) throw conflict("装柜完成后才能处理报关资料");
boolean refund = rows.stream().anyMatch(row -> "TAX_REFUND".equals(row.taxMode()));
mapper.insertContainer(IdWorker.getId(), planId, plan.containerBookingId(), refund ? WAITING : READY);
// A concurrent INSERT IGNORE may wait for another request's insert to commit. The
// transaction's earlier plan reads can retain an RR snapshot that cannot see that row.
// Read the winning aggregate under its row lock, rather than reusing a snapshot read.
// The no-op upsert already takes an exclusive lock on a duplicate, avoiding the
// shared-to-exclusive lock upgrade of INSERT IGNORE followed by FOR UPDATE.
// Earlier plan reads may retain an RR snapshot predating the winning insert, so
// retrieve the real aggregate with a current read, not a snapshot read.
ContainerRow container = mapper.lockContainer(planId);
if (container == null) throw conflict("整柜报关记录初始化失败");
return refresh(planId, container);