| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- <?php
- namespace App\Console\Commands;
- use App\Services\ThirdGameOrderService;
- use Carbon\Carbon;
- use Illuminate\Console\Command;
- class ThirdGameOrderBackfill extends Command
- {
- protected $signature = 'third-game:backfill-orders
- {--days=15 : 未指定时间时回填最近几天,1-15}
- {--start= : 开始时间,格式 Y-m-d H:i:s}
- {--end= : 结束时间,格式 Y-m-d H:i:s}';
- protected $description = '分段回填 api-bet 最近 15 天内的三方游戏历史订单';
- public function handle(ThirdGameOrderService $service): int
- {
- $timezone = config('app.timezone', 'Asia/Shanghai');
- $now = Carbon::now($timezone);
- $startOption = trim((string) $this->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);
- }
- }
- }
|