Laravel取消超时订单的解决方案(延迟分发)

由 Jefsky 发布于 2024-03-05

需求是这样的

很多时候用户下单之后不会立刻结账付款,把订单一直挂着,甚至有些用户久而久之把订单忘记了。这样会导致库存一直被占用,经营无法正常进行了。所以,建议给用户订单一个超时时间,够钟就系统自动取消订单。

大概思想

用Laravel的延迟分发来制作,用户下单之后,开始出发延时分发,例如30分钟,时间到就执行,如果已经支付了就return,没有就取消订单,恢复库存和优惠券。感觉这个方法比之前的定时要好很多,有新订单就执行,没有就算,而且时效性的问题也解决了。

动手

用命令创建一个job

php artisan make:job CancleOrder

修改环境文件.env中队列配置项指定为 redis

QUEUE_CONNECTION=redis 如果是QUEUE_CONNECTION=sync就是同步了 对了,你也需要准备好redis环境先

编辑app/job/CancleOrder文件

<?php namespace App\Jobs;

use App\Models\ShopOrder;
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\Log;

/**
 * CloseOrder
 * @namespace App\Jobs
 * @date 2024/3/5
 * @author Jefsky
 */
class CloseOrder implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $order;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct(ShopOrder $order, $delay)
    {
        // 这里接收的是订单的实例
        $this->order = $order;
        // 核心配置:设置延迟的时间,delay()方法的参数代表多少秒之后执行
        $this->delay($delay);
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // 判断对应的订单是否已经被支付,如果已经支付则不需要关闭订单,直接退出
        if (!empty($this->order->pay_status)) {
            Log::info($this->order->id . '订单已支付无需操作' . time());
            return;
        }

        // 通过事务执行 sql
        // 取消订单
        $this->order->status = '-1';
        $this->order->save();
        // 恢复库存、优惠券
        ...cancelData...
        if ($this->order->coupon_id) {
           ...
        }
        Log::info($this->order->id . '系统取消订单执行' . time());
    }
}

添加超时时间

config/app.php中添加超时时间 'order_ttl' => 1800,// 订单超时时间

触发

在创建订单的业务上加上延迟分发的任务 use App\Jobs\CloseOrder;

/**
     * 新增订单
     * @param $insert_data
     * @return mixed
     */
    public static function addOrder($data)
    {
        $order = new ShopOrder();
        ...
        $order->save();
        // 调用延迟队列任务,超时30分钟未支付关闭订单,恢复库存
        dispatch(new CloseOrder($lib, config('app.order_ttl')));
        return $order->id;
    }

安装predis依赖

使用 redis 作为队列驱动,还需要引入 predis/predis 依赖 composer require predis/predis

启动队列处理器

php artisan queue:work 每次修改任务执行程序,都需要先停止队列,修改完成后再重新启动。

如果想队列处理器进程一直在后台运行,可以写入到守护进程中,如Supervisor