99 lines
4.5 KiB
PHP
99 lines
4.5 KiB
PHP
<?php
|
||
|
||
namespace App\Console\Commands;
|
||
|
||
use Illuminate\Console\Command;
|
||
use Illuminate\Support\Facades\Log;
|
||
use Modules\EcommerceFinance\Repositories\JushuitanCostPriceHistoryMirrorRepository;
|
||
use Modules\EcommerceFinance\Services\JushuitanCostPriceHistorySynchronizer;
|
||
use Modules\EcommerceFinance\Services\JushuitanCostPriceSnapshotDiffer;
|
||
use Throwable;
|
||
|
||
class SyncJushuitanCostPriceHistory extends Command
|
||
{
|
||
protected $signature = 'business-mirror:sync-jushuitan-cost-price-history
|
||
{--full : 强制启动或继续首次全量同步}
|
||
{--source=open_api : open_api 为正式操作历史;snapshot 为独立的镜像价格观测}
|
||
{--page-size= : 聚水潭接口每页读取条数,最大 50}
|
||
{--max-pages= : 本轮最多读取页数,0 表示直到完成}';
|
||
|
||
protected $description = '按时间窗和页码同步聚水潭商品操作日志中的采购价变更到共享业务镜像';
|
||
|
||
public function handle(
|
||
JushuitanCostPriceHistoryMirrorRepository $mirror,
|
||
JushuitanCostPriceHistorySynchronizer $synchronizer,
|
||
): int {
|
||
if (! (bool) config('upstream_mirrors.refresh_enabled', false) && ! $this->option('full')) {
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
if ($this->option('source') === 'snapshot') {
|
||
try {
|
||
$result = app(JushuitanCostPriceSnapshotDiffer::class)->sync(
|
||
$this->option('page-size') === null ? 500 : (int) $this->option('page-size'),
|
||
$this->option('max-pages') === null ? 20 : (int) $this->option('max-pages'),
|
||
);
|
||
$this->info(sprintf('镜像价格观测:%s,本轮读取 %d 条,发现 %d 次价格变化;不代表完整操作历史。', $result['status'], $result['rows'], $result['changes']));
|
||
return self::SUCCESS;
|
||
} catch (Throwable $exception) {
|
||
Log::error('镜像价格观测失败。', ['exception' => $exception::class]);
|
||
$this->error('镜像价格观测失败,已保留基线、记录和续传游标。');
|
||
return self::FAILURE;
|
||
}
|
||
}
|
||
if ($this->option('source') !== 'open_api') {
|
||
$this->error('source 仅支持 open_api 或 snapshot。');
|
||
return self::FAILURE;
|
||
}
|
||
|
||
try {
|
||
$state = $mirror->state();
|
||
$full = $this->shouldRunFull($state);
|
||
$result = $synchronizer->sync(
|
||
$full,
|
||
$this->option('page-size') === null ? null : (int) $this->option('page-size'),
|
||
$this->option('max-pages') === null ? null : (int) $this->option('max-pages'),
|
||
);
|
||
} catch (Throwable $exception) {
|
||
Log::error('聚水潭历史成本价镜像同步失败。', ['exception' => $exception::class]);
|
||
$this->error(match ($exception->getMessage()) {
|
||
'jushuitan_classic_api_credentials_missing' => '聚水潭经典开放接口凭据未配置,请配置 partnerid、partnerkey 和 token。',
|
||
'jushuitan_classic_api_partner_id_invalid' => '聚水潭经典开放接口 partnerid 无效,请核对同一应用的 partnerid、partnerkey 和 token。',
|
||
default => '聚水潭成本价历史同步失败,已保留上一版共享数据。',
|
||
});
|
||
|
||
return self::FAILURE;
|
||
}
|
||
|
||
if ($result['status'] === 'requires_full_sync') {
|
||
$this->warn('聚水潭成本价历史尚未完成首次全量同步,请配置经典开放接口凭据后显式使用 --full。');
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
$this->info(sprintf(
|
||
'聚水潭成本价操作日志%s:%s,本轮 %d 页,累计扫描 %d 条操作日志、同步 %d 条采购价变更。',
|
||
$result['mode'] === 'full' ? '全量同步' : '增量同步',
|
||
$result['status'] === 'succeeded' ? '完成' : '进行中',
|
||
$result['pages'],
|
||
$result['source_rows'],
|
||
$result['rows'],
|
||
));
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
/** @param array<string,mixed>|null $state */
|
||
private function shouldRunFull(?array $state): bool
|
||
{
|
||
if ((bool) $this->option('full')) {
|
||
return true;
|
||
}
|
||
|
||
// 显式全量中断后由日常任务续传同一固定时间窗,完成前不激活半份数据。
|
||
return is_array($state)
|
||
&& ($state['run_mode'] ?? null) === 'full'
|
||
&& in_array($state['status'] ?? null, ['running', 'failed'], true);
|
||
}
|
||
}
|