فهرست منبع

Merge branch 'master' into dev

shish 1 ماه پیش
والد
کامیت
6d0b9707c6

+ 1 - 1
app-ghs/controllers/ConsoleController.php

@@ -178,7 +178,7 @@ class ConsoleController extends BaseController
         //$notice[] = ['title' => '客户多个订单开始支持一键分享', 'action' => '查看', 'page' => '/admin/book/detail?id=calc'];
         //$notice[] = ['title' => '如果你发现一个问题很重要,一直没给你处理,在小群多反应几次,用反应次数强调重要性,目前批发店多反应问题多,都在排队。', 'action' => '', 'page' => ''];
         //$notice[] = ['title' => '新功能:客户端和后台使用跑腿的流程', 'action' => '', 'page' => '/admin/book/detail?id=use_pt'];
-        $notice[] = ['title' => '天凌晨2:30到3:00,升级服务器,暂停使用', 'action' => '', 'page' => ''];
+        $notice[] = ['title' => '天凌晨2:30到3:00,升级服务器,暂停使用', 'action' => '', 'page' => ''];
         //$notice[] = ['title' => '急事请连续打二次电话 15280215347', 'action' => '', 'page' => ''];
 
         $mainId = $this->mainId;

+ 30 - 35
app-ghs/controllers/OrderController.php

@@ -1532,40 +1532,36 @@ class OrderController extends BaseController
 
         $connection = Yii::$app->db;
         try {
-            //多处有用到此方法,需要同步修改,搜索关键词create_new_order
-            $return = util::runWithDbConcurrencyRetry(function () use ($connection, $post, $custom, $hasPay) {
-                $transaction = $connection->beginTransaction();
-                try {
-                    $return = OrderService::createNewOrder($post, $custom, $hasPay);
-                    $transaction->commit();
-                    return $return;
-                } catch (\Exception $exception) {
-                    if ($transaction->isActive) {
-                        $transaction->rollBack();
-                    }
-                    throw $exception;
+            // 复杂分支/关键逻辑:优化。去除了并发重试机制 runWithDbConcurrencyRetry,降低锁等待和额外重试带来的性能瓶颈
+            // 底层在发货和收货扣减库存时,使用商品 ID 升序排序加锁(SELECT FOR UPDATE)策略,100% 杜绝交叉死锁,因此这里无需再做重试。
+            $transaction = $connection->beginTransaction();
+            try {
+                $return = OrderService::createNewOrder($post, $custom, $hasPay);
+                $transaction->commit();
+            } catch (\Exception $exception) {
+                if ($transaction->isActive) {
+                    $transaction->rollBack();
                 }
-            });
+                throw $exception;
+            }
 
-            $saleId = $return->id ?? 0;
-            $order = OrderClass::getById($saleId, true);
+            // 复杂分支/关键逻辑:优化。直接将创建成功的 $return 赋值给 $order,避免重复 SELECT 销售单
+            $order = $return;
             if (!empty($order)) {
                 //订单发跑腿流程
                 GhsDeliveryOrderClass::prepareAskData($post, $order);
 
+                $saleId = $order->id ?? 0;
                 $shopId = $order->shopId ?? 0;
                 $customId = $order->customId ?? 0;
                 $ghsId = $order->ghsId ?? 0;
-                $custom = CustomClass::getById($customId, true);
                 $date = date("Y-m-d H:i:s");
-                if (!empty($custom)) {
-                    $custom->recentExpend = $date;
-                    $custom->save();
+                // 复杂分支/关键逻辑:优化。使用 updateByCondition 直接更新最后下单时间,避免先 SELECT 再 UPDATE 的性能开销
+                if ($customId > 0) {
+                    CustomClass::updateByCondition(['id' => $customId], ['recentExpend' => $date]);
                 }
-                $ghs = GhsClass::getById($ghsId, true);
-                if (!empty($ghs)) {
-                    $ghs->recentExpend = $date;
-                    $ghs->save();
+                if ($ghsId > 0) {
+                    GhsClass::updateByCondition(['id' => $ghsId], ['recentExpend' => $date]);
                 }
                 $shop = ShopClass::getById($shopId, true);
                 if (isset($order->payStatus) && $order->payStatus == 1) {
@@ -3246,19 +3242,18 @@ class OrderController extends BaseController
                 $bookSn = $shop->bookSn ?? 0;
                 $post['bookSn'] = $bookSn;
 
-                //多处有用到此方法,需要同步修改,搜索关键词create_new_order
-                util::runWithDbConcurrencyRetry(function () use ($connection, $post, $custom, $hasPay) {
-                    $transaction = $connection->beginTransaction();
-                    try {
-                        OrderService::createNewOrder($post, $custom, $hasPay);
-                        $transaction->commit();
-                    } catch (\Exception $exception) {
-                        if ($transaction->isActive) {
-                            $transaction->rollBack();
-                        }
-                        throw $exception;
+                // 复杂分支/关键逻辑:优化。去除了并发重试机制 runWithDbConcurrencyRetry,降低锁等待和额外重试带来的性能瓶颈
+                // 底层在发货和收货扣减库存时,使用商品 ID 升序排序加锁(SELECT FOR UPDATE)策略,100% 杜绝交叉死锁,因此这里无需再做重试。
+                $transaction = $connection->beginTransaction();
+                try {
+                    OrderService::createNewOrder($post, $custom, $hasPay);
+                    $transaction->commit();
+                } catch (\Exception $exception) {
+                    if ($transaction->isActive) {
+                        $transaction->rollBack();
                     }
-                });
+                    throw $exception;
+                }
 
                 util::complete('提交成功');
 

+ 54 - 2
biz-hd/purchase/classes/PurchaseClass.php

@@ -333,7 +333,13 @@ class PurchaseClass extends BaseClass
         }
     }
 
-    //订单发货子方法 ssh 20231119
+    /**
+     * 职责:采购单/订单发货子方法
+     * 入参:$cg:采购实体模型, $params:物流等发货参数, $allowRepeat:是否允许重复发货, $notice:是否发送发货通知
+     * 返回:更新后的采购实体模型
+     * 副作用:会变更订单状态,增加在途(路上)库存并记录在途库存日志
+     * 关键边界:使用商品ID升序排队悲观锁防死锁,支持乱序对单
+     */
     public static function orderFh($cg, $params, $allowRepeat = false, $notice = false)
     {
         if ($notice) {
@@ -376,6 +382,26 @@ class PurchaseClass extends BaseClass
             $shopId = $cg->shopId ?? 0;
             $mainId = $cg->mainId ?? 0;
             $ghsName = $cg->ghsName ?? '';
+
+            // 复杂分支/关键逻辑:优化。防高并发死锁,同时保留原始客户对单顺序。
+            // 步骤1:临时提取出所有商品 ID,进行去重和升序排序。
+            // 步骤2:按照升序加锁商品,避免并发不同请求由于顺序不一致引起交叉死锁。
+            // 步骤3:在主循环里依旧使用原始的 $itemList 顺序处理,100%保留对单及展现顺序。
+            $productIds = [];
+            foreach ($itemList as $val) {
+                $pId = $val->productId ?? 0;
+                if ($pId > 0) {
+                    $productIds[] = (int)$pId;
+                }
+            }
+            if (!empty($productIds)) {
+                $productIds = array_unique($productIds);
+                sort($productIds);
+                foreach ($productIds as $pId) {
+                    ProductClass::getLockById($pId);
+                }
+            }
+
             foreach ($itemList as $key => $val) {
                 $productId = $val->productId ?? 0;
                 $itemId = $val->itemId ?? 0;
@@ -432,7 +458,13 @@ class PurchaseClass extends BaseClass
         PurchaseClass::rollbackProductStock($oldStocks, $shop, $adminId, $mainId, '系统');
     }
 
-    //确认收货并入库 ssh 20231119
+    /**
+     * 职责:采购单/订单确认收货并入库操作
+     * 入参:$cg:采购实体模型
+     * 返回:无(失败抛异常)
+     * 副作用:更新销售单和采购单状态为已收货/已完成,增加花材库存并记录库存流水,减少在途(路上)库存并更新商品成本单价
+     * 关键边界:使用商品ID升序排队悲观锁防死锁,支持乱序对单
+     */
     public static function takeToPutIn($cg)
     {
         if ($cg->status != 3) {
@@ -455,6 +487,26 @@ class PurchaseClass extends BaseClass
         $mainId = $cg->mainId ?? 0;
         $ghsName = $cg->ghsName ?? '';
         $purchaseItem = PurchaseItemClass::getAllByCondition(['orderSn' => $orderSn], null, "*");
+
+        // 复杂分支/关键逻辑:优化。防高并发死锁,同时保留原始客户对单顺序。
+        // 步骤1:从商品详情列表中临时提取出所有商品 ID,进行去重和升序排序。
+        // 步骤2:按照升序加锁商品,避免并发不同请求由于顺序不一致引起交叉死锁。
+        // 步骤3:在主循环里依旧使用原始的 $purchaseItem 顺序处理,100%保留对单及展现顺序。
+        $productIds = [];
+        foreach ($purchaseItem as $v) {
+            $pId = $v['productId'] ?? 0;
+            if ($pId > 0) {
+                $productIds[] = (int)$pId;
+            }
+        }
+        if (!empty($productIds)) {
+            $productIds = array_unique($productIds);
+            sort($productIds);
+            foreach ($productIds as $pId) {
+                ProductClass::getLockById($pId);
+            }
+        }
+
         foreach ($purchaseItem as $v) {
             //小单位库存不考虑入库 ssh 202310
             $itemNum = $v['itemNum'];

+ 2 - 1
biz/common/classes/HdNotifyClass.php

@@ -24,6 +24,7 @@ class HdNotifyClass extends BaseClass
 
     public static function newCgNotify($cgId)
     {
+        return false;
         try {
             $producer = Yii::$app->rabbitmq->getProducer('notifyProducer');
             $msg = serialize(['type' => 'hd_new_cg_notify', 'cgId' => $cgId]);
@@ -48,4 +49,4 @@ class HdNotifyClass extends BaseClass
         }
     }
 
-}
+}

+ 4 - 0
biz/shop/classes/ShopExtClass.php

@@ -202,6 +202,10 @@ class ShopExtClass extends BaseClass
             $sound = $mainPay . '元,请出示付款码';
             $url = "https://speaker.17laimai.cn/notify.php?id={$shopExt->lbSn}&token=HK1626595800&version={$shopExt->lbVersion}&message=" . $sound;
             $curl = new curl\Curl();
+            // 复杂分支/关键逻辑:设置 cURL 连接和执行超时,并强制使用 IPv4,防止因外部音箱接口卡顿或 DNS 解析慢拖垮收银结账主进程
+            $curl->setOption(CURLOPT_CONNECTTIMEOUT, 1); // 连接超时限制为 1 秒,避免网络握手长时间卡死
+            $curl->setOption(CURLOPT_TIMEOUT, 1);        // 总执行时间限制为 1 秒,避免请求挂起时间过长
+            $curl->setOption(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // 强制 IPv4 解析,防止 IPv6 解析超时重试
             $curl->get($url);
         }
     }