This commit is contained in:
2026-10-20 10:35:52 +08:00
parent ce189f062f
commit c13175bf77
202 changed files with 38397 additions and 7138 deletions
@@ -65,11 +65,11 @@ public interface InventoryCommandMapper {
"AND ((o.outbound_type='DISPATCH_DELIVERY' AND l.business_order_no=o.dispatch_no " +
"AND l.outbound_order_no=o.dispatch_no AND EXISTS (" +
"SELECT 1 FROM ll_dispatch_pda_task t JOIN ll_dispatch_pda_pick p " +
"ON p.customer_id=t.customer_id AND p.task_id=t.id " +
"ON p.task_id=t.id " +
"JOIN ll_inventory_operation op ON op.customer_id=p.customer_id " +
"AND op.operation_type='SHIP' AND op.status='COMPLETED' " +
"AND op.idempotency_key=CONCAT('DISPATCH:',o.dispatch_no,':',p.dispatch_item_id,':',p.idempotency_key,':SHIP') " +
"WHERE t.customer_id=o.customer_id AND t.dispatch_order_id=o.dispatch_order_id " +
"WHERE p.customer_id=o.customer_id AND t.dispatch_order_id=o.dispatch_order_id " +
"AND t.warehouse_id=o.overseas_warehouse_id AND p.inventory_position_id=l.position_id " +
"AND p.packages=-l.delta_packages AND l.idempotency_key=CONCAT(op.idempotency_key,':',op.id))) " +
"OR (o.outbound_type<>'DISPATCH_DELIVERY' AND l.outbound_order_no=o.outbound_no)) " +
@@ -0,0 +1,22 @@
package com.baiyue.lastleg.inventory.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/** 共同串行行只按全局真实实托主键建行和取锁,不承载业务占用状态。 */
@Mapper
public interface PhysicalPalletSerialMapper {
/** 首次使用建立空实托的串行行,重复使用保留首次创建时间。 */
@Insert("INSERT INTO ll_physical_pallet_guard(warehouse_pallet_id) VALUES(#{warehousePalletId}) "
+ "ON DUPLICATE KEY UPDATE warehouse_pallet_id=VALUES(warehouse_pallet_id)")
int ensure(@Param("warehousePalletId") long warehousePalletId);
/** 每次都取得实际主键行锁,不以一级或二级缓存替代数据库锁。 */
@Select("SELECT warehouse_pallet_id FROM ll_physical_pallet_guard "
+ "WHERE warehouse_pallet_id=#{warehousePalletId} FOR UPDATE")
@Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE)
Long lock(@Param("warehousePalletId") long warehousePalletId);
}
@@ -0,0 +1,9 @@
package com.baiyue.lastleg.inventory.port;
import java.util.List;
/** 全局真实实托组成变更的共同事务串行点,不承载业务占用或客户归属。 */
public interface PhysicalPalletSerialPort {
/** 加入调用方原事务,对真实实托去重升序建行并逐主键锁定;空目标不造身份。 */
void lock(List<Long> warehousePalletIds);
}
@@ -0,0 +1,26 @@
package com.baiyue.lastleg.inventory.repository;
import com.baiyue.lastleg.inventory.mapper.PhysicalPalletSerialMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
/** 真实实托共同串行行的持久化入口,所有 SQL 由 Mapper 执行。 */
@Repository
public class PhysicalPalletSerialRepository {
private final PhysicalPalletSerialMapper mapper;
/** 注入真实实托串行映射器。 */
public PhysicalPalletSerialRepository(PhysicalPalletSerialMapper mapper) {
this.mapper = mapper;
}
/** 调用方已完成去重升序;先按该顺序建齐行,再按同序逐主键锁定。 */
public void lockInOrder(List<Long> warehousePalletIds) {
// upsert 本身也会持行锁,因此建行和后续 FOR UPDATE 必须使用同一稳定顺序。
for (Long id : warehousePalletIds) mapper.ensure(id);
for (Long id : warehousePalletIds) {
if (!id.equals(mapper.lock(id))) throw new IllegalStateException("实托共同串行行不存在");
}
}
}
@@ -0,0 +1,36 @@
package com.baiyue.lastleg.inventory.service;
import com.baiyue.lastleg.inventory.port.PhysicalPalletSerialPort;
import com.baiyue.lastleg.inventory.repository.PhysicalPalletSerialRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.List;
import java.util.TreeSet;
/** 调用方原事务中的实托共同串行点,不生成实托身份或另建业务占用。 */
@Service
public class PhysicalPalletSerialService implements PhysicalPalletSerialPort {
private final PhysicalPalletSerialRepository repository;
/** 注入实托串行行的唯一持久化入口。 */
public PhysicalPalletSerialService(PhysicalPalletSerialRepository repository) {
this.repository = repository;
}
/** 校验完整目标集合后去重升序锁齐;没有真实实托时不插入任何替代行。 */
@Override
@Transactional(propagation = Propagation.MANDATORY)
public void lock(List<Long> warehousePalletIds) {
// 直接调用同样不能绕过事务要求,锁一直持有到外层业务提交或回滚。
if (!TransactionSynchronizationManager.isActualTransactionActive()
|| TransactionSynchronizationManager.isCurrentTransactionReadOnly())
throw new IllegalStateException("实托共同串行锁必须加入调用方现存写事务");
if (warehousePalletIds == null || warehousePalletIds.stream().anyMatch(id -> id == null || id <= 0))
throw new IllegalArgumentException("实托集合只接受合法真实实托ID");
if (warehousePalletIds.isEmpty()) return;
repository.lockInOrder(List.copyOf(new TreeSet<>(warehousePalletIds)));
}
}