Commit acda3c95de8dcddb447759e99edd670b9bda543f

Authored by YunaiV
1 parent 3d1b66d4

fix(wms): 优化库存批量锁定顺序

- 批量 FOR UPDATE 查询前复制并排序库存 id,保持查询输入顺序稳定
- 补充多条已存在库存变更单测,验证库存余额更新和流水顺序
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java
... ... @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.wms.dal.dataobject.md.item.WmsItemSkuDO;
14 14 import org.apache.ibatis.annotations.Mapper;
15 15  
16 16 import java.math.BigDecimal;
  17 +import java.util.ArrayList;
17 18 import java.util.Collection;
18 19 import java.util.Collections;
19 20 import java.util.List;
... ... @@ -99,8 +100,10 @@ public interface WmsInventoryMapper extends BaseMapperX<WmsInventoryDO> {
99 100 if (CollUtil.isEmpty(ids)) {
100 101 return Collections.emptyList();
101 102 }
  103 + List<Long> sortedIds = new ArrayList<>(ids);
  104 + Collections.sort(sortedIds);
102 105 return selectList(new LambdaQueryWrapperX<WmsInventoryDO>()
103   - .in(WmsInventoryDO::getId, ids)
  106 + .in(WmsInventoryDO::getId, sortedIds)
104 107 .orderByAsc(WmsInventoryDO::getId)
105 108 .last("FOR UPDATE"));
106 109 }
... ...
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java
... ... @@ -241,6 +241,46 @@ public class WmsInventoryServiceImplTest extends BaseDbUnitTest {
241 241 }
242 242  
243 243 @Test
  244 + public void testChangeInventory_multipleExistingInventoryKeys() {
  245 + // mock 数据
  246 + WmsItemDO item = createItem("ITEM-001", "红富士苹果");
  247 + WmsItemSkuDO sku = createSku(item.getId(), "SKU-001", "10kg 箱装");
  248 + inventoryMapper.insert(createInventory(sku.getId(), 200L, "7.00"));
  249 + inventoryMapper.insert(createInventory(sku.getId(), 100L, "5.00"));
  250 + WmsInventoryChangeReqDTO reqDTO = createChangeReq(sku.getId(), 100L, "2.00");
  251 + List<WmsInventoryChangeReqDTO.Item> items = new ArrayList<>(reqDTO.getItems());
  252 + items.add(new WmsInventoryChangeReqDTO.Item()
  253 + .setSkuId(sku.getId())
  254 + .setWarehouseId(200L)
  255 + .setQuantity(new BigDecimal("3.00"))
  256 + .setPrice(new BigDecimal("100.00"))
  257 + .setTotalPrice(new BigDecimal("300.00"))
  258 + .setRemark("测试入库 2"));
  259 + reqDTO.setItems(items);
  260 +
  261 + // 调用
  262 + inventoryService.changeInventory(reqDTO);
  263 +
  264 + // 断言:多条已存在库存余额分别更新
  265 + WmsInventoryDO inventory1 = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 100L);
  266 + assertNotNull(inventory1);
  267 + assertEquals(0, new BigDecimal("7.00").compareTo(inventory1.getQuantity()));
  268 + WmsInventoryDO inventory2 = inventoryMapper.selectBySkuIdAndWarehouseId(sku.getId(), 200L);
  269 + assertNotNull(inventory2);
  270 + assertEquals(0, new BigDecimal("10.00").compareTo(inventory2.getQuantity()));
  271 + // 断言:库存流水保持入参明细顺序
  272 + List<WmsInventoryHistoryDO> histories = inventoryHistoryMapper.selectList();
  273 + histories.sort(Comparator.comparing(WmsInventoryHistoryDO::getId));
  274 + assertEquals(2, histories.size());
  275 + assertEquals(100L, histories.get(0).getWarehouseId());
  276 + assertEquals(0, new BigDecimal("5.00").compareTo(histories.get(0).getBeforeQuantity()));
  277 + assertEquals(0, new BigDecimal("7.00").compareTo(histories.get(0).getAfterQuantity()));
  278 + assertEquals(200L, histories.get(1).getWarehouseId());
  279 + assertEquals(0, new BigDecimal("7.00").compareTo(histories.get(1).getBeforeQuantity()));
  280 + assertEquals(0, new BigDecimal("10.00").compareTo(histories.get(1).getAfterQuantity()));
  281 + }
  282 +
  283 + @Test
244 284 public void testChangeInventory_concurrentCreateSameInventoryOnlyOneBalance() throws Exception {
245 285 // mock 数据
246 286 WmsItemDO item = createItem("ITEM-001", "红富士苹果");
... ...