option('full')) { return self::SUCCESS; } $requested = $this->argument('dataset'); $keys = $requested === null ? $this->scheduledKeys($datasets, $mirror) : [(string) $requested]; $maxDatasets = max((int) ($this->option('max-datasets') ?? config('upstream_mirrors.table_sync.max_datasets_per_run', 1)), 1); $processed = 0; $failed = false; foreach ($keys as $key) { if ($processed >= $maxDatasets) { break; } try { $result = $synchronizer->sync( $key, (bool) $this->option('full'), $this->option('batch') === null ? null : (int) $this->option('batch'), $this->option('max-batches') === null ? null : (int) $this->option('max-batches'), ); } catch (Throwable $exception) { $failed = true; Log::error('共享业务镜像表同步失败。', ['dataset' => $key, 'exception' => $exception::class]); $this->error($key . ':同步失败,已保留上一版共享数据。'); $processed++; continue; } if ($result['status'] === 'requires_full_sync') { if ($requested !== null) { $this->warn($key . ':尚未完成首次全量同步,请显式使用 --full。'); } continue; } $processed++; $this->info(sprintf( '%s:%s,本轮 %d 批,累计处理 %d 行。', $key, $result['status'] === 'succeeded' ? '同步完成' : '同步进行中', $result['batches'], $result['rows'], )); } return $failed ? self::FAILURE : self::SUCCESS; } /** @return list */ private function scheduledKeys( BusinessMirrorDatasetRegistry $datasets, GenericBusinessMirrorRepository $mirror, ): array { $scheduled = []; $dedicated = array_fill_keys(array_map( 'strval', (array) config('upstream_mirrors.table_sync.dedicated_datasets', []), ), true); foreach ($datasets->all() as $key => $dataset) { if (isset($dedicated[$key])) { continue; } $state = $mirror->state($dataset); if (! is_array($state) || ($state['initial_completed_at'] ?? null) === null) { continue; } $scheduled[] = [ 'key' => $key, 'last_attempted_at' => (string) ($state['last_attempted_at'] ?? ''), ]; } usort($scheduled, fn (array $left, array $right): int => [$left['last_attempted_at'], $left['key']] <=> [$right['last_attempted_at'], $right['key']] ); return array_column($scheduled, 'key'); } }