You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
3.9 KiB

1 day ago
<?php
namespace App\Console\Commands;
use App\Services\PeopleCounting\PeopleCountingSyncService;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
use InvalidArgumentException;
use Throwable;
class PeopleCountingBackfillCommand extends Command
{
use PeopleCountingDateRange;
protected $signature = 'people-counting:backfill
{--from= : 起始 Y-m-d默认 2026-08-03}
{--to= : 结束 Y-m-d默认昨天}
{--skip-daily : 不回填日合计}
{--skip-hourly : 不回填小时}
{--include-empty : 小时全 0 且 dataSource=memory 也写入}';
protected $description = '按日回填客流日合计与小时桶(天与天之间 sleep覆盖 upsert';
public function handle(PeopleCountingSyncService $sync): int
{
$fromRaw = trim((string) $this->option('from'));
$toRaw = trim((string) $this->option('to'));
try {
$start = $this->parsePeopleCountingDay($fromRaw !== '' ? $fromRaw : '2026-08-03');
$end = $toRaw !== ''
? $this->parsePeopleCountingDay($toRaw)
: Carbon::yesterday()->startOfDay();
if ($start->gt($end)) {
throw new InvalidArgumentException('--from 不能晚于 --to');
}
} catch (InvalidArgumentException $e) {
$this->error($e->getMessage());
return self::FAILURE;
}
$doDaily = ! (bool) $this->option('skip-daily');
$doHourly = ! (bool) $this->option('skip-hourly');
$skipIfAllZero = ! (bool) $this->option('include-empty');
$sleepMs = $this->peopleCountingSleepMs();
$today = Carbon::today()->toDateString();
$fail = 0;
$this->info(sprintf(
'backfill %s ~ %s daily=%s hourly=%s',
$start->toDateString(),
$end->toDateString(),
$doDaily ? 'yes' : 'no',
$doHourly ? 'yes' : 'no'
));
for ($day = $start->copy(); $day->lte($end); $day->addDay()) {
$d = $day->toDateString();
if ($d > $today) {
$this->warn("跳过未来日期 {$d}");
continue;
}
if ($doDaily) {
try {
$r = $sync->syncDailyForDate($day, true);
$this->line(sprintf(' daily %s written=%d skipped=%d', $r['date'], $r['written'], $r['skipped']));
Log::info('people-counting.backfill.daily', $r);
} catch (Throwable $e) {
$fail++;
$this->error(" daily {$d} 失败: ".$e->getMessage());
Log::warning('people-counting.backfill.daily.failed', [
'date' => $d,
'error' => $e->getMessage(),
]);
}
}
if ($doHourly) {
try {
$r = $sync->syncHourlyForDate($day, $skipIfAllZero);
$this->line($r['skipped']
? sprintf(' hourly %s skipped empty memory dataSource=%s', $r['date'], $r['data_source'] ?? 'null')
: sprintf(' hourly %s written=%d dataSource=%s', $r['date'], $r['written'], $r['data_source'] ?? 'null'));
Log::info('people-counting.backfill.hourly', $r);
} catch (Throwable $e) {
$fail++;
$this->error(" hourly {$d} 失败: ".$e->getMessage());
Log::warning('people-counting.backfill.hourly.failed', [
'date' => $d,
'error' => $e->getMessage(),
]);
}
}
if ($day->copy()->addDay()->lte($end) && $sleepMs > 0) {
usleep($sleepMs * 1000);
}
}
return $fail > 0 ? self::FAILURE : self::SUCCESS;
}
}