优化
This commit is contained in:
+2
-2
@@ -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)) " +
|
||||
|
||||
+22
@@ -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);
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.baiyue.lastleg.inventory.port;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** 全局真实实托组成变更的共同事务串行点,不承载业务占用或客户归属。 */
|
||||
public interface PhysicalPalletSerialPort {
|
||||
/** 加入调用方原事务,对真实实托去重升序建行并逐主键锁定;空目标不造身份。 */
|
||||
void lock(List<Long> warehousePalletIds);
|
||||
}
|
||||
+26
@@ -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("实托共同串行行不存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -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)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user