Commit acda3c95de8dcddb447759e99edd670b9bda543f
1 parent
3d1b66d4
fix(wms): 优化库存批量锁定顺序
- 批量 FOR UPDATE 查询前复制并排序库存 id,保持查询输入顺序稳定 - 补充多条已存在库存变更单测,验证库存余额更新和流水顺序
Showing
2 changed files
with
44 additions
and
1 deletions
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", "红富士苹果"); | ... | ... |