option('start')); $endOption = trim((string) $this->option('end')); if (($startOption === '') !== ($endOption === '')) { $this->error('--start 和 --end 必须同时提供'); return self::FAILURE; } try { if ($startOption !== '') { $start = Carbon::createFromFormat('Y-m-d H:i:s', $startOption, $timezone); $end = Carbon::createFromFormat('Y-m-d H:i:s', $endOption, $timezone); } else { $days = (int) $this->option('days'); if ($days < 1 || $days > 15) { $this->error('--days 必须是 1-15'); return self::FAILURE; } $end = $now->copy(); $start = $end->copy()->subDays($days); if ($days === 15) { $start->addMinute(); } } } catch (\Throwable $e) { $this->error('时间格式错误,应为 Y-m-d H:i:s'); return self::FAILURE; } if ($start->gte($end)) { $this->error('开始时间必须小于结束时间'); return self::FAILURE; } if ($start->diffInSeconds($end) > 15 * 24 * 60 * 60) { $this->error('历史回填范围不能超过15天'); return self::FAILURE; } if ($start->lt($now->copy()->subDays(15))) { $this->error('只能回填最近15天内的订单'); return self::FAILURE; } $cursor = $start->copy(); $totalSynced = 0; $windowCount = 0; $this->info(sprintf( '开始回填三方订单:%s 至 %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s') )); while ($cursor->lt($end)) { $windowEnd = $cursor->copy()->addHours(6); if ($windowEnd->gt($end)) { $windowEnd = $end->copy(); } $page = 1; do { $result = $this->syncWithRateLimitRetry( $service, $cursor->format('Y-m-d H:i:s'), $windowEnd->format('Y-m-d H:i:s'), $page ); if ($result === null) { return self::FAILURE; } $totalSynced += (int) ($result['synced'] ?? 0); $this->line(sprintf( '%s - %s page=%d received=%d synced=%d unmatched=%d', $cursor->format('Y-m-d H:i:s'), $windowEnd->format('Y-m-d H:i:s'), $page, (int) ($result['received'] ?? 0), (int) ($result['synced'] ?? 0), (int) ($result['unmatched_orders'] ?? 0) )); if (empty($result['has_more'])) { break; } if ($page >= 1000) { $this->error('单个时间段分页超过1000页,已停止回填'); return self::FAILURE; } sleep(10); $page++; } while (true); $windowCount++; $cursor = $windowEnd; } $this->info("历史订单回填完成:{$windowCount} 个时间段,写入或更新 {$totalSynced} 条"); return self::SUCCESS; } private function syncWithRateLimitRetry( ThirdGameOrderService $service, string $startTime, string $endTime, int $page ): ?array { while (true) { try { $result = $service->syncHistory($startTime, $endTime, $page, 2000); } catch (\Throwable $e) { $this->error($e->getMessage()); return null; } if (!empty($result['ok'])) { return $result; } $message = (string) ($result['msg'] ?? '三方订单同步失败'); if (!preg_match('/请在(\d+)秒后重试/u', $message, $matches)) { $this->error($message); return null; } $waitSeconds = max(1, (int) $matches[1] + 1); $this->warn("{$message},命令将在 {$waitSeconds} 秒后自动继续"); sleep($waitSeconds); } } }