首页 > 其他分享 >swoole实现订单的延时处理(自动取消订单,还原库存等操作)

swoole实现订单的延时处理(自动取消订单,还原库存等操作)

时间:2022-11-25 13:02:04浏览次数:83  
标签:orderId product swoole number pdo 订单 延时 order stock


一、业务场景:当客户下单在指定的时间内如果没有付款,那我们需要将这笔订单取消掉,比如好的处理方法是运用延时取消,很多人首先想到的当然是crontab,这个也行,不过这里我们运用swoole的异步毫秒定时器来实现,同样也不会影响到当前程序的运行,具体可以参考:https://wiki.swoole.com/wiki/page/319.html

 

二、说明,order_status为1时代表客户下单确定,为2时代表客户已付款,为0时代表订单已取消(正是swoole来做的)

 

三、举例说明,库存表csdn_product_stock产品ID为1的产品库存数量为20,产品ID为2的库存数量为40,然后客户下单一笔产品ID1减10,产品ID2减20,所以库存表只够2次下单,例子中10秒后自动还原库存,如下图:

图解:1、第一次下完单产品ID1库存从20减到了10,产品ID2库存从40减到了20;2、第二次下完单产品ID的库存为0了,产品ID2的库存也为0了,3、第三次下单时,程序提示Out of stock;4、过了10秒钟(每个订单下单后往后推10秒),客户两次下单,由于没有付款(csdn_order表的order_status为1),产品1和产品2的库存被还原了(csdn_order表的order_status变为0),客户又可以继续下单了.

1、所需要sql数据库表

下面贴出来纯手工PHP,很多同学用了原生PHP,就不会运用到框架里去,其实都一样的,不要想得那么复杂就是了。只要一点就是你用多了,你就会这样觉得咯。

配置文件config.php  ,这个在框架的话,基本上都是配置好了。

$dbHost = "192.168.23.110";
$dbUser = "root";
$dbPassword = "123456";
$dbName = "test";

swoole都是用在linux系统里的,这里的host你可以自己搭建虚拟主机,也可以网上购买属于自己的服务器

 

订单提交的文件order_submit.php,这里对订单生成,同时扣除库存的一系列操作

require("config.php");
try {
$pdo = new PDO("mysql:host=" . $dbHost . ";dbname=" . $dbName, $dbUser, $dbPassword, array(PDO::ATTR_PERSISTENT => true));
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$orderInfo = array(
'order_amount' => 10.92,
'user_name' => 'yusan',
'order_status' => 1,
'date_created' => 'now()',
'product_lit' => array(
0 => array(
'product_id' => 1,
'product_price' => 5.00,
'product_number' => 10,
'date_created' => 'now()'
),
1 => array(
'product_id' => 2,
'product_price' => 5.92,
'product_number' => 20,
'date_created' => 'now()'
)
)
);

try{
$pdo->beginTransaction();//开启事务处理

$sql = 'insert into csdn_order (order_amount, user_name, order_status, date_created) values (:orderAmount, :userName, :orderStatus, now())';
$stmt = $pdo->prepare($sql);
$affectedRows = $stmt->execute(array(':orderAmount' => $orderInfo['order_amount'], ':userName' => $orderInfo['user_name'], ':orderStatus' => $orderInfo['order_status']));
$orderId = $pdo->lastInsertId();
if(!$affectedRows) {
throw new PDOException("Failure to submit order!");
}
foreach($orderInfo['product_lit'] as $productInfo) {

$sqlProductDetail = 'insert into csdn_order_detail (order_id, product_id, product_price, product_number, date_created) values (:orderId, :productId, :productPrice, :productNumber, now())';
$stmtProductDetail = $pdo->prepare($sqlProductDetail);
$stmtProductDetail->execute(array(':orderId' => $orderId, ':productId' => $productInfo['product_id'], ':productPrice' => $productInfo['product_price'], ':productNumber' => $productInfo['product_number']));

$sqlCheck = "select product_stock_number from csdn_product_stock where product_id=:productId";
$stmtCheck = $pdo->prepare($sqlCheck);
$stmtCheck->execute(array(':productId' => $productInfo['product_id']));
$rowCheck = $stmtCheck->fetch(PDO::FETCH_ASSOC);
if($rowCheck['product_stock_number'] < $productInfo['product_number']) {
throw new PDOException("Out of stock, Failure to submit order!");
}


$sqlProductStock = 'update csdn_product_stock set product_stock_number=product_stock_number-:productNumber, date_modified=now() where product_id=:productId';
$stmtProductStock = $pdo->prepare($sqlProductStock);
$stmtProductStock->execute(array(':productNumber' => $productInfo['product_number'], ':productId' => $productInfo['product_id']));
$affectedRowsProductStock = $stmtProductStock->rowCount();

//库存没有正常扣除,失败,库存表里的product_stock_number设置了为非负数
//如果库存不足时,sql异常:SQLSTATE[22003]: Numeric value out of range: 1690 BIGINT UNSIGNED value is out of range in '(`test`.`csdn_product_stock`.`product_stock_number` - 20)'
if($affectedRowsProductStock <= 0) {
throw new PDOException("Out of stock, Failure to submit order!");
}
}
echo "Successful, Order Id is:" . $orderId .",Order Amount is:" . $orderInfo['order_amount'] . "。";
$pdo->commit();//提交事务
//exec("php order_cancel.php -a" . $orderId . " &");
pclose(popen('php order_cancel.php -a ' . $orderId . ' &', 'w'));
//system("php order_cancel.php -a" . $orderId . " &", $phpResult);
//echo $phpResult;
}catch(PDOException $e){
echo $e->getMessage();
$pdo->rollback();
}
$pdo = null;
} catch (PDOException $e) {
echo $e->getMessage();
}

订单的延时处理order_cancel.php

require("config.php");
$queryString = getopt('a:');
$userParams = array($queryString);
appendLog(date("Y-m-d H:i:s") . "\t" . $queryString['a'] . "\t" . "start");

try {
$pdo = new PDO("mysql:host=" . $dbHost . ";dbname=" . $dbName, $dbUser, $dbPassword, array(PDO::ATTR_PERSISTENT => true));
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

swoole_timer_after(10000, function ($queryString) {
global $queryString, $pdo;

try{
$pdo->beginTransaction();//开启事务处理

$orderId = $queryString['a'];
$sql = "select order_status from csdn_order where order_id=:orderId";
$stmt = $pdo->prepare($sql);
$stmt->execute(array(':orderId' => $orderId));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
//$row['order_status'] === "1"代表已下单,但未付款,我们还原库存只针对未付款的订单
if(isset($row['order_status']) && $row['order_status'] === "1") {
$sqlOrderDetail = "select product_id, product_number from csdn_order_detail where order_id=:orderId";
$stmtOrderDetail = $pdo->prepare($sqlOrderDetail);
$stmtOrderDetail->execute(array(':orderId' => $orderId));
while($rowOrderDetail = $stmtOrderDetail->fetch(PDO::FETCH_ASSOC)) {
$sqlRestoreStock = "update csdn_product_stock set product_stock_number=product_stock_number + :productNumber, date_modified=now() where product_id=:productId";
$stmtRestoreStock = $pdo->prepare($sqlRestoreStock);
$stmtRestoreStock->execute(array(':productNumber' => $rowOrderDetail['product_number'], ':productId' => $rowOrderDetail['product_id']));
}

$sqlRestoreOrder = "update csdn_order set order_status=:orderStatus where order_id=:orderId";
$stmtRestoreOrder = $pdo->prepare($sqlRestoreOrder);
$stmtRestoreOrder->execute(array(':orderStatus' => 0, ':orderId' => $orderId));
}

$pdo->commit();//提交事务
}catch(PDOException $e){
echo $e->getMessage();
$pdo->rollback();
}
$pdo = null;

appendLog(date("Y-m-d H:i:s") . "\t" . $queryString['a'] . "\t" . "end\t" . json_encode($queryString));
}, $pdo);

} catch (PDOException $e) {
echo $e->getMessage();
}
function appendLog($str) {
$dir = 'log.txt';
$fh = fopen($dir, "a");
fwrite($fh, $str . "\n");
fclose($fh);
}

标签:orderId,product,swoole,number,pdo,订单,延时,order,stock
From: https://blog.51cto.com/u_13940603/5886367

相关文章

  • PHP消息队列如何处理订单和配送
    在项目开发者经常会遇到『给用户群发短信』、『订单系统有大量的日志需要记录』或者在秒杀业务的时候服务器无法承受瞬间并发的压力。这种情况下,我们怎么保证系统正常有效的......
  • Curl 在 Swoole 协程中的解决方案
    在Swoole应用中,是不推荐使用Curl的,因为Curl会阻塞进程。本文会用实际的代码和数据,用最直观的方式,让你明白为什么。最后还会给出Curl在Swoole中的解决方案,如果不想......
  • 31.01-1-千万级订单的生成背后痛点【双元】(1) id生成案例,分库分表
    01-1-千万级订单的生成背后痛点【双元】(1)                分库分表自增id重复,     ......
  • freertos-刘火良:延时列表(链表)
    前面几章的学习中,任务从创建后一直位于就绪列表中,延时、优先级等操作全部在就绪列表进行,这是不太方便的。根据任务的几个状态知,还需要一个延时列表,当任务进入延时状态时,则......
  • Kafka 延时队列,重试队列,死信队列
    延时队列在发送延时消息的时候并不是先投递到要发送的真实主题(real_topic)中,而是先投递到一些Kafka内部的主题(delay_topic)中,这些内部主题对用户不可见,然后通过一个自定......
  • Java实现自动取消订单
    摘要Java实现自动取消订单这个功能我实际经验,某医药电商平台的订单管理系统,下单45分钟付款,否者取消。方式一:quartz,每几分钟执行一次(根据订单处理速度,和订单生成情况)。每次......
  • 用户提交订单,30分钟还没付款,取消订单功能分析
    摘要用户提交订单,30分钟还没付款,取消订单功能分析统一来说,业务有“在一段时间之后,完成一个工作任务”的需求。实现这种定时任务有哪些方法呢,来总结一下想到的方法。一、定时......
  • 销售订单行项的成本估算及其发布
    对于不可配置物料级的标准成本估算,我们知道可以用ck11n来做,对于销售订单行的估算,可以后台配置成自动估算,也可以用ck51或ck51n来做手工估算(见图),估算的同时可以做......
  • 如何防止用户重复提交订单?(下)
    一、摘要在上一篇文章中,我们详细的介绍了随着下单流量逐渐上升,为了降低数据库的访问压力,通过请求唯一ID+redis分布式锁来防止接口重复提交,流程图如下!每次提交的时候,需要......
  • SAP笔记-SD 销售订单 收入 和 成本 对应科目的逻辑
    销售订单收入和成本对应科目的逻辑(2009-02-19)一.收入:1.      销售订单类型->单据定价过程  2.      单据定价过程->定价过程sorg:销售组织,dc......