73 lines
4.6 KiB
PHP
73 lines
4.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace app\command;
|
|
|
|
use app\adminapi\logic\tcm\MobileRecordingMediaLogic;
|
|
use app\common\service\mobilerecording\MobileRecordingObjects;
|
|
use app\common\service\mobilerecording\MobileRecordingWork;
|
|
use think\console\Command;
|
|
use think\console\Input;
|
|
use think\console\Output;
|
|
use think\console\input\Option;
|
|
use think\facade\Db;
|
|
|
|
/** Schedule once per minute with a non-overlap lock; contains no cloud credentials in arguments. */
|
|
final class MobileRecordingCosWork extends Command
|
|
{
|
|
protected function configure()
|
|
{
|
|
$this->setName('mobile-recording:cos-work')->setDescription('推进手机录音COS提交及私有对象删除重试')
|
|
->addOption('dry-run', null, Option::VALUE_NONE, '只统计,不调用COS')
|
|
->addOption('limit', null, Option::VALUE_REQUIRED, '最多处理的上传数,1至100', '5')
|
|
->addOption('seconds', null, Option::VALUE_REQUIRED, '上传调度时间预算,1至300秒;单个COS请求仍有10秒超时', '20');
|
|
}
|
|
protected function execute(Input $input, Output $output): int
|
|
{
|
|
try {
|
|
$limit = max(1, min(100, (int) $input->getOption('limit')));
|
|
$seconds = max(1, min(300, (int) $input->getOption('seconds')));
|
|
$dry = (bool) $input->getOption('dry-run');
|
|
$deadline = microtime(true) + $seconds;
|
|
$counts = MobileRecordingObjects::cleanup(1, $dry);
|
|
$counts += ['uploads_selected' => 0, 'uploads_completed' => 0, 'uploads_deferred' => 0,
|
|
'uploads_denied' => 0, 'staging_cleaned' => 0];
|
|
$rows = Db::name('mobile_recording_upload')->alias('u')
|
|
->join([Db::name('mobile_recording_binding')->getTable() => 'b'], 'b.id=u.binding_id')
|
|
->leftJoin([Db::name('mobile_recording_object')->getTable() => 'o'], 'o.upload_id=u.upload_id')
|
|
->leftJoin([Db::name('mobile_recording_asset')->getTable() => 'a'], 'a.upload_id=u.upload_id AND a.binding_id=u.binding_id AND a.kind=u.kind')
|
|
->leftJoin([Db::name('mobile_recording_work')->getTable() => 'w'], 'w.upload_id=u.upload_id')
|
|
->whereRaw("((u.state='uploading' AND u.received_bytes=u.size_bytes) OR (u.state='complete' AND o.state IN ('pending','finalizing','verifying')) OR (u.state='complete' AND o.state='ready' AND a.upload_id IS NOT NULL AND COALESCE(w.staging_cleaned,0)=0))")
|
|
->whereRaw('COALESCE(w.next_retry_at,0) <= ' . time())
|
|
->field('u.upload_id,u.state,o.state AS object_state,b.admin_id,b.diagnosis_id')
|
|
->orderRaw('COALESCE(w.last_attempt_at,0),u.updated_at,u.upload_id')->limit($limit)->select()->toArray();
|
|
$counts['uploads_selected'] = count($rows);
|
|
if (!$dry) {
|
|
foreach ($rows as $row) {
|
|
if (microtime(true) >= $deadline) { break; }
|
|
try {
|
|
if ($row['state'] === 'complete' && $row['object_state'] === 'ready') {
|
|
if (!MobileRecordingObjects::cleanupStaging($row['upload_id'], true)) { throw new \DomainException('Media publication no longer active'); }
|
|
$counts['staging_cleaned']++;
|
|
} elseif ($row['state'] === 'complete') { MobileRecordingMediaLogic::migrateLocal($row['upload_id']); $counts['uploads_completed']++; }
|
|
else { MobileRecordingMediaLogic::complete(['upload_id' => $row['upload_id'], 'diagnosis_id' => (int) $row['diagnosis_id']], (int) $row['admin_id']); }
|
|
if ($row['state'] === 'uploading') {
|
|
// complete may publish successfully while best-effort unlink failed. Keep that task retryable.
|
|
if (!MobileRecordingObjects::cleanupStaging($row['upload_id'], true)) { throw new \DomainException('Media publication no longer active'); }
|
|
$counts['uploads_completed']++;
|
|
}
|
|
MobileRecordingWork::attempted($row['upload_id']);
|
|
} catch (\Throwable $e) {
|
|
MobileRecordingWork::attempted($row['upload_id'], $e);
|
|
$counts['uploads_deferred']++;
|
|
if ($e instanceof \DomainException) { $counts['uploads_denied']++; }
|
|
}
|
|
}
|
|
}
|
|
$output->writeln('MOBILE_RECORDING_COS_WORK ' . json_encode($counts));
|
|
return $counts['errors'] > 0 || $counts['uploads_deferred'] > 0 ? 1 : 0;
|
|
} catch (\Throwable $e) { $output->writeln('MOBILE_RECORDING_COS_WORK storage_or_configuration_error'); return 1; }
|
|
}
|
|
}
|