30 分钟未付款取消订单,怎么做?

30 分钟未付款取消订单,怎么做?

大家好,又见面了,我是全栈君。

点击上方“ 码农编程进阶笔记 ”,选择“置顶或者星标

文末有干货,每天定时与您相约!

第一次亲密接触

问题:我这边有个需求,用户下单后 30 分钟如果没付款就取消掉,这个要怎么写呀。

qufo: 这个还不简单,写个取消订单的命令,弄个计划任务定时不就行了。

舞飞杨:哦,就是 crontab ?

qufo: 是呀,follow me

先来个

$php artisan make:command OrderCancel
Console command created successfully.

然后修改 app\Console\Commands\OrderCancel.php 为如下:

<?php
namespace App\Console\Commands;
use App\Http\Models\Order;
use Illuminate\Console\Command;


class OrderCancel extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'order:cancel';
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '30分钟未付款取消订单';


    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
{
        parent::__construct();
    }




    /**
     * Execute the console command.
     *
     * @return mixed
     * @throws
     */
    public function handle()
{
        try {
            $unPaid = Order::where('created','<',time()-30*60) //创建时间在30分钟以前
            ->where('order_status',1) // 刚下单未支付
            ->get();
            foreach ($unPaid as $order) {
                $order->cancel(); // 执行取消动作
            }
        } catch (\Exception $e) {
            throw $e;
        }
        return true;
    }
}

试一下在项目根下执行 php artisan list 应该能看到下面那一行了。

order
order:cancel   30分钟未付款取消订单

直接执行命令 php artisan order:cancel 即可测试本地取消订单。

执行系统命令 crontab -e , 在里面加入

* * * * * cd /项目的根目录 && php artisan schedule:run >> /dev/null 2>&1

然后 app\Console\Kernel.php 的 schedule 方法里,加入下面一行:

$schedule->command('order:cancel')->everyMinute();

这样,取消订单就会每分钟自动执行一次了,省事了。

代志大条了

舞飞杨:上次那个计划任务真不错,可以自动执行,可是近来订单增多,经常是前一个任务还没执行完下一个任务又开始启动了,然后锁着表改不了数据更惨了。

qufo: 那是,业务量小的时候这个方案好用方便,可是业务量大了,重入会出问题;而且定时任务涉及到 crontab 的权限控制问题。订单量大一点就不好用了。而且,因为我们的任务每分钟执行一次,所以有些订单会在 30 分钟的时候执行取消,有些会在接近 31 分的时候执行。就算没订单,一天也重复执行 1440 次。随着业务的扩展,除了取消订单,还会有提醒支付,催商家发货,催用户确认收货,催骑手接单等等一堆事情,这些加进去,计划任务越来越庸肿,执行效率大大降低,搞不好容易出大事。

舞飞杨:对呀对呀,现在的计划任务已经有 20 多个了,再加进去不是办法呀。之前的任务现在执行得乱 78 糟,全乱套了。现在还有什么好办法么?

qufo:有倒是有,不过我需要你有用过一样东西。

舞飞杨:你要什么?流氓。

qufo: 什么流氓,我说要用 redis 。

舞飞杨:哦,我知道,我装过,用过一阵子,不过,这有什么关系?

qufo:在订单确认成功之后,往 redis 里加入 key, 用 ORDER_CONFIRM:订单ID 这样的格式来,然后定义他 30 分钟后过期,我们监听这个键过期事件就好了。

先保证 redis 的版本大于 2.8 ,现在绝大部分不成问题了,然后修改 redis 的配置文件,加入

notify-keyspace-events "Ex"

以启用键过期的通知。

然后重新启动 redis 。

在 .env 里,确认 CACHE_DRIVER=redis ,并配置好相应的服务地址,密码之类的。

然后,在控制器中,处理好订单确认写入数据库后,增加一行

// 30分钟后过期--执行取消订单
Cache::store('redis')->put('ORDER_CONFIRM:'.$order->id,$order->id,30);

然后我们来监听 ORDER_CONFIRM:ORDER_ID 的过期事件

先建个命令,我们一会儿的监听全靠他了。

$php artisan make:command OrderExpireListen
Console command created successfully.

然后把命令执行文件 app\Console\Commands\OrderExpireListen.php 写成这样:

<?php
namespace App\Console\Commands;


use App\Http\Models\Order;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis as Redis;


class OrderExpireListen extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'order:expire';




    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '监听订单创建,在30分钟后如果没付款取消订单。';




    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
{
        parent::__construct();
    }


    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
{
        //
        $cachedb = config('database.redis.cache.database',0);
        $pattern = '__keyevent@'.$cachedb.'__:expired';
        Redis::subscribe([$pattern],function ($channel){     // 订阅键过期事件
            $key_type = str_before($channel,':');
            switch ($key_type) {
                case 'ORDER_CONFIRM':
                    $order_id = str_after($channel,':');    // 取出订单 ID
                    $order = Order::find($order_id);
                    if ($order) {
                        $order->cancel(); // 执行取消操作
                    }
                    break;
                case 'ORDER_OTHEREVENT':
                    break;
                default:
                    break;
            }
        });
    }
}

文件好了之后,使用

$php artisan order:expire

启动,就可以了。

redis 的默认连接是有超时的。

你改下 app\config\database.php 中 redis 节,增加一个 read_write_timeout :

'redis' => [
        'client' => 'predis',
        'default' => [
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => env('REDIS_DB', 0),
            'read_write_timeout' => env('REDIS_RW_TIMEOUT', 5),  // 读写超时设定
        ],
    ],

然后在 .env 中配置 REDIS_RW_TIMEOUT=-1 这样就不会超时了。

舞飞杨:小哥哥,上次的东西真好,我把计划任务全改成那个了,好用,而且时间准,互不影响,

qufo: 嗯。

舞飞杨:可是我们的业务增长很快,一台机器处理不了,已经组了应用群集了,每台机器上都要装 redis 吗?

qufo: 嗯。

舞飞杨:不是吧,那么多 redis 服务器一台一个,能集中处理吗?所有的应用都把键存到一台机器上,然后只要一份监听程序监听那个过期事件?

qufo: 嗯。

舞飞杨:我听说你很厉害才找你。要是一台监听处理的机器处理来不及,再加一台去处理吗?

qufo: 嗯。

舞飞杨:嗯什么嗯,是你不知道吧?!

qufo: 什么叫不知道,当业务量大起来的时候,直接增加监听处理的机器是不行的,他们监听同一个过期事件,两台机器会同时接到过期事件,除非进行 hash 分工,要不然处理两遍事件就傻了。业务量足够大的时候,得用消息队列了。

舞飞杨:哦,消息队列怎么用?

qufo: 上次的监听处理程序只要一台处理,把监听处理的过程改一下,取出订单 ID 之后不要去处理,通过 rpush 放到一个 redis 的队列里去。另外起几台服务器,连到这个 redis 服务器,通过 blpop 接收消息队列里出来的订单 ID。这样,多台机器可以同时工作,一个订单只会从 blpop 里出来一次,不会重复执行,多台机器可以分担任务,又互不影响。消息队列也可以换成业界成熟的 rabbitmq 、 kafka 之类的专业消息队列,那又是另外一个话题了。反正业务量大了,变复杂了,消息总线跑不掉,天猫京东也差不多如此。

链接:https://learnku.com/articles/21488

30 分钟未付款取消订单,怎么做?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/111820.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 简单工厂模式

    简单工厂模式

    2021年12月3日
    49
  • 关于get请求的长度限制到底是多少?—-一个误区,一个教训

    关于get请求的长度限制到底是多少?—-一个误区,一个教训截至今日之前,我一直因为从某处看到get、post区别中写的:get有长度限制,1024B。很抱歉在未经过个人的检验后,直接奉为正确的定义(也提醒我个人:以后概念理论,还是需要好好验证或求证,要能在繁杂的网络知识中,认真求真,以防以讹传讹!!!)。今日,看到前同事大牛多年前的csdn知识总结,发现原来一直信奉的1024Get请求长度,是错误的。下面把从权威官网的解释复制过来,以做…

    2022年8月24日
    11
  • Ubuntu下使用SSH 命令用于登录远程桌面

    Ubuntu下使用SSH 命令用于登录远程桌面问题描述基础:ssh命令连接step1:SSH程序的安装step2:服务器启动ssh服务step3:查询服务器的ip地址step4:在本地主机端ssh远程登录服务器step5:退出远程登录进阶:利用公钥省去口令输入step1生成密钥对step2复制公钥至服务器参考资料问题描述做DL的经常需要在一台电脑(本地主机)上写代码,另一台电脑(服务器…

    2022年10月21日
    3
  • pinn求解ode_pt_pin

    pinn求解ode_pt_pin今天硕士论文开题答辩,想着学了这么长时间的PINN,七七八八也看了一些文献,一来是为了整理思路,二来可以方便以后回顾复习。使用PINN求解PDE与传统有限元、有限差分、谱方法等最大的区别是,无需做预先的假设,线性化和网格化。求解一般的偏微分方程的形式:PINN具体算法步骤如下:其中只有初边界训练数据包含u的值,内部配置点不包含u的值,只有定义域内部的点。(这一点一直迷糊,最近才理清楚)。PINN求解PDE框架图:可以通过自动微分最小化损失函数,得到神经网络最优参数,从而的到.

    2025年7月13日
    3
  • 测试用例八大要素有哪些?

    测试用例八大要素有哪些?测试用例八大要素:用例编号、操作步骤、测试标题、重要级别、前置条件、测试输入、所属模块、预期结果。用例编号由字符串组成,具有易于识别性和唯一性;操作步骤需要标明详细的测试步骤;测试标题需要简洁、明了;重要级别一般分为高、中、低;前置条件就是进行测试用例的前提条件;测试输入包括用户名、密码、订单号等内容;所属模块标明被测试的模块或者单元;预期结果表示预期输出的结果。测试用例很重要,是设计和测试过程的基础,同时测试用例也是软件测试的核心,可以发现软件可能出现的bug,一般每个软件产品都有与之对应的测试用

    2022年6月28日
    100
  • GPU技术_支持nvlink的显卡

    GPU技术_支持nvlink的显卡目录浅析GPU通信技术(上)-GPUDirectP2P浅析GPU通信技术(中)-NVLink浅析GPU通信技术(下)-GPUDirectRDMA1.背景上一篇文章《浅析GPU通信技术(上)-GPUDirectP2P》中我们提到通过GPUDirectP2P技术可以大大提升GPU服务器单机的GPU通信性能,但是受限于PCIExpre…

    2025年7月21日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号