llp пре 1 недеља
родитељ
комит
afb6255d11
1 измењених фајлова са 103 додато и 0 уклоњено
  1. 103 0
      packages/Longyi/FreeGift/src/Services/GiftProductResolver.php

+ 103 - 0
packages/Longyi/FreeGift/src/Services/GiftProductResolver.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace Longyi\FreeGift\Services;
+
+use Longyi\Core\Models\ProductVariant;
+use Longyi\Core\Repositories\ProductVariantRepository;
+use Webkul\Product\Repositories\ProductRepository;
+
+/**
+ * 把赠品 SKU 解析成「商品 + 变体」。
+ *
+ * `free_gift_rules.sku` 里存的可能是:
+ *  - simple / virtual 商品的 SKU                       → variant 为 null
+ *  - flexible_variant 父商品的 SKU(后台选择器给的就是这个)→ 取一个默认变体
+ *  - flexible_variant 某个变体的 SKU                    → 直接用该变体
+ *
+ * 单请求内按 SKU 缓存:一次 collectTotals 里 syncGiftItems / autoAddGiftItems
+ * 会反复问同一个 SKU。
+ */
+class GiftProductResolver
+{
+    /**
+     * @var array<string, array{product: object, variant: ?ProductVariant}|null>
+     */
+    protected array $cache = [];
+
+    public function __construct(
+        protected ProductRepository $productRepository,
+        protected ProductVariantRepository $variantRepository
+    ) {}
+
+    /**
+     * @return array{product: object, variant: ?ProductVariant}|null
+     */
+    public function resolve(string $sku): ?array
+    {
+        $sku = trim($sku);
+
+        if ($sku === '') {
+            return null;
+        }
+
+        if (! array_key_exists($sku, $this->cache)) {
+            $this->cache[$sku] = $this->doResolve($sku);
+        }
+
+        return $this->cache[$sku];
+    }
+
+    /**
+     * 解析失败(赠品被下架/删除、SKU 写错)返回 null,调用方按「本次送不出去」处理。
+     */
+    protected function doResolve(string $sku): ?array
+    {
+        // 1) 先当商品 SKU 找(simple / virtual / flexible_variant 的父商品)
+        $product = $this->productRepository->findOneWhere(['sku' => $sku]);
+
+        if ($product) {
+            if ($product->type === 'flexible_variant') {
+                $variant = $this->defaultVariant((int) $product->id);
+
+                return $variant ? ['product' => $product, 'variant' => $variant] : null;
+            }
+
+            return ['product' => $product, 'variant' => null];
+        }
+
+        // 2) 再当变体 SKU 找(flexible_variant 的行)
+        $variant = $this->variantRepository->findBySku($sku);
+
+        if (! $variant || ! $variant->product) {
+            return null;
+        }
+
+        return ['product' => $variant->product, 'variant' => $variant];
+    }
+
+    /**
+     * 配置的是父商品 SKU 时挑一个变体:优先「启用且有货」的第一条(按 sort_order),
+     * 全都无货时退回「启用」的第一条 —— 让库存校验去决定放不放行。
+     */
+    public function defaultVariant(int $productId): ?ProductVariant
+    {
+        return $this->variantRepository->getSaleableByProduct($productId)->first()
+            ?: $this->variantRepository->getByProduct($productId, true)->first();
+    }
+
+    /**
+     * 拿变体上的最终成交价(含特价/目录促销/客户组价)。
+     */
+    public function variantPrice(?ProductVariant $variant): float
+    {
+        if (! $variant) {
+            return 0;
+        }
+
+        try {
+            return (float) $variant->getEffectivePrice();
+        } catch (\Throwable) {
+            return (float) ($variant->special_price ?: $variant->price ?: 0);
+        }
+    }
+}