Files
star-erp/app/Console/Commands/MigrateToTenant.php

132 lines
4.1 KiB
PHP
Raw Normal View History

<?php
namespace App\Console\Commands;
use App\Models\Tenant;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* 將現有資料遷移到租戶資料庫
*
* 此指令用於初次設定多租戶時,將現有的 ERP 資料遷移到第一個租戶
*/
class MigrateToTenant extends Command
{
protected $signature = 'tenancy:migrate-data {tenant_id} {--dry-run : 只顯示會遷移的表,不實際執行}';
protected $description = '將現有 central DB 資料遷移到指定租戶資料庫';
/**
* 需要遷移的表 (依賴順序排列)
*/
protected array $tablesToMigrate = [
'users',
'password_reset_tokens',
'sessions',
'cache',
'cache_locks',
'jobs',
'job_batches',
'failed_jobs',
'categories',
'units',
'vendors',
'products',
'product_vendor',
'warehouses',
'inventories',
'inventory_transactions',
'purchase_orders',
'purchase_order_items',
'permissions',
'roles',
'model_has_permissions',
'model_has_roles',
'role_has_permissions',
];
public function handle(): int
{
$tenantId = $this->argument('tenant_id');
$dryRun = $this->option('dry-run');
// 檢查租戶是否存在
$tenant = Tenant::find($tenantId);
if (!$tenant) {
$this->error("租戶 '{$tenantId}' 不存在!請先建立租戶。");
return self::FAILURE;
}
$this->info("開始遷移資料到租戶: {$tenantId}");
$this->info("租戶資料庫: tenant{$tenantId}");
if ($dryRun) {
$this->warn('⚠️ Dry Run 模式 - 不會實際遷移資料');
}
// 取得 central 資料庫連線
$centralConnection = config('database.default');
$tenantDbName = 'tenant' . $tenantId;
foreach ($this->tablesToMigrate as $table) {
// 檢查表是否存在於 central
if (!Schema::connection($centralConnection)->hasTable($table)) {
$this->line(" ⏭️ 跳過 {$table} (表不存在)");
continue;
}
// 計算資料筆數
$count = DB::connection($centralConnection)->table($table)->count();
if ($count === 0) {
$this->line(" ⏭️ 跳過 {$table} (無資料)");
continue;
}
if ($dryRun) {
$this->info(" 📋 {$table}: {$count} 筆資料");
continue;
}
// 實際遷移資料
$this->info(" 🔄 遷移 {$table}: {$count} 筆資料...");
try {
// 使用租戶上下文執行
$tenant->run(function () use ($centralConnection, $table) {
// 取得 central 資料
$data = DB::connection($centralConnection)->table($table)->get();
if ($data->isEmpty()) {
return;
}
// 關閉外鍵檢查
DB::statement('SET FOREIGN_KEY_CHECKS=0');
// 清空目標表
DB::table($table)->truncate();
// 分批插入 (每批 100 筆)
foreach ($data->chunk(100) as $chunk) {
DB::table($table)->insert($chunk->map(fn($item) => (array) $item)->toArray());
}
// 恢復外鍵檢查
DB::statement('SET FOREIGN_KEY_CHECKS=1');
});
$this->info("{$table} 遷移完成");
} catch (\Exception $e) {
$this->error("{$table} 遷移失敗: " . $e->getMessage());
return self::FAILURE;
}
}
$this->newLine();
$this->info('🎉 資料遷移完成!');
return self::SUCCESS;
}
}