应建 pending_reviews 表存储失败任务并监听 jobfailed 事件写入,审核时用 queue::pushraw() 重投原始 payload,不可改 failed_jobs 表或直接反序列化执行。

队列任务失败后怎么进人工审核队列
直接把失败任务塞进一个专门的「待审核」队列,而不是原地重试或丢弃。Laravel 默认的 failed_jobs 表只存日志,不支持人工干预;得自己建表、写逻辑,让失败任务可查、可操作、可重发。
- 新建数据表
pending_reviews,字段至少包含:job_id(关联原始 job)、payload(JSON 字符串,保留原始任务内容)、exception(错误堆栈)、attempts、created_at - 在
App\Providers\AppServiceProvider的boot()里监听Illuminate\Queue\Events\JobFailed事件,把失败任务写入该表,同时调用event(new JobFailedForReview($job))触发自定义流程 - 别依赖
php artisan queue:retry—— 它只对failed_jobs表生效,你的审核记录不在那儿
怎么从审核页安全地重试指定任务
不能直接反序列化 payload 后调用 dispatchNow(),容易绕过中间件、丢失上下文、甚至执行两次。必须重建 Laravel 的完整队列调度路径。
- 审核页提交时,只传
pending_review_id,后端查出对应记录,提取payload并用Queue::pushRaw()重新投递到目标队列(如default或high) - 确保
payload中的displayName和job字段正确,否则会找不到对应类;常见坑是任务类用了相对命名空间,或没加use导致反序列化失败 - 重试前检查该任务是否已被人工标记为「忽略」或「已处理」,避免重复触发
为什么不能直接修改 failed_jobs 表来“假装没失败”
因为 failed_jobs 是只读归档表,Laravel 的重试机制靠的是 queue:retry 命令读取它再调用 pushRaw,但这个过程不会还原原始尝试次数、也不会跳过重试限制(比如 tries=3),强行改表会导致后续重试被拒绝。
-
queue:retry all会跳过已超过最大重试次数的任务,哪怕你手动清空了attempts字段 -
failed_jobs.payload里的数据可能已被 base64 编码或加密(取决于队列驱动和配置),直接编辑易损坏结构 - 更重要的是:业务上需要留痕——谁在什么时候批准了哪次重试,这和
failed_jobs的设计目标根本不同
Laravel 10+ 需特别注意的兼容点
10.x 开始,JobFailed 事件的 $job 属性不再直接暴露 payload,得用 $job->job->getRawBody() 或 $job->job->resolveName() 来获取,否则取出来的是空字符串或报错。
- 如果你用的是
database队列驱动,jobs表的attempts字段在失败后不会自动清零,重试时仍会累加,需在写入pending_reviews前显式重置 -
redis驱动下,payload是 JSON 格式但含换行和缩进,入库前建议json_encode(json_decode($payload), JSON_UNESCAPED_UNICODE)归一化 - 别忘了给审核后台加权限控制——不是所有用户都能看到或重试失败任务
人工审核这事,核心不是“怎么重试”,而是“怎么让重试行为可追溯、可授权、不越界”。表结构、事件绑定、重投方式,三处漏掉任何一环,都会变成线上隐患。











