141 lines
5.2 KiB
PHP
141 lines
5.2 KiB
PHP
<?php
|
||
|
||
namespace App\Console\Commands;
|
||
|
||
use App\Models\Business\CourierAgreementSignature;
|
||
use App\Models\Business\CourierQuoteSignature;
|
||
use App\Services\Esign\EsignClient;
|
||
use App\Support\CourierAgreementSignatureService;
|
||
use App\Support\CourierQuoteSignatureService;
|
||
use Illuminate\Console\Command;
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
use Illuminate\Support\Collection;
|
||
use Illuminate\Support\Facades\Log;
|
||
use Throwable;
|
||
|
||
class PollEsignSignatures extends Command
|
||
{
|
||
protected $signature = 'esign:poll-signatures
|
||
{--limit= : 本轮最多查询的签署流程数}
|
||
{--min-age= : 同一流程两次查询之间的最少秒数}';
|
||
|
||
protected $description = '在不使用回调地址时,定时查询 E 签宝流程状态并归档已签文件';
|
||
|
||
public function handle(
|
||
CourierQuoteSignatureService $quoteSignatures,
|
||
CourierAgreementSignatureService $agreementSignatures,
|
||
): int {
|
||
if (! (bool) config('services.esign.polling_enabled', false)) {
|
||
$this->components->info('E 签宝定时轮询未启用。');
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
if (! EsignClient::configured()) {
|
||
$this->components->warn('E 签宝尚未正确配置,已跳过定时轮询。');
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
$limit = min(max((int) ($this->option('limit')
|
||
?? config('services.esign.poll_batch_size', 25)), 1), 100);
|
||
$minAge = max((int) ($this->option('min-age')
|
||
?? config('services.esign.poll_min_age_seconds', 120)), 0);
|
||
$cutoff = now()->subSeconds($minAge);
|
||
$candidates = $this->candidates($limit, $cutoff);
|
||
$succeeded = 0;
|
||
$failed = 0;
|
||
|
||
foreach ($candidates as $candidate) {
|
||
$model = $candidate['model'];
|
||
|
||
try {
|
||
if ($candidate['type'] === 'quote') {
|
||
$quoteSignatures->synchronize($model, (int) ($model->updated_by ?: $model->created_by));
|
||
} else {
|
||
$agreementSignatures->synchronize($model, (int) ($model->updated_by ?: $model->created_by));
|
||
}
|
||
|
||
$succeeded++;
|
||
} catch (Throwable $exception) {
|
||
$failed++;
|
||
Log::warning('E 签宝签署流程定时查询失败。', [
|
||
'signature_type' => $candidate['type'],
|
||
'signature_id' => $model->getKey(),
|
||
'exception' => $exception::class,
|
||
]);
|
||
}
|
||
}
|
||
|
||
$this->components->info(sprintf(
|
||
'E 签宝轮询完成:候选 %d,成功 %d,失败 %d。',
|
||
$candidates->count(),
|
||
$succeeded,
|
||
$failed,
|
||
));
|
||
|
||
return $failed > 0 ? self::FAILURE : self::SUCCESS;
|
||
}
|
||
|
||
/** @return Collection<int,array{type:string,model:CourierQuoteSignature|CourierAgreementSignature}> */
|
||
private function candidates(int $limit, \DateTimeInterface $cutoff): Collection
|
||
{
|
||
$environment = strtolower(trim((string) config('services.esign.environment', 'sandbox')));
|
||
|
||
$quotes = CourierQuoteSignature::allCompanies()
|
||
->where('environment', $environment)
|
||
->whereNotNull('esign_sign_flow_id')
|
||
->where('esign_sign_flow_id', '<>', '')
|
||
->whereIn('status', [
|
||
CourierQuoteSignature::STATUS_SIGNING,
|
||
CourierQuoteSignature::STATUS_COMPLETED,
|
||
])
|
||
->where(fn (Builder $query) => $query
|
||
->whereNull('last_synced_at')
|
||
->orWhere('last_synced_at', '<=', $cutoff))
|
||
->orderByRaw('CASE WHEN last_synced_at IS NULL THEN 0 ELSE 1 END')
|
||
->orderBy('last_synced_at')
|
||
->orderBy('id')
|
||
->limit($limit)
|
||
->get()
|
||
->map(fn (CourierQuoteSignature $signature): array => [
|
||
'type' => 'quote',
|
||
'model' => $signature,
|
||
]);
|
||
|
||
$agreements = CourierAgreementSignature::allCompanies()
|
||
->disableCache()
|
||
->where('environment', $environment)
|
||
->whereNotNull('esign_sign_flow_id')
|
||
->where('esign_sign_flow_id', '<>', '')
|
||
->whereIn('status', [
|
||
CourierAgreementSignature::STATUS_SIGNING,
|
||
CourierAgreementSignature::STATUS_UNKNOWN,
|
||
CourierAgreementSignature::STATUS_COMPLETED,
|
||
])
|
||
->where(fn (Builder $query) => $query
|
||
->whereNull('last_synced_at')
|
||
->orWhere('last_synced_at', '<=', $cutoff))
|
||
->orderByRaw('CASE WHEN last_synced_at IS NULL THEN 0 ELSE 1 END')
|
||
->orderBy('last_synced_at')
|
||
->orderBy('id')
|
||
->limit($limit)
|
||
->get()
|
||
->map(fn (CourierAgreementSignature $signature): array => [
|
||
'type' => 'agreement',
|
||
'model' => $signature,
|
||
]);
|
||
|
||
return $quotes
|
||
->concat($agreements)
|
||
->sortBy(fn (array $candidate): string => sprintf(
|
||
'%020d:%s:%020d',
|
||
$candidate['model']->last_synced_at?->getTimestamp() ?? 0,
|
||
$candidate['type'],
|
||
$candidate['model']->getKey(),
|
||
))
|
||
->take($limit)
|
||
->values();
|
||
}
|
||
}
|