Преглед на файлове

feat(core): 为每个扩展添加 available 宏方法
- available 方法用于判断扩展是否已安装并启用

runphp преди 6 месеца
родител
ревизия
e3a7ee0e4a

+ 9 - 6
README.md

@@ -5,6 +5,7 @@
 这是用来管理扩展的扩展,主要是展示扩展列表,安装扩展,卸载扩展,更新扩展,扩展配置等功能。
 这是用来管理扩展的扩展,主要是展示扩展列表,安装扩展,卸载扩展,更新扩展,扩展配置等功能。
 
 
 ## 功能说明
 ## 功能说明
+
 1. **展示扩展列表**:列出所有已安装的扩展及其基本信息。
 1. **展示扩展列表**:列出所有已安装的扩展及其基本信息。
 2. **安装扩展**:支持从本地或远程仓库安装新的扩展。
 2. **安装扩展**:支持从本地或远程仓库安装新的扩展。
 3. **卸载扩展**:移除不再需要的扩展。
 3. **卸载扩展**:移除不再需要的扩展。
@@ -12,21 +13,23 @@
 5. **扩展配置**:为每个扩展提供独立的配置选项。
 5. **扩展配置**:为每个扩展提供独立的配置选项。
 
 
 ## 使用方法
 ## 使用方法
+
 1. **访问扩展管理页面**:
 1. **访问扩展管理页面**:
-   - 登录系统后,导航至“扩展管理”模块。
+    - 登录系统后,导航至“扩展管理”模块。
 2. **操作扩展**:
 2. **操作扩展**:
-   - 点击“安装”按钮以添加新扩展。
-   - 点击“卸载”按钮以移除现有扩展。
-   - 点击“更新”按钮以升级扩展版本。
-   - 点击“配置”按钮以调整扩展设置。
+    - 点击“安装”按钮以添加新扩展。
+    - 点击“卸载”按钮以移除现有扩展。
+    - 点击“更新”按钮以升级扩展版本。
+    - 点击“配置”按钮以调整扩展设置。
 
 
 ## 注意事项
 ## 注意事项
+
 1. **权限要求**:仅管理员用户可进行扩展的安装、卸载和更新操作。
 1. **权限要求**:仅管理员用户可进行扩展的安装、卸载和更新操作。
 2. **备份数据**:在执行卸载或更新操作前,请确保已备份相关数据。
 2. **备份数据**:在执行卸载或更新操作前,请确保已备份相关数据。
 3. **兼容性检查**:安装新扩展时,请确认其与当前系统的兼容性。
 3. **兼容性检查**:安装新扩展时,请确认其与当前系统的兼容性。
 
 
-
 ## HOOKS
 ## HOOKS
+
 1. **after_read_extension_config**: 在读取扩展配置后触发
 1. **after_read_extension_config**: 在读取扩展配置后触发
 1. **before_install_extension**: 在安装扩展前触发
 1. **before_install_extension**: 在安装扩展前触发
 1. **after_install_extension**: 在安装扩展后触发
 1. **after_install_extension**: 在安装扩展后触发

+ 1 - 1
command.php

@@ -4,8 +4,8 @@ declare(strict_types=1);
 use SixShop\System\Command\CoreExtensionConfigCommand;
 use SixShop\System\Command\CoreExtensionConfigCommand;
 use SixShop\System\Command\CrontabCommand;
 use SixShop\System\Command\CrontabCommand;
 use SixShop\System\Command\ExtensionManagementCommand;
 use SixShop\System\Command\ExtensionManagementCommand;
-use SixShop\System\Command\ModelPropertyCommand;
 use SixShop\System\Command\ExtensionScaffoldMakeCommand;
 use SixShop\System\Command\ExtensionScaffoldMakeCommand;
+use SixShop\System\Command\ModelPropertyCommand;
 
 
 return [
 return [
     'amp:property' => ModelPropertyCommand::class,
     'amp:property' => ModelPropertyCommand::class,

+ 1 - 1
composer.json

@@ -8,7 +8,7 @@
   ],
   ],
   "require": {
   "require": {
     "php": ">=8.3",
     "php": ">=8.3",
-    "six-shop/core": ">=0.5.9 <1.0"
+    "six-shop/core": ">=0.6 <1.0"
   },
   },
   "authors": [
   "authors": [
     {
     {

+ 5 - 6
database/migrations/20250624125157_extension_config.php

@@ -1,7 +1,6 @@
 <?php
 <?php
 
 
 use think\migration\Migrator;
 use think\migration\Migrator;
-use think\migration\db\Column;
 
 
 class ExtensionConfig extends Migrator
 class ExtensionConfig extends Migrator
 {
 {
@@ -14,12 +13,12 @@ class ExtensionConfig extends Migrator
             'id' => false,
             'id' => false,
             'primary_key' => 'id'
             'primary_key' => 'id'
         ]);
         ]);
-        
+
         $table->addColumn('id', 'integer', [
         $table->addColumn('id', 'integer', [
-                'identity' => true,
-                'signed' => false,
-                'comment' => '主键'
-            ])
+            'identity' => true,
+            'signed' => false,
+            'comment' => '主键'
+        ])
             ->addColumn('extension_id', 'string', [
             ->addColumn('extension_id', 'string', [
                 'limit' => 32,
                 'limit' => 32,
                 'comment' => '模块ID,关联extension表'
                 'comment' => '模块ID,关联extension表'

+ 0 - 1
database/migrations/20250627061219_extension.php

@@ -1,7 +1,6 @@
 <?php
 <?php
 
 
 use think\migration\Migrator;
 use think\migration\Migrator;
-use think\migration\db\Column;
 
 
 class Extension extends Migrator
 class Extension extends Migrator
 {
 {

+ 0 - 1
database/migrations/20250702110812_extension_add_category.php

@@ -1,7 +1,6 @@
 <?php
 <?php
 
 
 use think\migration\Migrator;
 use think\migration\Migrator;
-use think\migration\db\Column;
 
 
 class ExtensionAddCategory extends Migrator
 class ExtensionAddCategory extends Migrator
 {
 {

+ 12 - 9
src/Command/ExtensionScaffoldMakeCommand.php

@@ -4,7 +4,6 @@ declare(strict_types=1);
 namespace SixShop\System\Command;
 namespace SixShop\System\Command;
 
 
 use SixShop\Core\Helper;
 use SixShop\Core\Helper;
-use Throwable;
 use think\console\Command;
 use think\console\Command;
 use think\console\Input;
 use think\console\Input;
 use think\console\input\Argument;
 use think\console\input\Argument;
@@ -109,9 +108,13 @@ class ExtensionScaffoldMakeCommand extends Command
 
 
         if ($dryRun) {
         if ($dryRun) {
             $output->writeln("[DRY-RUN] 将创建以下目录:");
             $output->writeln("[DRY-RUN] 将创建以下目录:");
-            foreach ($dirs as $d) { $output->writeln("  - $d"); }
+            foreach ($dirs as $d) {
+                $output->writeln("  - $d");
+            }
             $output->writeln("[DRY-RUN] 将创建以下关键文件(部分):");
             $output->writeln("[DRY-RUN] 将创建以下关键文件(部分):");
-            foreach ($planFiles as $f) { $output->writeln("  - $f"); }
+            foreach ($planFiles as $f) {
+                $output->writeln("  - $f");
+            }
             return 0;
             return 0;
         }
         }
 
 
@@ -421,12 +424,12 @@ PHP, $ns, $studly, $module);
         // 迁移 & 安装/卸载 SQL
         // 迁移 & 安装/卸载 SQL
         if ($withMigration) {
         if ($withMigration) {
             $install = "-- 安装 SQL 示例\n" .
             $install = "-- 安装 SQL 示例\n" .
-                       "CREATE TABLE IF NOT EXISTS `extension_{$module}_item`(\n" .
-                       "  `id` int unsigned NOT NULL AUTO_INCREMENT,\n" .
-                       "  `title` varchar(255) NOT NULL DEFAULT '',\n" .
-                       "  `created_at` int unsigned NOT NULL DEFAULT 0,\n" .
-                       "  PRIMARY KEY (`id`)\n" .
-                       ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n";
+                "CREATE TABLE IF NOT EXISTS `extension_{$module}_item`(\n" .
+                "  `id` int unsigned NOT NULL AUTO_INCREMENT,\n" .
+                "  `title` varchar(255) NOT NULL DEFAULT '',\n" .
+                "  `created_at` int unsigned NOT NULL DEFAULT 0,\n" .
+                "  PRIMARY KEY (`id`)\n" .
+                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n";
             $uninstall = "DROP TABLE IF EXISTS `extension_{$module}_item`;\n";
             $uninstall = "DROP TABLE IF EXISTS `extension_{$module}_item`;\n";
             file_put_contents("$base/config/install.sql", $install);
             file_put_contents("$base/config/install.sql", $install);
             file_put_contents("$base/config/uninstall.sql", $uninstall);
             file_put_contents("$base/config/uninstall.sql", $uninstall);

+ 1 - 1
src/Controller/ExtensionConfigController.php

@@ -7,7 +7,6 @@ use SixShop\Core\Helper;
 use SixShop\System\ExtensionManager;
 use SixShop\System\ExtensionManager;
 use think\Request;
 use think\Request;
 use think\Response;
 use think\Response;
-use think\response\Json;
 
 
 class ExtensionConfigController
 class ExtensionConfigController
 {
 {
@@ -15,6 +14,7 @@ class ExtensionConfigController
     {
     {
         return Helper::success_response($extensionManager->getExtensionConfig($id));
         return Helper::success_response($extensionManager->getExtensionConfig($id));
     }
     }
+
     public function edit(string $id, ExtensionManager $extensionManager): Response
     public function edit(string $id, ExtensionManager $extensionManager): Response
     {
     {
         return Helper::success_response($extensionManager->getExtensionConfigForm($id));
         return Helper::success_response($extensionManager->getExtensionConfigForm($id));

+ 9 - 8
src/Controller/ExtensionController.php

@@ -1,11 +1,11 @@
 <?php
 <?php
 declare(strict_types=1);
 declare(strict_types=1);
+
 namespace SixShop\System\Controller;
 namespace SixShop\System\Controller;
 
 
 use SixShop\Core\Helper;
 use SixShop\Core\Helper;
 use SixShop\System\Enum\ExtensionStatusEnum;
 use SixShop\System\Enum\ExtensionStatusEnum;
 use SixShop\System\ExtensionManager;
 use SixShop\System\ExtensionManager;
-use SixShop\System\Migrate;
 use think\App;
 use think\App;
 use think\facade\Event;
 use think\facade\Event;
 use think\paginator\driver\Bootstrap;
 use think\paginator\driver\Bootstrap;
@@ -20,14 +20,14 @@ class ExtensionController
             'total' => count($extensionList),
             'total' => count($extensionList),
             'enabled' => 0,
             'enabled' => 0,
             'disabled' => 0,
             'disabled' => 0,
-            'installed'  => 0,
+            'installed' => 0,
             'uninstalled' => 0,
             'uninstalled' => 0,
             'category_map' => $extensionManager->getCategoryMap(),
             'category_map' => $extensionManager->getCategoryMap(),
         ];
         ];
-        
+
         // 获取ExtensionService实例,用于检查菜单状态
         // 获取ExtensionService实例,用于检查菜单状态
         $extensionService = new \app\common\service\ExtensionService();
         $extensionService = new \app\common\service\ExtensionService();
-        
+
         foreach ($extensionList as &$extension) {
         foreach ($extensionList as &$extension) {
             // 检查每个扩展是否已有菜单
             // 检查每个扩展是否已有菜单
             try {
             try {
@@ -35,7 +35,7 @@ class ExtensionController
             } catch (\Exception $e) {
             } catch (\Exception $e) {
                 $extension['has_menu'] = false;
                 $extension['has_menu'] = false;
             }
             }
-            
+
             match ($extension['status']) {
             match ($extension['status']) {
                 ExtensionStatusEnum::ENABLED => $data['enabled']++,
                 ExtensionStatusEnum::ENABLED => $data['enabled']++,
                 ExtensionStatusEnum::DISABLED => $data['disabled']++,
                 ExtensionStatusEnum::DISABLED => $data['disabled']++,
@@ -61,6 +61,7 @@ class ExtensionController
         $extensionManager->refresh($id);
         $extensionManager->refresh($id);
         return Helper::success_response($data);
         return Helper::success_response($data);
     }
     }
+
     public function install(string $id, ExtensionManager $extensionManager): Response
     public function install(string $id, ExtensionManager $extensionManager): Response
     {
     {
         Event::trigger('before_install_extension', $id);
         Event::trigger('before_install_extension', $id);
@@ -72,10 +73,10 @@ class ExtensionController
     public function uninstall(string $id, ExtensionManager $extensionManager): Response
     public function uninstall(string $id, ExtensionManager $extensionManager): Response
     {
     {
         Event::trigger('before_uninstall_extension', $id);
         Event::trigger('before_uninstall_extension', $id);
-        Event::trigger('before_uninstall_'.$id.'_extension');
+        Event::trigger('before_uninstall_' . $id . '_extension');
         $extensionManager->uninstall($id);
         $extensionManager->uninstall($id);
         Event::trigger('after_uninstall_extension', $id);
         Event::trigger('after_uninstall_extension', $id);
-        Event::trigger('after_uninstall_'.$id.'_extension');
+        Event::trigger('after_uninstall_' . $id . '_extension');
         return Helper::success_response();
         return Helper::success_response();
     }
     }
 
 
@@ -105,7 +106,7 @@ class ExtensionController
             $infoFile = $extensionPath . $item . '/info.php';
             $infoFile = $extensionPath . $item . '/info.php';
             if (is_file($infoFile)) {
             if (is_file($infoFile)) {
                 $info = require $infoFile;
                 $info = require $infoFile;
-                if (!($info['is_core']?? false)) {
+                if (!($info['is_core'] ?? false)) {
                     $options[] = [
                     $options[] = [
                         'value' => $info['id'],
                         'value' => $info['id'],
                         'label' => $info['name'],
                         'label' => $info['name'],

+ 1 - 0
src/Cron/SystemCron.php

@@ -1,5 +1,6 @@
 <?php
 <?php
 declare(strict_types=1);
 declare(strict_types=1);
+
 namespace SixShop\System\Cron;
 namespace SixShop\System\Cron;
 
 
 use SixShop\Core\Attribute\Cron;
 use SixShop\Core\Attribute\Cron;

+ 1 - 0
src/Entity/ExtensionEntity.php

@@ -1,5 +1,6 @@
 <?php
 <?php
 declare(strict_types=1);
 declare(strict_types=1);
+
 namespace SixShop\System\Entity;
 namespace SixShop\System\Entity;
 
 
 use SixShop\Core\Entity\BaseEntity;
 use SixShop\Core\Entity\BaseEntity;

+ 1 - 1
src/Enum/ExtensionStatusEnum.php

@@ -2,7 +2,7 @@
 
 
 namespace SixShop\System\Enum;
 namespace SixShop\System\Enum;
 
 
-enum ExtensionStatusEnum:int
+enum ExtensionStatusEnum: int
 {
 {
     case UNINSTALLED = 1; // 未安装
     case UNINSTALLED = 1; // 未安装
     case INSTALLED = 2;
     case INSTALLED = 2;

+ 1 - 0
src/Event/CrontabWorkerStartEvent.php

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

+ 30 - 5
src/Extension.php

@@ -4,13 +4,21 @@ declare(strict_types=1);
 namespace SixShop\System;
 namespace SixShop\System;
 
 
 use SixShop\Core\ExtensionAbstract;
 use SixShop\Core\ExtensionAbstract;
+use SixShop\Core\Service\AutoloadService;
 use SixShop\System\Cron\SystemCron;
 use SixShop\System\Cron\SystemCron;
+use SixShop\System\Enum\ExtensionStatusEnum;
 use SixShop\System\Hook\ExtensionStatusHook;
 use SixShop\System\Hook\ExtensionStatusHook;
 use SixShop\System\Hook\GatheringCrontabEventHook;
 use SixShop\System\Hook\GatheringCrontabEventHook;
+use SixShop\System\Model\ExtensionModel;
+use function SixShop\Core\extension_name_list;
 
 
 class Extension extends ExtensionAbstract
 class Extension extends ExtensionAbstract
 {
 {
 
 
+    public function __construct(private readonly AutoloadService $autoloadService)
+    {
+    }
+
     public function getHooks(): array
     public function getHooks(): array
     {
     {
         return [
         return [
@@ -19,15 +27,32 @@ class Extension extends ExtensionAbstract
         ];
         ];
     }
     }
 
 
-    protected function getBaseDir(): string
-    {
-        return dirname(__DIR__);
-    }
-
     public function getCronJobs(): array
     public function getCronJobs(): array
     {
     {
         return [
         return [
             SystemCron::class
             SystemCron::class
         ];
         ];
     }
     }
+
+    public function boot(): void
+    {
+        if ($this->isBooted) {
+            return;
+        }
+        foreach (extension_name_list() as $extensionID) {
+            /** @var ExtensionAbstract $extension */
+            $extension = $this->autoloadService->getExtension($extensionID);
+            get_class($extension)::macro('available', function () {
+                $extensionID = $this->getInfo()['id'];
+                return ExtensionModel::where('id', $extensionID)
+                        ->value('status', ExtensionStatusEnum::UNINSTALLED, true) === ExtensionStatusEnum::ENABLED;
+            });
+        }
+        parent::boot();
+    }
+
+    protected function getBaseDir(): string
+    {
+        return dirname(__DIR__);
+    }
 }
 }

+ 2 - 2
src/ExtensionManager.php

@@ -138,7 +138,7 @@ class ExtensionManager extends Service
                     $configModel->save($item);
                     $configModel->save($item);
                     Event::trigger('after_write_extension_config:' . $item['extension_id'] . ':' . $item['key'], $item);
                     Event::trigger('after_write_extension_config:' . $item['extension_id'] . ':' . $item['key'], $item);
                 }
                 }
-                Event::trigger('after_write_extension_config:' . $item['extension_id'], array_column($updateData, null,'key'));
+                Event::trigger('after_write_extension_config:' . $item['extension_id'], array_column($updateData, null, 'key'));
             });
             });
         }
         }
         return true;
         return true;
@@ -308,7 +308,7 @@ class ExtensionManager extends Service
         }
         }
         ExtensionModel::where([
         ExtensionModel::where([
             ['id', '=', $id],
             ['id', '=', $id],
-            ['version','<>', $currentVersion],
+            ['version', '<>', $currentVersion],
         ])->update(['version' => $currentVersion]);
         ])->update(['version' => $currentVersion]);
     }
     }
 }
 }

+ 4 - 1
src/Job/ClosureJob.php

@@ -1,13 +1,16 @@
 <?php
 <?php
 declare(strict_types=1);
 declare(strict_types=1);
+
 namespace SixShop\System\Job;
 namespace SixShop\System\Job;
 
 
-use SixShop\Core\Job\BaseJob;
 use Closure;
 use Closure;
+use SixShop\Core\Job\BaseJob;
+
 class ClosureJob extends BaseJob
 class ClosureJob extends BaseJob
 {
 {
 
 
     protected bool $isClosure = true;
     protected bool $isClosure = true;
+
     protected function execute(Closure $data)
     protected function execute(Closure $data)
     {
     {
         return value($data);
         return value($data);

+ 2 - 2
src/Middleware/MacroPageMiddleware.php

@@ -15,8 +15,8 @@ class MacroPageMiddleware
         $request->macro('pageAndLimit', function (): array {
         $request->macro('pageAndLimit', function (): array {
             $page = input('page/d', 1);
             $page = input('page/d', 1);
             $limit = input('limit/d', 10);
             $limit = input('limit/d', 10);
-            throw_if($page < 1, Exception::class,'页码不能小于1');
-            throw_if($limit < 1 || $limit > 100, Exception::class,'每页数量必须在1-100之间');
+            throw_if($page < 1, Exception::class, '页码不能小于1');
+            throw_if($limit < 1 || $limit > 100, Exception::class, '每页数量必须在1-100之间');
             return ['page' => $page, 'list_rows' => $limit];
             return ['page' => $page, 'list_rows' => $limit];
         });
         });
         return $next($request);
         return $next($request);

+ 66 - 69
src/Migrate.php

@@ -16,17 +16,14 @@ use think\Model;
 
 
 class Migrate
 class Migrate
 {
 {
+    protected ?array $migrations = null;
+    protected AdapterInterface $adapter;
+    protected App $app;
     private string $moduleName;
     private string $moduleName;
-
     private string $path;
     private string $path;
-    protected ?array $migrations = null;
     private $input;
     private $input;
     private $output;
     private $output;
 
 
-    protected AdapterInterface $adapter;
-
-    protected App $app;
-
     public function __construct(App $app, string $moduleName)
     public function __construct(App $app, string $moduleName)
     {
     {
         $this->app = $app;
         $this->app = $app;
@@ -37,65 +34,6 @@ class Migrate
         $this->output = null;
         $this->output = null;
     }
     }
 
 
-    public function install(): array
-    {
-        $migrations = $this->getMigrations();
-        $versions = $this->getVersions();
-        $currentVersion = $this->getCurrentVersion();
-        if (empty($versions) && empty($migrations)) {
-            return [];
-        }
-        ksort($migrations);
-        $installVersions = [];
-        foreach ($migrations as $migration) {
-            if ($migration->getVersion() <= $currentVersion) {
-                continue;
-            }
-            if (!in_array($migration->getVersion(), $versions)) {
-                $installVersions[] = $migration->getVersion();
-                $this->executeMigration($migration);
-            }
-        }
-        return $installVersions;
-    }
-
-    public function uninstall(): void
-    {
-        $migrations = $this->getMigrations();
-        $versionLog = $this->getVersionLog();
-        $versions = array_keys($versionLog);
-
-        ksort($migrations);
-        sort($versions);
-        if (empty($versions)) {
-            return;
-        }
-        krsort($migrations);
-        foreach ($migrations as $migration) {
-            if (in_array($migration->getVersion(), $versions)) {
-                if (isset($versionLog[$migration->getVersion()]) && 0 != $versionLog[$migration->getVersion()]['breakpoint']) {
-                    break;
-                }
-                $this->executeMigration($migration, MigrationInterface::DOWN);
-            }
-        }
-    }
-
-    public function getMigrationList(): array
-    {
-        $migrations = $this->getMigrations();
-        MigrationsModel::maker(function (Model $model) {
-            if ($model instanceof MigrationsModel) {
-                $model->setOption('suffix', $this->moduleName);
-            }
-        });
-        $versionLog =  MigrationsModel::column('*', 'version');
-        foreach ($migrations as $key => $migration) {
-            $migrations[$key] = $versionLog[$key] ?? ['version'=> $key];
-        }
-        return array_values($migrations);
-    }
-
     protected function getMigrations(): ?array
     protected function getMigrations(): ?array
     {
     {
         if (null === $this->migrations) {
         if (null === $this->migrations) {
@@ -157,14 +95,31 @@ class Migrate
         return $this->migrations;
         return $this->migrations;
     }
     }
 
 
-    protected function getVersions()
+    public function install(): array
     {
     {
-        return $this->getAdapter()->getVersions();
+        $migrations = $this->getMigrations();
+        $versions = $this->getVersions();
+        $currentVersion = $this->getCurrentVersion();
+        if (empty($versions) && empty($migrations)) {
+            return [];
+        }
+        ksort($migrations);
+        $installVersions = [];
+        foreach ($migrations as $migration) {
+            if ($migration->getVersion() <= $currentVersion) {
+                continue;
+            }
+            if (!in_array($migration->getVersion(), $versions)) {
+                $installVersions[] = $migration->getVersion();
+                $this->executeMigration($migration);
+            }
+        }
+        return $installVersions;
     }
     }
 
 
-    protected function getVersionLog()
+    protected function getVersions()
     {
     {
-        return $this->getAdapter()->getVersionLog();
+        return $this->getAdapter()->getVersions();
     }
     }
 
 
     public function getAdapter()
     public function getAdapter()
@@ -290,4 +245,46 @@ class Migrate
         $this->getAdapter()
         $this->getAdapter()
             ->migrated($migration, $direction, date('Y-m-d H:i:s', $startTime), date('Y-m-d H:i:s', time()));
             ->migrated($migration, $direction, date('Y-m-d H:i:s', $startTime), date('Y-m-d H:i:s', time()));
     }
     }
+
+    public function uninstall(): void
+    {
+        $migrations = $this->getMigrations();
+        $versionLog = $this->getVersionLog();
+        $versions = array_keys($versionLog);
+
+        ksort($migrations);
+        sort($versions);
+        if (empty($versions)) {
+            return;
+        }
+        krsort($migrations);
+        foreach ($migrations as $migration) {
+            if (in_array($migration->getVersion(), $versions)) {
+                if (isset($versionLog[$migration->getVersion()]) && 0 != $versionLog[$migration->getVersion()]['breakpoint']) {
+                    break;
+                }
+                $this->executeMigration($migration, MigrationInterface::DOWN);
+            }
+        }
+    }
+
+    protected function getVersionLog()
+    {
+        return $this->getAdapter()->getVersionLog();
+    }
+
+    public function getMigrationList(): array
+    {
+        $migrations = $this->getMigrations();
+        MigrationsModel::maker(function (Model $model) {
+            if ($model instanceof MigrationsModel) {
+                $model->setOption('suffix', $this->moduleName);
+            }
+        });
+        $versionLog = MigrationsModel::column('*', 'version');
+        foreach ($migrations as $key => $migration) {
+            $migrations[$key] = $versionLog[$key] ?? ['version' => $key];
+        }
+        return array_values($migrations);
+    }
 }
 }

+ 11 - 10
src/Model/ExtensionConfigModel.php

@@ -1,5 +1,6 @@
 <?php
 <?php
 declare(strict_types=1);
 declare(strict_types=1);
+
 namespace SixShop\System\Model;
 namespace SixShop\System\Model;
 
 
 use think\Model;
 use think\Model;
@@ -22,16 +23,6 @@ class ExtensionConfigModel extends Model
     protected $name = 'extension_config';
     protected $name = 'extension_config';
     protected $pk = 'id';
     protected $pk = 'id';
 
 
-    protected function getOptions(): array
-    {
-        return [
-            'type' => [
-                'value' => 'json'
-            ],
-            'jsonAssoc' => true,
-        ];
-    }
-
     public function getValueAttr(Json $value, array $data)
     public function getValueAttr(Json $value, array $data)
     {
     {
         $raw = $value->value();
         $raw = $value->value();
@@ -54,4 +45,14 @@ class ExtensionConfigModel extends Model
             default => $raw,
             default => $raw,
         };
         };
     }
     }
+
+    protected function getOptions(): array
+    {
+        return [
+            'type' => [
+                'value' => 'json'
+            ],
+            'jsonAssoc' => true,
+        ];
+    }
 }
 }

+ 1 - 0
src/Model/MigrationsModel.php

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

+ 1 - 0
src/Trait/EventTrait.php

@@ -1,5 +1,6 @@
 <?php
 <?php
 declare(strict_types=1);
 declare(strict_types=1);
+
 namespace SixShop\System\Trait;
 namespace SixShop\System\Trait;