Quellcode durchsuchen

fix: 消除 t_device_inventory 重复记录隐患

应用层:
- completeOrder 查重键统一为 selectByDeviceAndProduct(deviceId, productId)
  与其他两条入库路径保持一致,避免 productCode 不一致导致重复 INSERT

数据库层:
- 新增去重 + 唯一约束迁移 SQL: docs/database/dedup_device_inventory_unique.sql
- 保留每组 (device_id, product_id) 最新记录,删除其余
- 添加 UNIQUE KEY uk_device_product (device_id, product_id)

Co-Authored-By: Claude <noreply@anthropic.com>
skyline vor 2 Tagen
Ursprung
Commit
ebe55631e4

+ 30 - 0
docs/database/dedup_device_inventory_unique.sql

@@ -0,0 +1,30 @@
+-- ============================================================================
+-- t_device_inventory 去重 + 添加唯一约束
+--
+-- 背景:同一设备同一商品存在多条记录,原因是:
+--   1. ReplenishmentOrderServiceImpl.completeOrder 用 productCode 查重,
+--      与其他路径(用 productId)不一致
+--   2. 数据库层缺少 (device_id, product_id) 唯一约束,并发场景下查重失效
+--
+-- 执行前建议:先 SELECT 确认重复数据量
+--   SELECT device_id, product_id, COUNT(*) cnt FROM t_device_inventory
+--   WHERE product_id IS NOT NULL GROUP BY device_id, product_id HAVING cnt > 1;
+-- ============================================================================
+
+-- Step 1: 清理重复记录
+-- 策略:每组 (device_id, product_id) 保留 id 最大的那条(最新插入的),删除其余
+DELETE FROM t_device_inventory
+WHERE id NOT IN (
+    SELECT max_id FROM (
+        SELECT MAX(id) AS max_id
+        FROM t_device_inventory
+        WHERE product_id IS NOT NULL
+        GROUP BY device_id, product_id
+    ) AS kept
+)
+AND product_id IS NOT NULL;
+
+-- Step 2: 添加唯一约束,从根本上杜绝重复
+-- 如果该约束已存在会报错 Duplicate key name,这是预期行为
+ALTER TABLE t_device_inventory
+ADD UNIQUE KEY uk_device_product (device_id, product_id);

+ 3 - 5
haha-service/src/main/java/com/haha/service/impl/ReplenishmentOrderServiceImpl.java

@@ -459,11 +459,9 @@ public class ReplenishmentOrderServiceImpl extends ServiceImpl<ReplenishmentOrde
         for (ReplenishmentOrderItem item : items) {
             if (item.getPlannedQuantity() == null || item.getPlannedQuantity() <= 0) continue;
 
-            // 查找或创建库存记录
-            LambdaQueryWrapper<DeviceInventory> wrapper = new LambdaQueryWrapper<DeviceInventory>()
-                    .eq(DeviceInventory::getDeviceId, order.getDeviceId())
-                    .eq(DeviceInventory::getProductCode, item.getProductCode());
-            DeviceInventory inv = deviceInventoryMapper.selectOne(wrapper);
+            // 查找或创建库存记录(用 productId 匹配,与其他路径保持一致)
+            DeviceInventory inv = deviceInventoryMapper.selectByDeviceAndProduct(
+                    order.getDeviceId(), item.getProductId());
             if (inv == null) {
                 inv = new DeviceInventory();
                 inv.setDeviceId(order.getDeviceId());