文件
xionggeshop/addons/shopro/command/AutoConfirmOverdue.php
可乐加冰 c264a7d30c feat(order): 新增自动确认过期命令并优化确认收货队列逻辑
- 注册 AutoConfirmOverdue 命令行任务以处理过期订单。
- 优化 autoConfirm 方法,增强参数提取与数据查询的健壮性。
- 增加确认收货成功与跳过的详细日志,便于后续问题排查。
- 新增 autoConfirmSkipReason 方法,精准区分任务跳过原因。
- 完善异常捕获注释,明确失败时不删除任务以便回流重试。
2026-11-05 08:50:34 +08:00

177 行
5.6 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

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