- 注册 AutoConfirmOverdue 命令行任务以处理过期订单。 - 优化 autoConfirm 方法,增强参数提取与数据查询的健壮性。 - 增加确认收货成功与跳过的详细日志,便于后续问题排查。 - 新增 autoConfirmSkipReason 方法,精准区分任务跳过原因。 - 完善异常捕获注释,明确失败时不删除任务以便回流重试。
177 行
5.6 KiB
PHP
177 行
5.6 KiB
PHP
<?php
|
||
|
||
namespace addons\shopro\command;
|
||
|
||
use addons\shopro\model\Config;
|
||
use addons\shopro\model\Order;
|
||
use addons\shopro\model\OrderItem;
|
||
use think\Cache;
|
||
use think\console\Command;
|
||
use think\console\Input;
|
||
use think\console\input\Option;
|
||
use think\console\Output;
|
||
use think\Queue;
|
||
|
||
/**
|
||
* 扫描超期未收货订单,重新推入 shopro 队列执行 OrderAutoOper@autoConfirm
|
||
*/
|
||
class AutoConfirmOverdue extends Command
|
||
{
|
||
protected function configure()
|
||
{
|
||
$this->setName('shopro:auto-confirm-overdue')
|
||
->addOption('dry-run', null, Option::VALUE_NONE, '只列出将入队的订单,不推送队列')
|
||
->addOption('limit', null, Option::VALUE_REQUIRED, '最多处理条数,默认 100', 100)
|
||
->addOption('days', null, Option::VALUE_REQUIRED, '覆盖配置的自动收货天数(不传则读 order_auto_confirm)')
|
||
->addOption('force', null, Option::VALUE_NONE, '忽略短时去重缓存,强制重新入队')
|
||
->setDescription('Re-queue overdue shipped order items into shopro autoConfirm');
|
||
}
|
||
|
||
protected function execute(Input $input, Output $output)
|
||
{
|
||
$dryRun = (bool) $input->getOption('dry-run');
|
||
$force = (bool) $input->getOption('force');
|
||
$limit = max(1, (int) $input->getOption('limit'));
|
||
$days = $this->resolveDays($input);
|
||
|
||
$deadline = time() - ($days * 86400);
|
||
$output->writeln(sprintf(
|
||
'[info] order_auto_confirm=%d day(s), deadline<=%s, limit=%d%s',
|
||
$days,
|
||
date('Y-m-d H:i:s', $deadline),
|
||
$limit,
|
||
$dryRun ? ', dry-run' : ''
|
||
));
|
||
|
||
$items = OrderItem::where('dispatch_status', OrderItem::DISPATCH_STATUS_SENDED)
|
||
->where('dispatch_type', '<>', 'selfetch')
|
||
->where('refund_status', 'not in', [
|
||
OrderItem::REFUND_STATUS_OK,
|
||
OrderItem::REFUND_STATUS_FINISH,
|
||
])
|
||
->order('id', 'asc')
|
||
->select();
|
||
|
||
$queued = 0;
|
||
$skipped = 0;
|
||
$matched = 0;
|
||
|
||
foreach ($items as $item) {
|
||
$sendTime = $this->extractSendTime($item);
|
||
if ($sendTime <= 0) {
|
||
$skipped++;
|
||
continue;
|
||
}
|
||
if ($sendTime > $deadline) {
|
||
continue;
|
||
}
|
||
|
||
$order = Order::get($item['order_id']);
|
||
if (!$order) {
|
||
$output->writeln(sprintf(
|
||
'[SKIP] item#%d order#%d 订单不存在或已删除',
|
||
$item['id'],
|
||
$item['order_id']
|
||
));
|
||
$skipped++;
|
||
continue;
|
||
}
|
||
|
||
$matched++;
|
||
$line = sprintf(
|
||
'order#%d %s item#%d send_time=%s overdue_days=%d',
|
||
$order['id'],
|
||
$order['order_sn'] ?? '',
|
||
$item['id'],
|
||
date('Y-m-d H:i:s', $sendTime),
|
||
(int) floor((time() - $sendTime) / 86400)
|
||
);
|
||
|
||
if ($dryRun) {
|
||
$output->writeln('[dry-run] ' . $line);
|
||
if ($matched >= $limit) {
|
||
break;
|
||
}
|
||
continue;
|
||
}
|
||
|
||
$cacheKey = 'shopro:auto_confirm_requeue:' . $item['id'];
|
||
if (!$force && Cache::get($cacheKey)) {
|
||
$output->writeln('[SKIP-DEDUP] ' . $line);
|
||
$skipped++;
|
||
continue;
|
||
}
|
||
|
||
try {
|
||
// 仅传 id,与 Job 内重新查询一致,避免大对象序列化
|
||
Queue::push(
|
||
'\addons\shopro\job\OrderAutoOper@autoConfirm',
|
||
[
|
||
'order' => ['id' => (int) $order['id']],
|
||
'item' => ['id' => (int) $item['id']],
|
||
],
|
||
'shopro'
|
||
);
|
||
Cache::set($cacheKey, 1, 3600);
|
||
$queued++;
|
||
$output->writeln('[QUEUED] ' . $line);
|
||
} catch (\Exception $e) {
|
||
$output->writeln('[FAIL] ' . $line . ' err=' . $e->getMessage());
|
||
\think\Log::write(
|
||
'shopro:auto-confirm-overdue push fail item=' . $item['id'] . ' ' . $e->getMessage()
|
||
);
|
||
}
|
||
|
||
if ($queued >= $limit) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
$output->writeln(sprintf(
|
||
'[done] matched=%d queued=%d skipped=%d%s',
|
||
$matched,
|
||
$queued,
|
||
$skipped,
|
||
$dryRun ? ' (dry-run, no push)' : ''
|
||
));
|
||
|
||
return 0;
|
||
}
|
||
|
||
protected function resolveDays(Input $input)
|
||
{
|
||
$override = $input->getOption('days');
|
||
if ($override !== null && $override !== false && $override !== '') {
|
||
$days = (int) $override;
|
||
return $days > 0 ? $days : 10;
|
||
}
|
||
|
||
$config = Config::where('name', 'order')->find();
|
||
$cfg = $config ? json_decode($config['value'], true) : [];
|
||
if (!is_array($cfg)) {
|
||
$cfg = [];
|
||
}
|
||
$days = (isset($cfg['order_auto_confirm']) && $cfg['order_auto_confirm'] > 0)
|
||
? (int) $cfg['order_auto_confirm']
|
||
: 10;
|
||
|
||
return $days;
|
||
}
|
||
|
||
protected function extractSendTime($item)
|
||
{
|
||
$ext = [];
|
||
if (!empty($item['ext'])) {
|
||
$decoded = json_decode($item['ext'], true);
|
||
if (is_array($decoded)) {
|
||
$ext = $decoded;
|
||
}
|
||
}
|
||
if (!empty($ext['send_time'])) {
|
||
return (int) $ext['send_time'];
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
}
|