feat(notice): timer 异步投递消费者与超时回收任务

This commit is contained in:
augushong
2026-10-12 12:41:03 +00:00
parent d4004f2f1b
commit ca16cd56de
5 changed files with 314 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace base\tools\controller\timer;
use app\common\controller\TimerController;
use base\admin\model\SystemNoticeTargetBase;
use think\facade\Db;
/**
* 通知投递超时回收任务(Base 层).
*
* 消费者崩溃后 status=1(发送中)的行会永久卡住,本任务按抢占时间
* 超时回收重置回待发送,交由消费者重新抢占(at-least-once 语义,
* 渠道驱动幂等性由发送侧业务兜底)。
*
* 回收阈值必须大于消费者单条最大处理时长,否则会误回收正在处理的行
* (超时重投的重复消费比永久卡行好处理:回写 WHERE 含 claim_token,
* 旧令牌回写自动失效)。
*
* 回收本身是原子条件 UPDATE,幂等无害;多节点部署 run_type=auto
* 单节点跑即可(回收与消费者无节点亲和)。
*/
class NoticeReclaimBase extends TimerController
{
/**
* 防刷间隔(秒):回收是低频兜底任务,防外部重复请求.
*/
protected $frequency = 60;
/**
* 并发上限 cap:单分片(id=0),回收全局单份执行.
*/
protected $concurrency = 1;
/**
* 回收阈值(秒):必须大于消费者单条最大处理时长.
*/
protected const RECLAIM_TIMEOUT = 600;
public function do()
{
$affected = Db::name('system_notice_target')
->where('status', SystemNoticeTargetBase::STATUS_SENDING)
->where('claim_time', '<', time() - self::RECLAIM_TIMEOUT)
->update([
'status' => SystemNoticeTargetBase::STATUS_PENDING,
'claim_token' => '',
'claim_time' => 0,
'update_time' => time(),
]);
return 'reclaimed: ' . $affected;
}
}