php 订单过期处理,PHP实现处理过期或者超时订单,并还原库存
訂單是我們在日常開發中經常會遇到的一個功能,最近在做一個訂單過期與超時的開發。訂單過期與超時就不用我解釋了吧,其實兩者都是同一個問題來著,就是訂單未支付的處理,我們要做的是對這些未支付的訂單到了一定時間就自動取消,好了,你第一反應那肯定就是做一個定時任務了!是的,就是定時任務,但是哪個才會是最佳方案呢,下面來看看:
一、前端到時間請求取消
你肯定不會想著在前端來做定時請求取消該訂單的,因為如果客戶禁用了該應用或者沒聯網,那到了一定時間時,還一直都是未處理狀態的。所以前端一般都是手動去觸發取消訂單的。
二、服務端定時查詢有沒有需要取消的訂單,然后批量處理。
這種方法我們用得最多的,不過存在準確度的問題,還有需要確認定時任務的周期,但是我們可以結合redis來實現,我們可以在存redis時候順便存在mysql這樣的數據庫做長久存儲然后用服務端定時查詢,這里我用到了redis的訂閱功能。
首先先修改一下配置
vim?/etc/redis/redis.conf
notify-keyspace-events?“Ex”?#x 代表了過期事件。
然后再重啟redis
service?redis?restart
這里創建4個文件
index.php? 創建訂單,發布消息,10s后查詢訂單狀態并更新訂單
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訂閱的一些邏輯處理
setOption();
$redis->psubscribe(array('__keyevent@0__:expired'),?'keyCallback');
//?回調函數,這里寫處理邏輯
function?keyCallback($redis,?$pattern,?$chan,?$msg)
{
$mysql?=?new?\mysql();
$mysql->connect();
$where?=?"ordersn?=?'".$msg."'";
$mysql->select('order','',$where);
$finds=$mysql->fetchAll();
if(isset($finds[0]['status'])?&&?$finds[0]['status']==0){
$data???=?array('status'?=>?3,'updatetime'=>date('Y-m-d?H:i:s',time()));
$where??=?"id?=?".$finds[0]['id'];
$mysql->update('order',$data,$where);
}
}
這里我把redis的一些操作和mysql的一些處理做了封裝處理,這個你們可以用到你們自己的項目當中的
Redis2.class.php
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處理的一些封裝
'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;
}
}
對每一次訂單的訪問我們做了服務器監聽任務,如下:
cd?/var/www/html/redis
#即時監聽,ctrl+c?退出監聽??ctrl+z?暫停監聽
nohup?php?psubscribe.php
#后臺監聽
nohup?php?psubscribe.php?&
設置本地域名并訪問http://www.startphp.cn/index.php
此時,每訪問一次index.php,就會創建一個訂單,10s鐘后,如果該訂單依舊處于未支付狀態,就設置訂單失效。
這里要注意一下:
在命令之前加上 nohup ,啟動的進程將會忽略linux的掛起信號 (SIGHUP)
這時候,當我們組合 nohup 和 &兩種方式時,啟動的進程不會占用控制臺,并且不依賴控制臺,控制臺關閉之后,進程被1號進程收養,成為孤兒進程,這就和守護進程的機制非常類似了,并且nohup默認會把程序的輸出重定向到當前目錄下的nohup.out文件,如果沒有可寫權限,則寫入 $homepath/nohup.out
但是php的cli模式在服務器運行后,總是會掉線,處理這個問題的方法,寫一個腳本
1.編寫shell腳本,定時檢查進程是否存在,不存在的話就開啟服務,并且將運行情況寫入日志
cd?/
mkdir?mytask
cd?mytask
touch?phprunning.sh
vi?phprunning.sh
腳本文件如下:
#!/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
*?*?*?*?*?sleep?5;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?10;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?15;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?20;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?25;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?30;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?35;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?40;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?45;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?50;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
*?*?*?*?*?sleep?55;?sh?/mytask/phprunning.sh?>>?/mytask/crontab.log
效果你可以查看task.run.log
cat?/mytask/task.run.log
結果如下:
-e?2019.10.22?14:28:41?running.....
-e?2019.10.22?14:28:46?running.....
-e?2019.10.22?14:28:51?running.....
-e?2019.10.22?14:28:56?running.....
-e?2019.10.22?14:29:06?running.....
-e?2019.10.22?14:29:11?running.....
-e?2019.10.22?14:29:16?running.....
-e?2019.10.22?14:29:21?running.....
-e?2019.10.22?14:29:26?running.....
-e?2019.10.22?14:29:31?running.....
-e?2019.10.22?14:29:36?running.....
-e?2019.10.22?14:29:41?running.....
-e?2019.10.22?14:29:46?running.....
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的php 订单过期处理,PHP实现处理过期或者超时订单,并还原库存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php限制小程序访问,PHP投票小程序,
- 下一篇: s matlab toolbox,Mat