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 */ 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(); } }