ThinkPHP 6 不支持原生链式任务调度,可靠实现需分三层:1. Shell脚本命令级串联(同步依赖);2. 队列级接力(异步解耦);3. 状态驱动型链路(流程指针)。

ThinkPHP 6 本身不支持像 Laravel schedule 那样的链式任务调度语法(如 ->daily()->withoutOverlapping()),所谓“任务链式调用”在 TP6 中并非框架原生能力,而是开发者通过命令组合 + Shell 脚本封装 + 队列接力模拟出的逻辑链条。真正可靠、可维护的“链式执行”,必须拆解为清晰的触发层、执行层与协作层。
✅ 正确理解 TP6 的“链式”本质
不是语法糖,而是流程编排:前一个任务成功后主动触发下一个命令,或通过队列传递数据驱动后续动作。强行在单个 execute() 里串行调多个业务逻辑,既难调试、又无法失败隔离、更无法横向扩展。
? 实现可靠任务链的三个关键层级
1. 命令级串联:用 Shell 脚本控制执行顺序
适合有明确先后依赖、且允许同步等待的场景(如:备份 → 压缩 → 上传 → 清理)。
- 写一个
chain.sh:#!/bin/bash cd /var/www/myapp PHP_BIN=/www/server/php/74/bin/php
步骤1:数据库备份
$PHP_BIN think backup:db --date=$(date +%Y%m%d) >> /runtime/log/backup.log 2>&1 if [ $? -ne 0 ]; then exit 1; fi
步骤2:压缩备份文件
$PHP_BIN think backup:compress --date=$(date +%Y%m%d) >> /runtime/log/compress.log 2>&1 if [ $? -ne 0 ]; then exit 1; fi
步骤3:上传到对象存储
$PHP_BIN think backup:upload --date=$(date +%Y%m%d) >> /runtime/log/upload.log 2>&1
- Cron 调用它:
`0 2 * * * /bin/bash /var/www/myapp/shell/chain.sh`
> ⚠️ 注意:每步用 `$?` 判断上一步退出码,失败立即中断,避免脏数据流转。
**2. 队列级接力:用 `Queue::push()` 触发下一段逻辑**
适合异步、解耦、需失败重试或延迟执行的链路(如:下单 → 库存锁定 → 支付超时检测 → 自动取消)。
- 在 `PlaceOrderJob::fire()` 末尾:
```php
// 成功锁定库存后,投递支付监控任务
$data = ['order_id' => $order['id'], 'expire_at' => time() + 1800];
Queue::push('app\job\MonitorPaymentJob', $data, 'payment');
// 30分钟后若未支付,自动取消订单(利用 Redis zset 延迟)
Queue::later(1800, 'app\job\CancelOrderJob', ['order_id' => $order['id']], 'order');- 每个 Job 独立实现
fire(),各自处理异常并手动delete()或release()。
✅ 优势:任一环节失败不影响上游,可单独重试;天然支持并发与水平扩展。
立即学习“PHP免费学习笔记(深入)”;
3. 状态驱动型链路:用数据库字段做流程指针
适合状态机类长流程(如审核流:提交 → 初审 → 复审 → 发布)。
- 不靠代码调用顺序,而靠轮询或事件监听状态变更:
// 审核通过后,更新 status 并触发下一节点 Db::name('article')->where('id', $id)->update(['status' => 'reviewed']); event('ArticleReviewed', $article); // 触发事件,由监听器启动复审任务 - 对应事件监听器中:
public function handle($event) { Queue::push('app\job\ReviewSecondJob', ['id' => $event->id], 'review'); }
? 关键点:状态变更作为唯一可信信号,避免“代码写死顺序”带来的僵化。
❌ 这些“伪链式”做法要避开
- 在一个
execute()方法里连续调用Db::table()->...、Http::get()、Mail::send()—— 无隔离、无重试、日志混杂、无法单独启停。 - 用
sleep(5)等待外部结果再继续 —— CLI 进程阻塞,超时风险高,资源浪费。 - 把多个任务逻辑硬编码进一个 Job 类的
fire()—— 违反单一职责,测试与复用成本飙升。
不复杂但容易忽略:链式不是写得长,而是断点清晰、失败可见、恢复可控。



















