|
|
<?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;
|
|
|
}
|
|
|
}
|