|
|
@@ -8,6 +8,7 @@ use biz\wx\classes\WxMessageClass;
|
|
|
use bizHd\custom\classes\HdClass;
|
|
|
use bizHd\express\classes\HdDeliveryOrderClass;
|
|
|
use bizHd\hb\classes\HbClass;
|
|
|
+use bizHd\homePageConfig\classes\HomePageModuleClass;
|
|
|
use bizHd\order\classes\ScanPayClass;
|
|
|
use bizHd\wx\classes\WxOpenClass;
|
|
|
use bizMall\custom\classes\CustomClass;
|
|
|
@@ -605,10 +606,22 @@ class OrderController extends BaseController
|
|
|
if ($stock <= 0) {
|
|
|
util::fail('库存不足');
|
|
|
}
|
|
|
+ $saleGoodsId = $specGoodsId > 0 ? $specGoodsId : $goodsId;
|
|
|
+ $activityType = strval($post['activityType'] ?? '');
|
|
|
|
|
|
//事务处理
|
|
|
$connection = Yii::$app->db;
|
|
|
$transaction = $connection->beginTransaction();
|
|
|
+ $transactionFinished = false;
|
|
|
+ register_shutdown_function(function () use (&$transactionFinished, $transaction) {
|
|
|
+ if ($transactionFinished) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ HomePageModuleClass::rollbackSeckillReservationSnapshot();
|
|
|
+ if ($transaction->isActive) {
|
|
|
+ $transaction->rollBack();
|
|
|
+ }
|
|
|
+ });
|
|
|
try {
|
|
|
$custom = $this->custom;
|
|
|
if (empty($custom)) {
|
|
|
@@ -631,16 +644,48 @@ class OrderController extends BaseController
|
|
|
$post['payWay'] = 0;
|
|
|
|
|
|
$shop = $this->shop;
|
|
|
- $params = [];
|
|
|
- $goodsData = $goodsInfo->attributes;
|
|
|
- $ret = \bizHd\goods\classes\GoodsClass::getFinalPrice($goodsData, $shop, $custom, $params);
|
|
|
- $goodsInfo->priceType = $ret['priceType'] ?? 0;
|
|
|
- if ($goodsInfo->priceType == 0) {
|
|
|
- util::fail('没有价格,不能下单哈');
|
|
|
+
|
|
|
+ // 秒杀商品:不信任前端传的价格,独立回查 Redis 秒杀配置核实活动状态/库存/限购后再核价
|
|
|
+ $seckillRow = null;
|
|
|
+ if ($activityType === 'seckill') {
|
|
|
+ $seckillRow = HomePageModuleClass::getSeckillActiveRow($mainId, $saleGoodsId);
|
|
|
+ if (empty($seckillRow)) {
|
|
|
+ util::fail('秒杀活动已结束或商品已下架,请重新选择');
|
|
|
+ }
|
|
|
+ $seckillStock = floatval($seckillRow['stock'] ?? 0);
|
|
|
+ $soldCount = HomePageModuleClass::getSeckillSoldCount($mainId, $saleGoodsId);
|
|
|
+ if (bcadd($soldCount, $goodsNum, 2) > $seckillStock) {
|
|
|
+ util::fail('秒杀库存不足,请重新选择');
|
|
|
+ }
|
|
|
+ $seckillLimit = floatval($seckillRow['limit'] ?? 0);
|
|
|
+ if ($seckillLimit > 0) {
|
|
|
+ $boughtCount = HomePageModuleClass::getSeckillCustomBoughtCount($mainId, $saleGoodsId, $customId);
|
|
|
+ if (bcadd($boughtCount, $goodsNum, 2) > $seckillLimit) {
|
|
|
+ util::fail("秒杀商品每人限购{$seckillLimit}件,已超出可购买数量");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- $unitPrice = $ret['price'] ?? 0;
|
|
|
- if ($unitPrice <= 0) {
|
|
|
- util::fail('没有价格,不能下单呢');
|
|
|
+
|
|
|
+ if ($seckillRow) {
|
|
|
+ $goodsInfo->priceType = 1;
|
|
|
+ $unitPrice = floatval($seckillRow['price'] ?? 0);
|
|
|
+ if ($unitPrice <= 0) {
|
|
|
+ util::fail('秒杀价格异常,不能下单');
|
|
|
+ }
|
|
|
+ // 通过校验后立即预占名额,失败/异常时随事务一起回滚(见 register_shutdown_function)
|
|
|
+ HomePageModuleClass::reserveSeckillPurchase($mainId, $saleGoodsId, $customId, $goodsNum);
|
|
|
+ } else {
|
|
|
+ $params = [];
|
|
|
+ $goodsData = $goodsInfo->attributes;
|
|
|
+ $ret = \bizHd\goods\classes\GoodsClass::getFinalPrice($goodsData, $shop, $custom, $params);
|
|
|
+ $goodsInfo->priceType = $ret['priceType'] ?? 0;
|
|
|
+ if ($goodsInfo->priceType == 0) {
|
|
|
+ util::fail('没有价格,不能下单哈');
|
|
|
+ }
|
|
|
+ $unitPrice = $ret['price'] ?? 0;
|
|
|
+ if ($unitPrice <= 0) {
|
|
|
+ util::fail('没有价格,不能下单呢');
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//计算总金额
|
|
|
@@ -878,7 +923,6 @@ class OrderController extends BaseController
|
|
|
$post['mainId'] = $mainId;
|
|
|
$post['needPrint'] = dict::getDict('needPrint', 'need');
|
|
|
$hasPay = 0;
|
|
|
- $saleGoodsId = $specGoodsId > 0 ? $specGoodsId : $goodsId;
|
|
|
$product = [['productId' => $saleGoodsId, 'unitType' => 0, 'property' => 0, 'unitPrice' => $unitPrice, 'num' => $goodsNum]];
|
|
|
$post['product'] = $product;
|
|
|
$return = \bizHd\order\services\OrderService::createHdOrder($post, $custom, $hasPay);
|
|
|
@@ -894,9 +938,13 @@ class OrderController extends BaseController
|
|
|
$orderSn = $return->orderSn;
|
|
|
$actPrice = $return->actPrice ?? 0;
|
|
|
$transaction->commit();
|
|
|
+ $transactionFinished = true;
|
|
|
+ HomePageModuleClass::clearSeckillReservationSnapshot();
|
|
|
util::success(['orderSn' => $orderSn, 'totalPrice' => $actPrice, 'couponId' => 0, 'id' => $orderId]);
|
|
|
} catch (Exception $e) {
|
|
|
$transaction->rollBack();
|
|
|
+ HomePageModuleClass::rollbackSeckillReservationSnapshot();
|
|
|
+ $transactionFinished = true;
|
|
|
Yii::info("失败原因:" . $e->getMessage());
|
|
|
util::fail('下单失败');
|
|
|
}
|
|
|
@@ -1031,6 +1079,7 @@ class OrderController extends BaseController
|
|
|
return;
|
|
|
}
|
|
|
\bizHd\product\classes\ProductClass::rollbackLimitBuySnapshot();
|
|
|
+ HomePageModuleClass::rollbackSeckillReservationSnapshot();
|
|
|
if ($transaction->isActive) {
|
|
|
$transaction->rollBack();
|
|
|
}
|
|
|
@@ -1143,15 +1192,48 @@ class OrderController extends BaseController
|
|
|
if ($stockSet == 1 && $stock <= 0) {
|
|
|
util::fail('花束库存不足');
|
|
|
}
|
|
|
+
|
|
|
+ // 秒杀商品:不信任前端传的价格,独立回查 Redis 秒杀配置核实活动状态/库存/限购后再核价
|
|
|
+ $seckillRow = null;
|
|
|
+ if (strval($element['activityType'] ?? '') === 'seckill') {
|
|
|
+ $seckillRow = HomePageModuleClass::getSeckillActiveRow($mainId, $saleGoodsId);
|
|
|
+ if (empty($seckillRow)) {
|
|
|
+ util::fail('秒杀活动已结束或商品已下架,请重新选择');
|
|
|
+ }
|
|
|
+ $seckillStock = floatval($seckillRow['stock'] ?? 0);
|
|
|
+ $soldCount = HomePageModuleClass::getSeckillSoldCount($mainId, $saleGoodsId);
|
|
|
+ if (bcadd($soldCount, $goodsNum, 2) > $seckillStock) {
|
|
|
+ util::fail('秒杀库存不足,请重新选择');
|
|
|
+ }
|
|
|
+ $seckillLimit = floatval($seckillRow['limit'] ?? 0);
|
|
|
+ if ($seckillLimit > 0) {
|
|
|
+ $boughtCount = HomePageModuleClass::getSeckillCustomBoughtCount($mainId, $saleGoodsId, $hdCustomId);
|
|
|
+ if (bcadd($boughtCount, $goodsNum, 2) > $seckillLimit) {
|
|
|
+ util::fail("秒杀商品每人限购{$seckillLimit}件,已超出可购买数量");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
$goodsData = $goodsInfo->attributes;
|
|
|
- $ret = \bizHd\goods\classes\GoodsClass::getFinalPrice($goodsData, $shop, $custom, []);
|
|
|
- $priceType = $ret['priceType'] ?? 0;
|
|
|
- if ($priceType == 0) {
|
|
|
- util::fail('花束没有价格,不能下单');
|
|
|
+ if ($seckillRow) {
|
|
|
+ $unitPrice = floatval($seckillRow['price'] ?? 0);
|
|
|
+ if ($unitPrice <= 0) {
|
|
|
+ util::fail('秒杀价格异常,不能下单');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ $ret = \bizHd\goods\classes\GoodsClass::getFinalPrice($goodsData, $shop, $custom, []);
|
|
|
+ $priceType = $ret['priceType'] ?? 0;
|
|
|
+ if ($priceType == 0) {
|
|
|
+ util::fail('花束没有价格,不能下单');
|
|
|
+ }
|
|
|
+ $unitPrice = $ret['price'] ?? 0;
|
|
|
+ if ($unitPrice <= 0) {
|
|
|
+ util::fail('花束没有价格,不能下单');
|
|
|
+ }
|
|
|
}
|
|
|
- $unitPrice = $ret['price'] ?? 0;
|
|
|
- if ($unitPrice <= 0) {
|
|
|
- util::fail('花束没有价格,不能下单');
|
|
|
+ if ($seckillRow) {
|
|
|
+ // 通过校验后立即预占名额,失败/异常时随事务一起回滚(见 register_shutdown_function)
|
|
|
+ HomePageModuleClass::reserveSeckillPurchase($mainId, $saleGoodsId, $hdCustomId, $goodsNum);
|
|
|
}
|
|
|
$currentTotal = bcmul($unitPrice, $goodsNum, 2);
|
|
|
$modifyPrice = bcadd($modifyPrice, $currentTotal, 2);
|
|
|
@@ -1305,6 +1387,7 @@ class OrderController extends BaseController
|
|
|
$transaction->commit();
|
|
|
$transactionFinished = true;
|
|
|
\bizHd\product\classes\ProductClass::clearLimitBuyRollbackSnapshot();
|
|
|
+ HomePageModuleClass::clearSeckillReservationSnapshot();
|
|
|
|
|
|
$orderSn = $return->orderSn ?? '';
|
|
|
$orderPrice = $return->orderPrice ?? ($return->actPrice ?? 0);
|
|
|
@@ -1320,6 +1403,7 @@ class OrderController extends BaseController
|
|
|
} catch (\Exception $e) {
|
|
|
$transaction->rollBack();
|
|
|
\bizHd\product\classes\ProductClass::rollbackLimitBuySnapshot();
|
|
|
+ HomePageModuleClass::rollbackSeckillReservationSnapshot();
|
|
|
$transactionFinished = true;
|
|
|
Yii::error("合并下单失败:" . $e->getMessage());
|
|
|
util::fail('下单失败');
|