diff --git a/plugin/think-plugs-helper/composer.json b/plugin/think-plugs-helper/composer.json
index 263f62526..fb83b6916 100644
--- a/plugin/think-plugs-helper/composer.json
+++ b/plugin/think-plugs-helper/composer.json
@@ -14,7 +14,6 @@
"php": "^8.1",
"ext-json": "*",
"ext-zlib": "*",
- "doctrine/dbal": "^4.2",
"topthink/think-orm": "^2.0|^3.0|^4.0",
"topthink/think-ide-helper": "*"
},
@@ -30,4 +29,4 @@
]
}
}
-}
+}
\ No newline at end of file
diff --git a/plugin/think-plugs-helper/src/DbBackupStruct.php b/plugin/think-plugs-helper/src/DbBackupStruct.php
deleted file mode 100644
index 366526e11..000000000
--- a/plugin/think-plugs-helper/src/DbBackupStruct.php
+++ /dev/null
@@ -1,163 +0,0 @@
-setName('xadmin:helper:backup');
- $this->addOption('all', 'a', Option::VALUE_NONE, 'Backup All Tables');
- $this->setDescription('恢复数据前是否强制清空所有表数据');
- }
-
- /**
- * 指令执行入口.
- */
- public function handle(): void
- {
- $this->backupSchema() && $this->backupTables();
- }
-
- /**
- * 检查是否允许执行任务
- * @return bool 返回true表示允许执行
- */
- public function isEnabled(): bool
- {
- return SystemService::isDebug();
- }
-
- /**
- * 备份数据库结构,使用 gzip 压缩保存.
- */
- protected function backupSchema(): bool
- {
- try {
- $outputFile = $this->getSchemaPath();
- if (!($gz = gzopen($outputFile, 'w9'))) {
- $this->output->error("❌ 无法打开压缩文件写入数据库结构:{$outputFile}");
- return false;
- }
- $schema = $this->makeConnect()->createSchemaManager();
- gzwrite($gz, serialize($schema->introspectSchema()));
- gzclose($gz);
- $this->output->info("✅ 数据库结构已压缩保存至:{$outputFile}");
- return true;
- } catch (\Throwable $throwable) {
- $this->output->error("❌ 数据库结构导出失败:{$throwable->getMessage()}");
- return false;
- }
- }
-
- /**
- * 备份数据表数据,gzip 压缩写入.
- */
- protected function backupTables(): bool
- {
- $backupPath = $this->getBackupPath();
- is_dir(dirname($backupPath)) || mkdir(dirname($backupPath), 0755, true);
- if (!($gz = gzopen($backupPath, 'w9'))) {
- $this->output->error("❌ 无法打开压缩文件写入数据表数据:{$backupPath}");
- return false;
- }
- $force = (bool)$this->input->getOption('all');
- foreach ($this->getBkTables($force) as $table) {
- $total = 0;
- if (!empty($fields = $this->app->db->getFields($table))) {
- $query = $this->app->db->table($table)->order(in_array('id', $fields) ? 'id' : array_values($fields)[0]);
- in_array('ssid', $fields) && $query = $query->where('ssid', '0');
- in_array('deleted_at', $fields) && $query = $query->whereNull('deleted_at');
- $query->chunk(10000, function ($rows) use ($gz, $table, &$total) {
- foreach ($rows as $row) {
- $record = ['table' => $table, 'data' => (array)$row];
- gzwrite($gz, json_encode($record, JSON_UNESCAPED_UNICODE) . "\n");
- ++$total;
- }
- });
- }
- $this->output->writeln("✅ 表 {$table} 备份完成,共 {$total} 行");
- }
-
- gzclose($gz);
- $this->output->info("📂 表数据已压缩写入:{$backupPath}");
- return true;
- }
-
- /**
- * 获取需要备份的表.
- */
- protected function getBkTables(bool $all = true): array
- {
- // 接收指定打包数据表
- if ($all) {
- [$tables] = SystemService::getTables();
- } elseif (empty($tables = Library::$sapp->config->get('phinx.tables', []))) {
- $this->output->error('❌ 配置文件未定义数据表列表,请检查配置项:phinx.tables');
- return [];
- }
- return $tables;
- }
-
- /**
- * 创建连接对接.
- */
- protected function makeConnect(): Connection
- {
- $config = $this->app->db->connect()->getConfig();
- $config['host'] = $config['hostname'] ?? '';
- $config['user'] = $config['username'] ?? '';
- $config['dbname'] = $config['database'] ?? '';
- if (in_array($config['type'], ['mysql', 'sqlite', 'oci'])) {
- $config['driver'] = 'pdo_' . $config['type'];
- }
- return DriverManager::getConnection($config);
- }
-
- /**
- * 结构文件路径,压缩格式.
- */
- protected function getSchemaPath(): string
- {
- return syspath('database/backup.schema.gz');
- }
-
- /**
- * 数据备份文件路径,压缩格式.
- */
- protected function getBackupPath(): string
- {
- return syspath('database/backup.data.gz');
- }
-}
diff --git a/plugin/think-plugs-helper/src/DbRestoreStruct.php b/plugin/think-plugs-helper/src/DbRestoreStruct.php
deleted file mode 100644
index 2f61c6b26..000000000
--- a/plugin/think-plugs-helper/src/DbRestoreStruct.php
+++ /dev/null
@@ -1,251 +0,0 @@
-setName('xadmin:helper:restore');
- $this->addOption('force', 'f', Option::VALUE_NONE, 'Force All Update');
- $this->setDescription('恢复数据前是否强制清空所有表数据');
- }
-
- /**
- * 命令执行入口。
- * @throws Exception
- */
- public function handle(): void
- {
- $this->restoreSchema() && $this->restoreBackup();
- }
-
- /**
- * 检查是否允许执行任务
- * @return bool 返回true表示允许执行
- */
- public function isEnabled(): bool
- {
- return SystemService::isDebug();
- }
-
- /**
- * 恢复数据库结构。
- * @throws Exception
- */
- protected function restoreSchema(): bool
- {
- if (!is_file($gzPath = self::getSchemaPath())) {
- $this->output->error("❌ 结构文件不存在:{$gzPath}");
- return false;
- }
-
- $content = @gzdecode(file_get_contents($gzPath));
- if (empty($content) || !($backupSchema = @unserialize($content)) instanceof Schema) {
- $this->output->error("❌ 解压或反序列化失败:{$gzPath}");
- return false;
- }
-
- $connect = self::makeConnect();
- $platform = $connect->getDatabasePlatform();
- $diff = (new Comparator($platform))->compareSchemas(
- $connect->createSchemaManager()->introspectSchema(),
- $backupSchema
- );
-
- $sqls = [];
- foreach ($diff->getCreatedTables() as $t) {
- $sqls = [...$sqls, ...$platform->getCreateTableSQL($t)];
- }
- foreach ($diff->getAlteredTables() as $t) {
- $sqls = [...$sqls, ...$platform->getAlterTableSQL($t)];
- }
- foreach ($diff->getDroppedTables() as $t) {
- $sqls[] = $platform->getDropTableSQL($t->getName());
- }
-
- if (!$sqls) {
- $this->output->info('✅ 数据库结构已一致,无需变更。');
- return true;
- }
-
- try {
- foreach ($sqls as $sql) {
- $this->output->writeln("🔧 执行 SQL:{$sql}");
- $connect->executeStatement($sql);
- }
- $this->output->info('✅ 数据库结构同步完成。');
- return true;
- } catch (\Throwable $throwable) {
- $this->output->error('❌ 结构同步失败:' . $throwable->getMessage());
- return false;
- }
- }
-
- /**
- * 恢复业务数据。
- * @throws Exception
- */
- protected function restoreBackup(): bool
- {
- $force = (bool)$this->input->getOption('force');
- if (empty($tables = $this->getBkTables($force))) {
- $this->output->error('❌ 未定义需恢复的数据表');
- return false;
- }
-
- if (!file_exists($path = $this->getBackupPath())) {
- $this->output->error("❌ 备份数据文件不存在:{$path}");
- return false;
- }
-
- copy($path, $tmp = syspath('runtime/backup_data_tmp.gz'));
- $schemaManager = $this->makeConnect()->createSchemaManager();
-
- // 先清空 forceCleanTables 表,无需 count 检查
- $forceCleanTables = ['system_menu', 'system_dict_data', 'system_dict_type'];
- foreach (array_intersect($forceCleanTables, $tables) as $table) {
- _query($table)->empty();
- $this->output->writeln("🧹 强制清空表:{$table}");
- }
-
- if ($force) {
- // 如果是强制恢复,需要清空所有表(非 forceCleanTables 表)
- foreach ($schemaManager->listTableNames() as $table) {
- if (!in_array($table, $forceCleanTables, true)) {
- _query($table)->empty();
- $this->output->writeln("✅ 已经清空表:{$table}");
- }
- }
- }
-
- // 计算需要恢复的数据表
- $restoreTableFlags = [];
- foreach ($tables as $table) {
- $restoreTableFlags[$table] = $force || in_array($table, $forceCleanTables, true) || $this->app->db->table($table)->count() === 0;
- }
-
- if (!($fp = gzopen($tmp, 'rb'))) {
- $this->output->writeln("❌ 无法打开备份文件:{$tmp}");
- return false;
- }
-
- $totalLines = 0;
- $currentLine = 0;
- $batchInsert = [];
- $insertCount = array_fill_keys($tables, 0);
-
- try {
- while (!gzeof($fp)) {
- ++$totalLines;
- $row = json_decode(trim(gzgets($fp)), true);
- $table = $row['table'] ?? '-';
-
- // 判断是否跳过恢复,非强制恢复时,根据 tableFlags 决定是否插入
- if (empty($restoreTableFlags[$table]) || empty($row['data']) || !is_array($row['data'])) {
- continue;
- }
-
- ++$currentLine;
- ++$insertCount[$table];
- $batchInsert[$table][] = $row['data'];
- if (count($batchInsert[$table]) >= 1000) {
- $this->flushBatchInsert($table, $batchInsert[$table]);
- $this->output->writeln("📥 表 {$table} 批量插入 1000 行,已读取 {$totalLines} 行");
- }
- }
-
- // 插入剩余数据
- foreach ($batchInsert as $table => $rows) {
- if ($count = count($rows)) {
- $this->flushBatchInsert($table, $rows);
- $this->output->writeln("📥 表 {$table} 批量插入 {$count} 行,已读取 {$totalLines} 行");
- }
- }
- $this->output->writeln("✅ 数据恢复完成,共插入 {$currentLine} 行(读取 {$totalLines} 行)");
- foreach ($insertCount as $table => $count) {
- $count > 0 && $this->output->writeln("✅ 表 {$table} 插入 {$count} 行");
- }
- @unlink($tmp);
-
- // 恢复管理员数据
- $this->insertSuperUser();
-
- // 清理系统运行缓存
- return RuntimeService::clear(false);
- } catch (\Throwable $throwable) {
- trace_file($throwable);
- $this->output->error('❌ 数据恢复失败:' . $throwable->getMessage());
- return false;
- } finally {
- gzclose($fp);
- }
- }
-
- /**
- * 批量插入数据。
- */
- private function flushBatchInsert(string $table, array &$rows): void
- {
- if (!$rows) {
- return;
- }
-
- try {
- $this->app->db->table($table)->insertAll($rows);
- } catch (\Throwable $throwable) {
- $this->output->writeln("⚠️ 表 {$table} 插入失败:{$throwable->getMessage()}");
- } finally {
- $rows = [];
- }
- }
-
- /**
- * 插入默认管理员。
- */
- private function insertSuperUser(): void
- {
- $model = SystemUser::mk()->whereRaw('1=1')->findOrEmpty();
- $model->isEmpty() && $model->save([
- 'id' => '10000',
- 'username' => 'admin',
- 'nickname' => '超级管理员',
- 'password' => '21232f297a57a5a743894a0e4a801fc3',
- 'headimg' => 'https://thinkadmin.top/static/img/head.png',
- ], true);
- $this->output->writeln('✅ 管理员账号恢复成功');
- }
-}
diff --git a/plugin/think-plugs-helper/src/Service.php b/plugin/think-plugs-helper/src/Service.php
index 38a713eef..8bfc8ca33 100644
--- a/plugin/think-plugs-helper/src/Service.php
+++ b/plugin/think-plugs-helper/src/Service.php
@@ -27,8 +27,6 @@ class Service extends \think\Service
$this->commands([
DbModelStruct::class,
DbIndexStruct::class,
- DbBackupStruct::class,
- DbRestoreStruct::class,
]);
}
}