90 lines
4.1 KiB
PHP
90 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Modules\EcommerceFinance\Services\BusinessDataPlacement;
|
|
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
|
|
use Throwable;
|
|
|
|
class BusinessDataPlan extends Command
|
|
{
|
|
protected $signature = 'business-data:plan {--json : 输出机器可读 JSON}';
|
|
protected $description = '只读核对业务镜像数据集的页面用途、当前体量和目标存储层';
|
|
|
|
public function handle(BusinessMirrorDatasetRegistry $datasets, BusinessDataPlacement $placements): int
|
|
{
|
|
try {
|
|
$registered = $datasets->all();
|
|
$policies = $placements->policies();
|
|
$codeOwned = array_keys((array) config('upstream_mirrors.datasets', []));
|
|
$missing = array_values(array_diff($codeOwned, array_keys($policies)));
|
|
$unknown = array_values(array_diff(array_keys($policies), $codeOwned));
|
|
$sizes = $this->tableSizes($placements);
|
|
$rows = [];
|
|
|
|
foreach ($registered as $key => $dataset) {
|
|
$policy = in_array($key, $missing, true)
|
|
? ['role' => 'unclassified', 'consumer' => '', 'target_store' => 'unclassified', 'sync' => 'unknown']
|
|
: $placements->policy($key);
|
|
$size = $sizes[$dataset->targetTable()] ?? ['rows' => null, 'bytes' => null];
|
|
$state = $placements->state($key);
|
|
$rows[] = [
|
|
'dataset' => $key,
|
|
'table' => $dataset->targetTable(),
|
|
'role' => $policy['role'] ?? 'unclassified',
|
|
'consumer' => $policy['consumer'] ?? '',
|
|
'target_store' => $policy['target_store'] ?? 'unclassified',
|
|
'sync' => $policy['sync'] ?? 'always',
|
|
'estimated_rows' => $size['rows'],
|
|
'size_mib' => $size['bytes'] === null ? null : round($size['bytes'] / 1024 / 1024, 1),
|
|
'migration_phase' => $state['phase'] ?? 'not_started',
|
|
];
|
|
}
|
|
|
|
$payload = ['datasets' => $rows, 'missing_policies' => $missing, 'unknown_policies' => $unknown];
|
|
if ($this->option('json')) {
|
|
$this->line(json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
|
|
} else {
|
|
$this->table(
|
|
['数据集', '当前表', '角色', '页面/用途', '目标层', '同步', '估算行数', 'MiB', '迁移阶段'],
|
|
array_map(static fn (array $row): array => array_values($row), $rows),
|
|
);
|
|
if ($missing !== []) {
|
|
$this->error('缺少存储策略:' . implode(', ', $missing));
|
|
}
|
|
if ($unknown !== []) {
|
|
$this->error('存在无对应数据集的存储策略:' . implode(', ', $unknown));
|
|
}
|
|
}
|
|
|
|
return $missing === [] && $unknown === [] ? self::SUCCESS : self::FAILURE;
|
|
} catch (Throwable $exception) {
|
|
report($exception);
|
|
$this->error('无法读取业务数据分层计划,请确认共享系统库和业务镜像库连接可用。');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
}
|
|
|
|
/** @return array<string,array{rows:?int,bytes:?int}> */
|
|
private function tableSizes(BusinessDataPlacement $placements): array
|
|
{
|
|
$connection = $placements->connection();
|
|
if ($connection->getDriverName() !== 'mysql') {
|
|
return [];
|
|
}
|
|
|
|
$database = $connection->getDatabaseName();
|
|
return $connection->table('information_schema.TABLES')
|
|
->where('TABLE_SCHEMA', $database)
|
|
->get(['TABLE_NAME', 'TABLE_ROWS', 'DATA_LENGTH', 'INDEX_LENGTH'])
|
|
->mapWithKeys(static fn (object $row): array => [
|
|
(string) $row->TABLE_NAME => [
|
|
'rows' => $row->TABLE_ROWS === null ? null : (int) $row->TABLE_ROWS,
|
|
'bytes' => (int) $row->DATA_LENGTH + (int) $row->INDEX_LENGTH,
|
|
],
|
|
])->all();
|
|
}
|
|
}
|