Browse Source

refactor(core): 扩展添加启动逻辑

runphp 6 tháng trước cách đây
mục cha
commit
372b7d74c5

+ 1 - 2
README.md

@@ -1,10 +1,9 @@
-
-
 # SixShop Core扩展
 
 这个扩展为后端应用提供了核心功能
 
 ## 安装
+
 ```shell
 composer require six-shop/core
 ```

+ 42 - 42
composer.json

@@ -1,45 +1,45 @@
 {
-    "name": "six-shop/core",
-    "description": "核心扩展",
-    "type": "composer-plugin",
-    "keywords": [
-        "sixshop",
-        "thinkphp"
-    ],
-    "license": "MIT",
-    "autoload": {
-        "psr-4": {
-            "SixShop\\Core\\": "src/"
-        }
-    },
-    "authors": [
-        {
-            "name": "hui he",
-            "email": "runphp@qq.com"
-        }
-    ],
-    "require": {
-        "php": ">=8.3",
-        "composer-plugin-api": "^2.0",
-        "composer/composer": "^2.8.11",
-        "topthink/framework": "^8.1",
-        "topthink/think-orm": "^4.0",
-        "topthink/think-helper": "^3.1.11",
-        "topthink/think-ide-helper": "^2.0",
-        "topthink/think-migration": "^3.1.1",
-        "topthink/think-multi-app": "^1.1.1",
-        "topthink/think-queue": "^3.0.12",
-        "topthink/think-view": "^2.0.5",
-        "topthink/think-worker": "^5.0.2",
-        "opis/closure": "^4.3.1",
-        "workerman/crontab": "^1.0.7"
-    },
-    "extra": {
-        "think": {
-            "services": [
-                "SixShop\\Core\\Service\\CoreService"
-            ]
-        },
-        "class": "SixShop\\Core\\Plugin"
+  "name": "six-shop/core",
+  "description": "核心扩展",
+  "type": "composer-plugin",
+  "keywords": [
+    "sixshop",
+    "thinkphp"
+  ],
+  "license": "MIT",
+  "autoload": {
+    "psr-4": {
+      "SixShop\\Core\\": "src/"
     }
+  },
+  "authors": [
+    {
+      "name": "hui he",
+      "email": "runphp@qq.com"
+    }
+  ],
+  "require": {
+    "php": ">=8.3",
+    "composer-plugin-api": "^2.0",
+    "composer/composer": "^2.8.11",
+    "topthink/framework": "^8.1",
+    "topthink/think-orm": "^4.0",
+    "topthink/think-helper": "^3.1.11",
+    "topthink/think-ide-helper": "^2.0",
+    "topthink/think-migration": "^3.1.1",
+    "topthink/think-multi-app": "^1.1.1",
+    "topthink/think-queue": "^3.0.12",
+    "topthink/think-view": "^2.0.5",
+    "topthink/think-worker": "^5.0.2",
+    "opis/closure": "^4.3.1",
+    "workerman/crontab": "^1.0.7"
+  },
+  "extra": {
+    "think": {
+      "services": [
+        "SixShop\\Core\\Service\\CoreService"
+      ]
+    },
+    "class": "SixShop\\Core\\Plugin"
+  }
 }

+ 4 - 1
src/Attribute/Cron.php

@@ -1,5 +1,6 @@
 <?php
 declare(strict_types=1);
+
 namespace SixShop\Core\Attribute;
 
 #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
@@ -8,5 +9,7 @@ class Cron
     public function __construct(
         public string $rule,
         public string $name = '',
-    ) {}
+    )
+    {
+    }
 }

+ 3 - 1
src/Attribute/Hook.php

@@ -8,5 +8,7 @@ class Hook
 {
     public function __construct(
         public string|array $hook,
-    ) {}
+    )
+    {
+    }
 }

+ 1 - 0
src/Contracts/CoreExtensionInterface.php

@@ -1,5 +1,6 @@
 <?php
 declare(strict_types=1);
+
 namespace SixShop\Core\Contracts;
 
 interface CoreExtensionInterface

+ 5 - 0
src/Contracts/ExtensionInterface.php

@@ -48,4 +48,9 @@ interface ExtensionInterface
      * 获取扩展计划任务
      */
     public function getCronJobs(): array;
+
+    /**
+     * 启动扩展
+     */
+    public function boot(): void;
 }

+ 1 - 0
src/Event/BeforeRegisterRouteEvent.php

@@ -1,5 +1,6 @@
 <?php
 declare(strict_types=1);
+
 namespace SixShop\Core\Event;
 
 class BeforeRegisterRouteEvent

+ 2 - 1
src/Exception/ExceptionHandle.php

@@ -1,5 +1,6 @@
 <?php
 declare(strict_types=1);
+
 namespace SixShop\Core\Exception;
 
 use think\db\exception\ModelNotFoundException;
@@ -18,7 +19,7 @@ class ExceptionHandle extends Handle
             $e = new NotFoundException($e);
         }
         if ($e instanceof ValidateException) {
-            $e = new LogicException(error_response(msg:$e->getMessage(),  status: 'invalid_argument', code: 400, httpCode: 200));
+            $e = new LogicException(error_response(msg: $e->getMessage(), status: 'invalid_argument', code: 400, httpCode: 200));
         }
         return parent::render($request, $e);
     }

+ 1 - 0
src/Exception/LogicException.php

@@ -1,5 +1,6 @@
 <?php
 declare(strict_types=1);
+
 namespace SixShop\Core\Exception;
 
 use think\exception\HttpResponseException;

+ 16 - 2
src/ExtensionAbstract.php

@@ -4,10 +4,18 @@ declare(strict_types=1);
 namespace SixShop\Core;
 
 use SixShop\Core\Contracts\ExtensionInterface;
+use think\helper\Macroable;
 
+/**
+ * @method bool available() 扩展是否可用
+ */
 abstract class ExtensionAbstract implements ExtensionInterface
 {
-    private array $info;
+    use Macroable;
+
+    protected array $info;
+
+    protected bool $isBooted = false;
 
     public function getInfo(): array
     {
@@ -45,9 +53,10 @@ abstract class ExtensionAbstract implements ExtensionInterface
         }
         return require $this->getBaseDir() . '/command.php';
     }
+
     public function getHooks(): array
     {
-        return  [];
+        return [];
     }
 
     /**
@@ -72,4 +81,9 @@ abstract class ExtensionAbstract implements ExtensionInterface
     {
         return [];
     }
+
+    public function boot(): void
+    {
+        $this->isBooted = true;
+    }
 }

+ 0 - 1
src/ExtensionInstaller.php

@@ -4,7 +4,6 @@ declare(strict_types=1);
 namespace SixShop\Core;
 
 use Composer\Installer\LibraryInstaller;
-use Composer\Package\PackageInterface;
 
 class ExtensionInstaller extends LibraryInstaller
 {

+ 10 - 10
src/Helper.php

@@ -158,6 +158,16 @@ final class Helper
         ]);
     }
 
+    /**
+     * 抛出逻辑异常
+     * @throws LogicException
+     * @deprecated 函数已弃用, 请使用 throw_logic_exception()
+     */
+    public static function throw_logic_exception(string $msg = 'error', int $code = 1, string $status = 'error', mixed $data = [], int $httpCode = 200, $header = [], $options = []): void
+    {
+        throw new LogicException(self::error_response($msg, $status, $code, $data, $httpCode, $header, $options));
+    }
+
     /**
      * 返回失败数据
      * @deprecated 函数已弃用, 请使用 error_response()
@@ -172,16 +182,6 @@ final class Helper
         ], $httpCode, $header, $options);
     }
 
-    /**
-     * 抛出逻辑异常
-     * @throws LogicException
-     * @deprecated 函数已弃用, 请使用 throw_logic_exception()
-     */
-    public static function throw_logic_exception(string $msg = 'error', int $code = 1, string $status = 'error', mixed $data = [], int $httpCode = 200, $header = [], $options = []): void
-    {
-        throw new LogicException(self::error_response($msg, $status, $code, $data, $httpCode, $header, $options));
-    }
-
     /**
      * 构建树形结构选项
      * @param array $data 数据源

+ 35 - 35
src/Job/BaseJob.php

@@ -3,10 +3,10 @@ declare(strict_types=1);
 
 namespace SixShop\Core\Job;
 
+use Closure;
 use think\facade\Log;
 use think\queue\Job;
 use function Opis\Closure\{serialize, unserialize};
-use Closure;
 
 /**
  * @template T
@@ -26,36 +26,18 @@ abstract class BaseJob
     protected bool $isClosure = false;
 
     /**
-     * 任务失败处理方法 - 子类可重写
-     *
-     * @param T $data 任务数据
-     */
-    protected function onFailed(mixed $data): void
-    {
-        // 默认失败处理逻辑
-        Log::error('队列任务执行失败: ' . static::class, (array)$data);
-    }
-
-    /**
-     * 任务前置处理 - 子类可重写
-     *
-     * @param T $data 任务数据
-     * @return bool 是否继续执行
-     */
-    protected function beforeExecute(mixed $data): bool
-    {
-        return true;
-    }
-
-    /**
-     * 任务后置处理 - 子类可重写
+     * 分发任务
      *
      * @param T $data 任务数据
-     * @param mixed $result 执行结果
+     * @param int $delay 延迟时间
+     * @param string|null $queue 队列名称
      */
-    protected function afterExecute(mixed $data, mixed $result): void
+    public static function dispatch(mixed $data = '', int $delay = 0, ?string $queue = null): JobDispatcher
     {
-        // 可以在这里添加通用的后置处理逻辑
+        if ($data instanceof Closure) {
+            $data = serialize($data);
+        }
+        return new JobDispatcher(static ::class, $data, $delay, $queue);
     }
 
     /**
@@ -93,18 +75,25 @@ abstract class BaseJob
     }
 
     /**
-     * 分发任务
+     * 任务前置处理 - 子类可重写
      *
      * @param T $data 任务数据
-     * @param int $delay 延迟时间
-     * @param string|null $queue 队列名称
+     * @return bool 是否继续执行
      */
-    public static function dispatch(mixed $data = '', int $delay = 0, ?string $queue = null): JobDispatcher
+    protected function beforeExecute(mixed $data): bool
     {
-        if ($data instanceof Closure) {
-            $data = serialize($data);
-        }
-        return new JobDispatcher(static ::class, $data, $delay, $queue);
+        return true;
+    }
+
+    /**
+     * 任务后置处理 - 子类可重写
+     *
+     * @param T $data 任务数据
+     * @param mixed $result 执行结果
+     */
+    protected function afterExecute(mixed $data, mixed $result): void
+    {
+        // 可以在这里添加通用的后置处理逻辑
     }
 
     /**
@@ -140,6 +129,17 @@ abstract class BaseJob
         }
     }
 
+    /**
+     * 任务失败处理方法 - 子类可重写
+     *
+     * @param T $data 任务数据
+     */
+    protected function onFailed(mixed $data): void
+    {
+        // 默认失败处理逻辑
+        Log::error('队列任务执行失败: ' . static::class, (array)$data);
+    }
+
     /**
      * 设置最大重试次数
      *

+ 5 - 4
src/Job/JobDispatcher.php

@@ -7,10 +7,11 @@ class JobDispatcher
 {
     public function __construct(
         private readonly string $jobClass,
-        private readonly mixed $data = null,
-        private int $delay = 0,
-        private ?string $queue = null
-    ) {
+        private readonly mixed  $data = null,
+        private int             $delay = 0,
+        private ?string         $queue = null
+    )
+    {
     }
 
     /**

+ 23 - 24
src/Plugin.php

@@ -18,6 +18,29 @@ class Plugin implements PluginInterface, EventSubscriberInterface
 
     public static array $installedSixShopExtensions = [];
 
+    public static function getSubscribedEvents(): array
+    {
+        return [
+            ScriptEvents::POST_AUTOLOAD_DUMP => 'onPostAutoloadDump',
+        ];
+    }
+
+    /**
+     * @return array{root: array{reference: string}, versions: array<string, array>}
+     */
+    public static function getInstalledSixShopExtensions(): array
+    {
+        if (self::$installedSixShopExtensions) {
+            return self::$installedSixShopExtensions;
+        }
+        $vendorDir = key(ClassLoader::getRegisteredLoaders());
+        $filePath = $vendorDir . '/composer/installedSixShop.php';
+        if (file_exists($filePath)) {
+            return self::$installedSixShopExtensions = require $filePath;
+        }
+        throw new \RuntimeException('Please run "composer dump-autoload" to generate installedSixShop.php');
+    }
+
     public function activate(Composer $composer, IOInterface $io): void
     {
         $installer = new ExtensionInstaller($io, $composer, self::EXTENSION_TYPE);
@@ -32,14 +55,6 @@ class Plugin implements PluginInterface, EventSubscriberInterface
     {
     }
 
-    public static function getSubscribedEvents(): array
-    {
-        return [
-            ScriptEvents::POST_AUTOLOAD_DUMP => 'onPostAutoloadDump',
-        ];
-    }
-
-
     public function onPostAutoloadDump(Event $event): void
     {
         $installedSixShopExtensions = [
@@ -59,20 +74,4 @@ class Plugin implements PluginInterface, EventSubscriberInterface
         $filePath = $event->getComposer()->getConfig()->get('vendor-dir') . '/composer/installedSixShop.php';
         file_put_contents($filePath, '<?php return ' . var_export($installedSixShopExtensions, true) . ';');
     }
-
-    /**
-     * @return array{root: array{reference: string}, versions: array<string, array>}
-     */
-    public static function getInstalledSixShopExtensions(): array
-    {
-        if (self::$installedSixShopExtensions) {
-            return self::$installedSixShopExtensions;
-        }
-        $vendorDir = key(ClassLoader::getRegisteredLoaders());
-        $filePath = $vendorDir . '/composer/installedSixShop.php';
-        if (file_exists($filePath)) {
-            return self::$installedSixShopExtensions = require $filePath;
-        }
-        throw new \RuntimeException('Please run "composer dump-autoload" to generate installedSixShop.php');
-    }
 }

+ 1 - 0
src/Request.php

@@ -1,5 +1,6 @@
 <?php
 declare(strict_types=1);
+
 namespace SixShop\Core;
 
 use think\helper\Macroable;

+ 5 - 0
src/Service/AutoloadService.php

@@ -37,6 +37,11 @@ class AutoloadService
                 $this->app->bind('extension.' . $moduleName, $extensionClass);
             }
         }
+        foreach ($extensionComposerMap + $extensionNameList as $moduleName => $_) {
+            $extension = $this->getExtension($moduleName);
+            $extension->boot();
+            $this->app->event->trigger('extension.boot', $extension);
+        }
     }
 
     public function getExtension(string $moduleName): ExtensionInterface

+ 3 - 0
src/Service/CommandService.php

@@ -21,6 +21,9 @@ class CommandService
             } catch (ClassNotFoundException $_) {
                 continue;
             }
+            if (!$extension->available()) {
+                continue;
+            }
             $commands += $extension->getCommands();
         }
         $closure($commands);

+ 21 - 22
src/Service/CoreService.php

@@ -11,7 +11,6 @@ use SixShop\Core\Plugin;
 use SixShop\Core\Request;
 use think\event\HttpRun;
 use think\exception\Handle;
-use think\facade\Event;
 use think\Service;
 
 class CoreService extends Service
@@ -28,6 +27,11 @@ class CoreService extends Service
 
     private static array $normalExtensionNameList = [];
 
+    public static function getExtensionComposerMap(): array
+    {
+        return self::$extensionComposerMap;
+    }
+
     public function register(): void
     {
         $this->app->bind(Handle::class, ExceptionHandle::class);
@@ -39,25 +43,6 @@ class CoreService extends Service
         $this->compatibleExtensionNameList();
     }
 
-    public function boot(): void
-    {
-        $this->app->make(AutoloadService::class)->load(self::$extensionComposerMap,self::$normalExtensionNameList);
-        $this->app->make(HookAttributeService::class)->init();
-        $this->app->event->trigger('hook_init', $this->app);
-        $this->app->event->listen(HttpRun::class, function () {
-            $this->registerRoutes($this->app->make(RegisterRouteService::class)->init());
-        });
-
-        $this->app->make(CommandService::class)->init(function ($commands) {
-            $this->commands($commands);
-        });
-    }
-
-    public static function getExtensionComposerMap(): array
-    {
-        return self::$extensionComposerMap;
-    }
-
     private function initExtensionList(): void
     {
         if (!empty(self::$extensionComposerMap)) {
@@ -65,7 +50,7 @@ class CoreService extends Service
         }
         $runtimePath = $this->app->getRootPath() . 'runtime/';
         $reference = Plugin::getInstalledSixShopExtensions()['root']['reference'];
-        $extensionComposerFile = $runtimePath . 'extension_' .$reference.'.php';
+        $extensionComposerFile = $runtimePath . 'extension_' . $reference . '.php';
         if (file_exists($extensionComposerFile)) {
             self::$extensionComposerMap = require $extensionComposerFile;
             return;
@@ -100,7 +85,7 @@ class CoreService extends Service
                     if (array_key_exists($item, self::$extensionComposerMap)) {
                         continue;
                     }
-                    if (is_dir( Helper::extension_path($item).'src')) {
+                    if (is_dir(Helper::extension_path($item) . 'src')) {
                         self::$extensionNameList[] = $item;
                         self::$normalExtensionNameList[] = $item;
                     }
@@ -108,4 +93,18 @@ class CoreService extends Service
             }
         }
     }
+
+    public function boot(): void
+    {
+        $this->app->make(AutoloadService::class)->load(self::$extensionComposerMap, self::$normalExtensionNameList);
+        $this->app->make(HookAttributeService::class)->init();
+        $this->app->event->trigger('hook_init', $this->app);
+        $this->app->event->listen(HttpRun::class, function () {
+            $this->registerRoutes($this->app->make(RegisterRouteService::class)->init());
+        });
+
+        $this->app->make(CommandService::class)->init(function ($commands) {
+            $this->commands($commands);
+        });
+    }
 }

+ 4 - 1
src/Service/HookAttributeService.php

@@ -21,7 +21,10 @@ readonly class HookAttributeService
         foreach (Helper::extension_name_list() as $extensionName) {
             try {
                 $extension = $this->autoloadService->getExtension($extensionName);
-            } catch (ClassNotFoundException $_) {
+            } catch (ClassNotFoundException) {
+                continue;
+            }
+            if (!$extension->available()) {
                 continue;
             }
             $hookClassList = $extension->getHooks();

+ 4 - 1
src/Service/RegisterRouteService.php

@@ -17,10 +17,13 @@ class RegisterRouteService
 
     public function init(): \Closure
     {
-        return function ()  {
+        return function () {
             $appName = $this->http->getName();
             foreach (Helper::extension_name_list() as $extensionName) {
                 $extension = $this->autoloadService->getExtension($extensionName);
+                if (!$extension->available()) {
+                    continue;
+                }
                 $routes = $extension->getRoutes();
                 if (isset($routes[$appName])) {
                     $routeFile = $routes[$appName];