llp 1 неделя назад
Родитель
Сommit
21a8c36ab0
1 измененных файлов с 29 добавлено и 2 удалено
  1. 29 2
      packages/Longyi/FreeGift/src/Services/GiftProcessor.php

+ 29 - 2
packages/Longyi/FreeGift/src/Services/GiftProcessor.php

@@ -185,7 +185,15 @@ class GiftProcessor
                 continue;
             }
 
-            $gift->setCartItemId((int) $cartItem->id);
+            /*
+             * 行才是真相:认领到的行必须立刻同步 reservedQty。
+             *
+             * syncReservedQty() 在本轮更早的位置已经跑过,那时这些候选可能还没
+             * 注册进来(规则刚改 SKU / 状态行缺失 / 上一轮 persist 没落库),
+             * reservedQty 还是 0。不同步的话 B2 会认为「还差 1 个」,又拿这个
+             * cartItemId 去累加,最终出现「后台设 1 件、购物车 2 件」。
+             */
+            $gift->setCartItemId((int) $cartItem->id)->setReservedQty((float) $cartItem->quantity);
 
             // 规则已不再成立 → 把赠品从购物车里摘掉
             if ($gift->getAllowedQty() < 1) {
@@ -391,7 +399,12 @@ class GiftProcessor
 
         $gift->setProductId((int) $product->id)
             ->setVariantId($variant?->id)
-            ->setMinimalPrice($giftRule?->minimal_items_price);
+            ->setMinimalPrice($giftRule?->minimal_items_price)
+            /*
+             * 带上名额:writeGiftCartItem() 的「单行数量不超过允许量」防线
+             * 依赖它(手动领取是另一个入口,同样不能超过名额)。
+             */
+            ->setAllowedQty((float) $row->allowed_qty);
 
         $existingId = $this->findGiftCartItem($cart, $gift)?->id ?: $row->cart_item_id;
 
@@ -434,6 +447,20 @@ class GiftProcessor
             // 指针悬空(赠品行被删过 / 购物车被重建)→ 退回新建
             if ($existing && (int) $existing->cart_id === (int) $cart->id) {
                 $qty = (float) $existing->quantity + $qty;
+
+                /*
+                 * 封顶:一行赠品的数量不允许超过该候选的允许量。
+                 *
+                 * 自动加购和手动领取是两个入口,若某一轮认领失败 / 两次请求并发
+                 * 拿到了过期的 reserved_qty,就会在同一行上累加出「名额 1 却 2 件」。
+                 * 这里按 allowed_qty 截断,作为最后一道防线(allowed < 1 时跳过,
+                 * 例如手动领取场景候选没带名额)。
+                 */
+                $allowed = (float) $gift->getAllowedQty();
+
+                if ($allowed >= 1) {
+                    $qty = min($qty, $allowed);
+                }
             } else {
                 $cartItemId = null;
             }