PHP如何實現處理過期或者超時訂單的,並還原庫存


訂單是我們在日常開發中經常會遇到的一個功能,最近在做一個訂單過期與超時的開發。訂單過期與超時就不用我解釋了吧,其實兩者都是同一個問題來着,就是訂單未支付的處理,我們要做的是對這些未支付的訂單到了一定時間就自動取消,好了,你第一反應那肯定就是做一個定時任務了!是的,就是定時任務,但是哪個才會是最佳方案呢,下面來看看:

 

一、前端到時間請求取消

你肯定不會想着在前端來做定時請求取消該訂單的,因為如果客戶禁用了該應用或者沒聯網,那到了一定時間時,還一直都是未處理狀態的。所以前端一般都是手動去觸發取消訂單的。

 

二、服務端定時查詢有沒有需要取消的訂單,然后批量處理。

這種方法我們用得最多的,不過存在准確度的問題,還有需要確認定時任務的周期,但是我們可以結合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


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM