订单是我们在日常开发中经常会遇到的一个功能,最近在做一个订单过期与超时的开发。订单过期与超时就不用我解释了吧,其实两者都是同一个问题来着,就是订单未支付的处理,我们要做的是对这些未支付的订单到了一定时间就自动取消,好了,你第一反应那肯定就是做一个定时任务了!是的,就是定时任务,但是哪个才会是最佳方案呢,下面来看看:

一、前端到时间请求取消

你肯定不会想着在前端来做定时请求取消该订单的,因为如果客户禁用了该应用或者没联网,那到了一定时间时,还一直都是未处理状态的。所以前端一般都是手动去触发取消订单的。

二、服务端定时查询有没有需要取消的订单,然后批量处理。

这种方法我们用得最多的,不过存在准确度的问题,还有需要确认定时任务的周期,但是我们可以结合redis来实现,我们可以在存redis时候顺便存在mysql这样的数据库做长久存储然后用服务端定时查询,这里我用到了redis的订阅功能。

首先先修改一下配置

然后再重启redis

这里创建4个文件

index.php  创建订单,发布消息,10s后查询订单状态并更新订单

<?php

require_once 'Redis2.class.php';
require_once 'db.class.php'; $redis2     = new \Redis2();
$list       = $redis2->lRange('order',0,9999999); $mysql      = new \mysql();
$mysql->connect(); $data       = ['ordersn'=>'SN'.time().'T'.rand(10000000,99999999),'status'=>0,'createtime'=>date('Y-m-d H:i:s',time())]; $mysql->insert('order',$data); $order_sn   = $data['ordersn']; $redis2->setex($order_sn,10,$order_sn);

psubscribe.php,发布redis订阅的一些逻辑处理

这里我把redis的一些操作和mysql的一些处理做了封装处理,这个你们可以用到你们自己的项目当中的

Redis2.class.php

<?php

class Redis2
{
    private $redis;     public function __construct($host = '127.0.0.1', $port = 6379)
    {
        $this->redis = new Redis();
        $this->redis->connect($host, $port);
        $this->redis->auth('123456');
    }     public function setex($key, $time, $val)
    {
        return $this->redis->setex($key, $time, $val);
    }     public function set($key, $val)
    {
        return $this->redis->set($key, $val);
    }     public function get($key)
    {
        return $this->redis->get($key);
    }     public function expire($key = null, $time = 0)
    {
        return $this->redis->expire($key, $time);
    }     public function psubscribe($patterns = array(), $callback)
    {
        $this->redis->psubscribe($patterns, $callback);
    }     public function setOption()
    {
        $this->redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
    }     public function lRange($key,$start,$end)
    {
        return $this->redis->lRange($key,$start,$end);
    }     public function lPush($key, $value1, $value2 = null, $valueN = null ){
        return $this->redis->lPush($key, $value1, $value2 = null, $valueN = null );
    }     public function delete($key1, $key2 = null, $key3 = null)
    {
        return $this->redis->delete($key1, $key2 = null, $key3 = null);
    } }

db.class.php,对sql处理的一些封装

<?php
class mysql
{
    private $mysqli;
    private $result;
    /**
     * 数据库连接
     * @param $config 配置数组
     */     public function connect()
    {
        $config=array(
            'host'=>'127.0.0.1',
            'username'=>'root',
            'password'=>'123456qwerty',
            'database'=>'marhal',
            'port'=>3306,
            );         $host = $config['host'];    //主机地址
        $username = $config['username'];//用户名
        $password = $config['password'];//密码
        $database = $config['database'];//数据库
        $port = $config['port'];    //端口号
        $this->mysqli = new mysqli($host, $username, $password, $database, $port);     }
    /**
     * 数据查询
     * @param $table 数据表
     * @param null $field 字段
     * @param null $where 条件
     * @return mixed 查询结果数目
     */
    public function select($table, $field = null, $where = null)
    {
        $sql = "SELECT * FROM `{$table}`";
        //echo $sql;exit;
        if (!empty($field)) {
            $field = '`' . implode('`,`', $field) . '`';
            $sql = str_replace('*', $field, $sql);
        }
        if (!empty($where)) {
            $sql = $sql . ' WHERE ' . $where;
        }         $this->result = $this->mysqli->query($sql);         return $this->result;
    }
    /**
     * @return mixed 获取全部结果
     */
    public function fetchAll()
    {
        return $this->result->fetch_all(MYSQLI_ASSOC);
    }
    /**
     * 插入数据
     * @param $table 数据表
     * @param $data 数据数组
     * @return mixed 插入ID
     */
    public function insert($table, $data)
    {
        foreach ($data as $key => $value) {
            $data[$key] = $this->mysqli->real_escape_string($value);
        }
        $keys = '`' . implode('`,`', array_keys($data)) . '`';
        $values = '\'' . implode("','", array_values($data)) . '\'';
        $sql = "INSERT INTO `{$table}`( {$keys} )VALUES( {$values} )";
        $this->mysqli->query($sql);
        return $this->mysqli->insert_id;
    }
    /**
     * 更新数据
     * @param $table 数据表
     * @param $data 数据数组
     * @param $where 过滤条件
     * @return mixed 受影响记录
     */
    public function update($table, $data, $where)
    {
        foreach ($data as $key => $value) {
            $data[$key] = $this->mysqli->real_escape_string($value);
        }
        $sets = array();
        foreach ($data as $key => $value) {
            $kstr = '`' . $key . '`';
            $vstr = '\'' . $value . '\'';
            array_push($sets, $kstr . '=' . $vstr);
        }
        $kav = implode(',', $sets);
        $sql = "UPDATE `{$table}` SET {$kav} WHERE {$where}";         $this->mysqli->query($sql);
        return $this->mysqli->affected_rows;
    }
    /**
     * 删除数据
     * @param $table 数据表
     * @param $where 过滤条件
     * @return mixed 受影响记录
     */
    public function delete($table, $where)
    {
        $sql = "DELETE FROM `{$table}` WHERE {$where}";
        $this->mysqli->query($sql);
        return $this->mysqli->affected_rows;
    }
}

对每一次订单的访问我们做了服务器监听任务,如下:

设置本地域名并访问http://www.startphp.cn/index.php

此时,每访问一次index.php,就会创建一个订单,10s钟后,如果该订单依旧处于未支付状态,就设置订单失效。

但是php的cli模式在服务器运行后,总是会掉线,处理这个问题的方法,我们不妨写一个脚本

1.编写shell脚本,定时检查进程是否存在,不存在的话就开启服务,并且将运行情况写入日志

脚本文件如下:

#!/bin/sh
PIDS=`pidof php`
if [ "$PIDS" != "" ]; then
echo "在运行"
echo -e $(date +%Y"."%m"."%d" "%k":"%M":"%S)" running....." >> /mytask/task.run.log
else
echo "不在运行,开始启动"
echo -e $(date +%Y"."%m"."%d" "%k":"%M":"%S)" start php start....." >> /mytask/task.start.log
cd /var/www/html/redis
php psubscribe.php &
echo -e $(date +%Y"."%m"."%d" "%k":"%M":"%S)" start php success....." >> /mytask/task.start.log
fi

在crontab任务里创建任务,这里设定的是每5s检查一次,crontab -e

效果你可以查看task.run.log

结果如下:

有需要学习交流的友人请加入交流群的咱们一起,群内都是1-7年的开发者,希望可以一起交流,探讨PHP,swoole这块的技术 或者有其他问题 也可以问,获取swoole或者php进阶相关资料私聊管理即可

别忘了点赞哦,定期分享干货

点此加入该群​jq.qq.com

PHP如何实现处理过期或者超时订单的,并还原库存的更多相关文章

  1. 下订单更新订单表然后减少库存表中的数据,出现库存超卖,使用数据库和redis坚决库存超卖的问题

    上面的代码更新库存的数据,存在多线程的问题,第一种方法使用synchronized关键字修饰的语句块代码,但是性能较低,并且还是存在问题的 在分布式的场景下,当前库存系统部署在多个tomcat上,即使 ...

  2. 基于rabbitMQ 消息延时队列方案 模拟电商超时未支付订单处理场景

    前言 传统处理超时订单 采取定时任务轮训数据库订单,并且批量处理.其弊端也是显而易见的:对服务器.数据库性会有很大的要求,并且当处理大量订单起来会很力不从心,而且实时性也不是特别好 当然传统的手法还可 ...

  3. day89:luffy:使用Celery完成我的订单超时取消&Polyv视频加密播放

    目录 1.我的订单超时取消 2.PoliV视频播放 1.我的订单超时取消 使用Celery完成超时取消功能 mycelery/order/tasks.py from mycelery.main imp ...

  4. RabbitMQ实现订单超时案例

    前言 人间清醒 目录 前言 业务场景 JUC(DelayQueue)方案 DelayQueue简介 JUC DelayQueue实现订单超时案例代码 案例代码 Redis Key过期事件方案 简介 R ...

  5. PHP【Laravel】delayer基于redis的实现订单超时改变状态

    实现这个功能前你需要知道以下,不然可能会比较吃力:1.服务器的计划任务,shell脚本,或者你有宝塔自带的计划任务会方便很多.2.有所了解Redis.3.会写PHP业务逻辑. 好了进入在正题,这里使用 ...

  6. ENode框架Conference案例分析系列之 - 订单处理减库存的设计

    前言 前面的文章,我介绍了Conference案例的业务.上下文划分.领域模型.架构,以及代码整体流程.接下来想针对案例中一些重要的场景,分别做进一步的分析.本文想先介绍一下Conference案例的 ...

  7. web前端处理订单待支付倒计时计算显示问题

    在商城类项目的时候,有很多待支付的订单,有时候在订单列表页面会分别显示倒计时,就是页面会有很多倒计时的订单. 处理方法: 1.调用后端接口拿到所有的订单,获取所有的倒计时订单,获取到期时间(尽量时间戳 ...

  8. 使用“消息服务框架”(MSF)实现分布式事务的三阶段提交协议(电商创建订单的示例)

    1,示例解决方案介绍 在上一篇 <消息服务框架(MSF)应用实例之分布式事务三阶段提交协议的实现>中,我们分析了分布式事务的三阶段提交协议的原理,现在我们来看看如何使用消息服务框架(MSF ...

  9. CQRS之旅——旅程4(扩展和增强订单和注册限界上下文)

    旅程4:扩展和增强订单和注册限界上下文 进一步探索订单和注册的有界上下文. "我明白,如果一个人想看些新鲜的东西,旅行并不是没有意义的."儒勒·凡尔纳,环游世界80天 对限界上下文 ...

随机推荐

  1. 【荐】CSS实现漂亮实用带箭头的流程图

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. How To Use These LED Garden Lights

    Are you considering the lighting options for the outdoor garden? Depending on how you use it, LED ga ...

  3. Linux 中查看可用的网络接口

    目录 Linux 中查看可用的网络接口 在 Linux 中找到可用的网络接口 title: Linux 中查看可用的网络接口 date: 2020/2/25 16:56:36 toc: true -- ...

  4. springboot中druid监控的配置(DruidConfiguration)

    当数据库连接池使用druid 时,我们进行一些简单的配置就能查看到sql监控,web监控,url监控等等. 以springboot为例,配置如下 import com.alibaba.druid.su ...

  5. 如何预测股票分析--自动ARIMA

    在上一篇中,我们发现knn和线性回归一样,表现的不是特别好,来看看时间序列的表现 时间序列预测法其实是一种回归预测方法,属于定量预测,其基本原理是;一方面承认事物发展的延续性,运用过去时间序列的数据进 ...

  6. 边双连通分量 jarjan (poj 3177)

    大意:给定一个无向连通图,判断至少加多少的边,才能使任意两点之间至少有两条的独立的路(没有公共的边,但可以经过同一个中间的顶点). 思路:在同一个双连通分量里的所有的点可以看做一个点,收缩后,新图是一 ...

  7. 转载:进程退出状态--waitpid status意义

    最近遇到一个进程突然退出的问题,由于没有注册signalhandler所以没有捕捉到任何信号. 但是从log中看到init waitpid返回的status为0x008b,以前对status不是很了解 ...

  8. 每天进步一点点------H.264学习 (一)

    分三个阶段学习1.第一个阶段: 学习H.264,首先要把最基本最必要的资料拿在手里.这些资料包括:标准文档+测试模型+经典文章,在本FTP中能找到.首先看 <H.264_MPEG-4 Part ...

  9. Analog power pin UPF defination

    在一个analog macro端口上,有些pin是always on的,有些是shut down的,如何描述这些pin的power属性?这是一个常见问题,驴就此机会大致描述一下常见的做法.对于这个问题 ...

  10. The Preliminary Contest for ICPC Asia Xuzhou 2019 J Random Access Iterator (树形DP)

    每次循环向下寻找孩子时,随机选取一个孩子,设dp[u]为从u出发,不能得出正确答案的概率,则从u出发,走一次的情况下不能得出正确答案的概率是 P = (dp[v1]+dp[v2]+dp[v3]+--d ...