文章目录
前情提要
最近使用 Laravel 为一个带有订单支付的微信小程序提供后端 API 的开发。
客户提出如果用户创建订单后 30 分钟仍未支付就要关闭该订单并恢复商品sku的库存。
经过考虑,决定使用 延迟分发
的队列任务实现该功能。
什么是延迟分发
例如,让我们指定调度任务在 10 分钟后他被调度后才执行,在这之前任务不会被执行
如何实现延迟分发
我们只需在定义调度任务的时候,使用 Laravel 自带的 delay
方法即可实现。
实现步骤(真实代码)
确保安装 redis 并且项目正常可用
将 .env 中队列配置项指定为 redis
QUEUE_CONNECTION=redis
在项目 app 目录下创建 Jobs 子目录并创建 CloseOrder.php 实现延迟队列
<?php
namespace App\Jobs;
use App\Models\Order;
use App\Models\ProductSku;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
/**
* 30分钟未支付,关闭订单
*
* 代表这个类需要被队列中执行,而不是触发时立即执行
*
* Class CloseOrder
* @package App\Jobs
*/
class CloseOrder implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $order;
/**
* Create a new job instance.
*
* @param Order $order
* @param $delay 1800s
* @return void
*/
public function __construct(Order $order, $delay)
{
// 这里接收的是订单的实例
$this->order = $order;
// 核心配置:设置延迟的时间,delay()方法的参数代表多少秒之后执行
$this->delay($delay);
}
/**
* 定义这个任务类具体的执行逻辑
* 当队列处理器从队列中取出任务时,会调用 handle() 方法
* Execute the job.
*
* @return void
*/
public function handle()
{
// 判断对应的订单是否已经被支付,如果已经支付则不需要关闭订单,直接退出
if ($this->order->pay_at) {
return;
}
// 通过事务执行 sql
DB::transaction(function () {
// 将订单的 closed 字段标记为2或指定值, 即关闭订单
$this->order->update(['closed' => 2]);
// 将订单中的库存数量加回到 SKU 的库存中去
ProductSku::where('id', $this->order->sku_id)->increment('stock', $this->order->product_number);
}
);
}
}
创建订单时调用上面的延迟调度任务
namespace App\Http\Controllers;
use App\Jobs\CloseOrder;
// 控制器逻辑
public function wechatPay(Request $request)
{
// 事务创建订单,减库存等操作,这里忽略
$order = DB::transaction(function(){
......
});
// 调用微信小程序统一下单接口
$result = WxApp::MiniPay()->jsapi($data);
// 调用延迟队列任务,超时30分钟未支付关闭订单,恢复库存
dispatch(new CloseOrder($order, config('app.order_ttl')));
// 返回给小程序,小程序调起微信支付
return $this->success($result);
}
运行队列处理器
php artisan queue:work
技巧:为了让 queue:work
进程永久地在后台运行,您应该使用一个进程监视器,如 Supervisor,以确保队列 worker 不会停止运行。
Supervisor 的配置除官方文档介绍以外,也可参考我的另一篇文章:Centos7 x64搭建Web Server(PHP,Nginx,Mysql5.7/8,Redis,Supervisor) 找到 Supervisor安装与配置章节即可。
结论
通过以上的代码完美了实现了客户指定的功能,此类应用场景在业务开发中还有很多,转变一下思路都可以使用队列实现,这里不得不说 Laravel 已经封装好的队列模块确实强大。