Преглед изворни кода

Merge branch 'dev-rewardPoints' into dev

bianjunhui пре 1 дан
родитељ
комит
4266a47e34

+ 78 - 55
packages/Webkul/Shop/src/Http/Controllers/API/HomeController.php

@@ -3,6 +3,7 @@
 namespace Webkul\Shop\Http\Controllers\API;
 
 use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Cache;
 use Webkul\Category\Repositories\CategoryRepository;
 use Webkul\Core\Repositories\ChannelRepository;
 use Webkul\Product\Repositories\ProductRepository;
@@ -10,6 +11,11 @@ use Webkul\Theme\Repositories\ThemeCustomizationRepository;
 
 class HomeController extends APIController
 {
+    /**
+     * 缓存 TTL(秒)
+     */
+    const CACHE_TTL = 300;
+
     /**
      * Create a new controller instance.
      */
@@ -37,35 +43,43 @@ class HomeController extends APIController
         $channel = $this->channelRepository->findOneByField('code', $channelCode)
             ?: core()->getCurrentChannel();
 
-        // 1. 获取主题自定义数据(banner、分类轮播、产品轮播等)
-        $customizations = $this->themeCustomizationRepository
-            ->orderBy('sort_order')
-            ->findWhere([
-                'status'     => 1,
-                'channel_id' => $channel->id,
-                'theme_code' => $channel->theme,
-            ]);
-
-        $sections = [];
-        foreach ($customizations as $item) {
-            $section = $this->formatSection($item, $locale);
-            if ($section !== null) {
-                $sections[] = $section;
+        $cacheKey = 'home_api:' . $channel->code . ':' . $locale;
+
+        $data = Cache::remember($cacheKey, self::CACHE_TTL, function () use ($channel, $locale) {
+            // 1. 获取主题自定义数据(banner、分类轮播、产品轮播等)
+            $customizations = $this->themeCustomizationRepository
+                ->orderBy('sort_order')
+                ->findWhere([
+                    'status'     => 1,
+                    'channel_id' => $channel->id,
+                    'theme_code' => $channel->theme,
+                ]);
+
+            $sections = [];
+            foreach ($customizations as $item) {
+                $section = $this->formatSection($item, $locale);
+                if ($section !== null) {
+                    $sections[] = $section;
+                }
             }
-        }
 
-        // 2. 获取分类树(供导航使用)
-        $categories = $this->categoryRepository->getVisibleCategoryTree(
-            $channel->root_category_id
-        );
+            // 2. 获取分类树
+            $categories = Cache::remember(
+                'home_categories:' . $channel->code . ':' . $locale,
+                self::CACHE_TTL,
+                fn () => $this->categoryRepository->getVisibleCategoryTree($channel->root_category_id)
+            );
 
-        return response()->json([
-            'success' => true,
-            'data'    => [
+            return [
                 'channel'    => $channel->code,
                 'sections'   => $sections,
                 'categories' => $this->formatCategoryTree($categories),
-            ],
+            ];
+        });
+
+        return response()->json([
+            'success' => true,
+            'data'    => $data,
         ]);
     }
 
@@ -122,41 +136,50 @@ class HomeController extends APIController
      */
     protected function getProductCarouselData(array $filters): array
     {
-        $params = [
-            'status'              => 1,
-            'visible_individually'=> 1,
-            'limit'               => $filters['limit'] ?? 12,
-            'sort'                => $filters['sort'] ?? 'name-asc',
-        ];
-
-        if (! empty($filters['new'])) {
-            $params['new'] = 1;
-        }
-
-        if (! empty($filters['featured'])) {
-            $params['featured'] = 1;
-        }
+        $cacheKey = 'home_products:' . md5(json_encode($filters));
+
+        return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($filters) {
+            $params = [
+                'status'              => 1,
+                'visible_individually'=> 1,
+                'limit'               => $filters['limit'] ?? 12,
+                'sort'                => $filters['sort'] ?? 'name-asc',
+            ];
 
-        $products = $this->productRepository->getAll($params);
+            if (! empty($filters['new'])) {
+                $params['new'] = 1;
+            }
 
-        return $products->map(function ($product) {
-            $image = $product->images->first()
-                ?? $product->base_image;
+            if (! empty($filters['featured'])) {
+                $params['featured'] = 1;
+            }
 
-            return [
-                'id'               => $product->id,
-                'sku'              => $product->sku,
-                'name'             => $product->name,
-                'slug'             => $product->slug,
-                'type'             => $product->type,
-                'price'            => $product->getTypeInstance()->getMinimalPrice(),
-                'special_price'    => $product->special_price,
-                'image_url'        => $image->url ?? null,
-                'is_new'           => (bool) ($product->new ?? false),
-                'is_featured'      => (bool) ($product->featured ?? false),
-                'short_description'=> $product->short_description,
-            ];
-        })->values()->toArray();
+            $products = $this->productRepository->getAll($params);
+
+            return $products->map(function ($product) {
+                $image = $product->images->first()
+                    ?? $product->base_image;
+
+                $approvedReviews = $product->reviews->where('status', 'approved');
+
+                return [
+                    'id'               => $product->id,
+                    'sku'              => $product->sku,
+                    'name'             => $product->name,
+                    'slug'             => $product->slug,
+                    'type'             => $product->type,
+                    'price'            => (float) $product->price,
+                    'min_price'        => $product->getTypeInstance()->getMinimalPrice(),
+                    'special_price'    => $product->special_price,
+                    'image_url'        => $image->url ?? null,
+                    'is_new'           => (bool) ($product->new ?? false),
+                    'is_featured'      => (bool) ($product->featured ?? false),
+                    'short_description'=> $product->short_description,
+                    'rating'           => round($approvedReviews->avg('rating') ?? 0, 1),
+                    'review_count'     => $approvedReviews->count(),
+                ];
+            })->values()->toArray();
+        });
     }
 
     /**

+ 50 - 0
packages/Webkul/Shop/src/Listeners/ThemeCacheCleaner.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Webkul\Shop\Listeners;
+
+use Illuminate\Support\Facades\Cache;
+
+class ThemeCacheCleaner
+{
+    /**
+     * 清除指定 Theme 的所有首页缓存。
+     */
+    protected function clearHomeCache($theme): void
+    {
+        $localeCodes = array_keys(core()->getAllLocales()->toArray()) ?: [core()->getDefaultLocaleCode()];
+
+        foreach ($localeCodes as $locale) {
+            // 清除首页整页缓存
+            Cache::forget('home_api:' . $theme->channel->code . ':' . $locale);
+            // 清除分类树缓存
+            Cache::forget('home_categories:' . $theme->channel->code . ':' . $locale);
+        }
+    }
+
+    /**
+     * 主题创建后清除缓存。
+     */
+    public function afterCreate($theme): void
+    {
+        $this->clearHomeCache($theme);
+    }
+
+    /**
+     * 主题更新后清除缓存。
+     */
+    public function afterUpdate($theme): void
+    {
+        $this->clearHomeCache($theme);
+    }
+
+    /**
+     * 主题删除前清除缓存。
+     */
+    public function beforeDelete($themeId): void
+    {
+        $theme = app(\Webkul\Theme\Repositories\ThemeCustomizationRepository::class)->find($themeId);
+        if ($theme) {
+            $this->clearHomeCache($theme);
+        }
+    }
+}

+ 16 - 0
packages/Webkul/Shop/src/Providers/EventServiceProvider.php

@@ -9,6 +9,7 @@ use Webkul\Shop\Listeners\Invoice;
 use Webkul\Shop\Listeners\Order;
 use Webkul\Shop\Listeners\Refund;
 use Webkul\Shop\Listeners\Shipment;
+use Webkul\Shop\Listeners\ThemeCacheCleaner;
 
 class EventServiceProvider extends ServiceProvider
 {
@@ -78,5 +79,20 @@ class EventServiceProvider extends ServiceProvider
         'sales.refund.save.after' => [
             [Refund::class, 'afterCreated'],
         ],
+
+        /**
+         * Theme customization cache cleanup.
+         */
+        'theme_customization.create.after' => [
+            [ThemeCacheCleaner::class, 'afterCreate'],
+        ],
+
+        'theme_customization.update.after' => [
+            [ThemeCacheCleaner::class, 'afterUpdate'],
+        ],
+
+        'theme_customization.delete.before' => [
+            [ThemeCacheCleaner::class, 'beforeDelete'],
+        ],
     ];
 }