Selaa lähdekoodia

Merge branch 'master' into dev

shish 2 kuukautta sitten
vanhempi
commit
9c98f23bf8

+ 11 - 3
app-ghs/controllers/AuthController.php

@@ -518,7 +518,13 @@ class AuthController extends PublicController
         util::success(['token' => $token, 'account' => $sjId, 'shopId' => $shopId]);
     }
 
-    //静默获取小程序用户信息
+    /**
+     * 静默获取小程序用户信息
+     * 职责:通过微信小程序 code 换取 openid 和 session_key 并自动登录供货商端
+     * 入参:GET 传参 code (微信临时登录凭证)
+     * 返回:登录成功返回 token、用户信息、店铺 ID 等;失败返回空 token
+     * 副作用:会更新管理员的最后登录时间,并将 session_key 写入 Redis 缓存
+     */
     public function actionMiniInfo()
     {
         //供货商平台
@@ -534,6 +540,10 @@ class AuthController extends PublicController
         $url = "https://api.weixin.qq.com/sns/jscode2session?appid={$appId}&secret={$appSecret}&js_code={$code}&grant_type=authorization_code";
         $curl = new curl\Curl();
         $curl->setOption(CURLOPT_SSL_VERIFYPEER, false);
+        // 复杂分支/关键逻辑:设置 cURL 连接和执行超时,并强制使用 IPv4,防止因微信接口卡顿或 DNS 解析慢拖垮 PHP-FPM 进程
+        $curl->setOption(CURLOPT_CONNECTTIMEOUT, 2); // 连接超时限制为 2 秒,避免网络握手长时间卡死
+        $curl->setOption(CURLOPT_TIMEOUT, 3);        // 总执行时间限制为 3 秒,避免请求挂起时间过长
+        $curl->setOption(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // 强制 IPv4 解析,防止 IPv6 解析超时重试
         $result = $curl->get($url);
         $arr = Json::decode($result);
         $sessionKey = isset($arr['session_key']) ? $arr['session_key'] : '';
@@ -580,8 +590,6 @@ class AuthController extends PublicController
             //没有管理店铺不允许登录
             util::success(['token' => '', 'currentMiniOpenId' => $openid, 'number' => 5]);
         }
-        $shopAdmin->lastLogin = date("Y-m-d H:i:s");
-        $shopAdmin->save();
         $shopAdminId = $shopAdmin->id ?? 0;
         //是否有切换门店的权限
         $switchShop = \biz\shop\classes\ShopAdminClass::hasSwitchShopRight($shopAdmin);

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

@@ -318,7 +318,7 @@ class ConsoleController extends BaseController
 
         $warning = '';
         $warningUrl = '';
-        $warning = '本页右上方,暂不显示昨天收入金额';
+        //$warning = '本页右上方,暂不显示昨天收入金额';
         //$warningUrl = '/admin/notice/warning';
 
         util::success([

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

@@ -735,7 +735,17 @@ class OrderController extends BaseController
         }
     }
 
-    //微信和支付宝付款码支付 ssh 2021.4.11
+    /**
+     * 微信和支付宝付款码支付
+     * 职责:处理商户扫描客户付款码(被扫支付)的请求,调用拉卡拉支付接口并更新本地订单状态
+     * 入参:GET 传参 id (订单ID), authCode (付款码)
+     * 返回:JSON 格式的支付结果(SUCCESS/FAILURE)
+     * 副作用:更新订单及采购单的支付状态、支付方式,记录支付日志,触发云打印、跑腿呼叫及语音播报
+     * 关键边界:
+     *   1. 验证订单及采购单的有效性与时效性
+     *   2. 校验付款码格式(微信/支付宝)
+     *   3. 优化:将拉卡拉外部网络请求移出数据库事务,避免因网络延迟长时间占用数据库连接和行锁
+     */
     public function actionCodePay()
     {
         ini_set('date.timezone', 'Asia/Shanghai');
@@ -817,8 +827,6 @@ class OrderController extends BaseController
         }
         $totalFee = $cg->actPrice ?? 0;
 
-        $connection = Yii::$app->db;
-        $transaction = $connection->beginTransaction();
         try {
             $subject = '购买花材 ' . $orderSn;
             $capitalType = dict::getDict('capitalType', 'xhPurchase', 'id');
@@ -842,39 +850,49 @@ class OrderController extends BaseController
                 'subject' => $subject,
                 'authCode' => $authCode,
             ];
+
+            // 复杂分支/关键逻辑:在调用外部支付接口前不开启事务,防止网络延迟卡死数据库连接
             $response = $laResource->scanPay($scanParams);
             if (isset($response['code']) && $response['code'] == 'BBS00000') {
 
-                $account_type = $response['resp_data']['account_type'] ?? '';
-                $payWayType = dict::getDict('payWay', 'wxPay');
-                if ($account_type == 'WECHAT') {
+                // 复杂分支/关键逻辑:支付成功后,开启数据库事务,确保本地订单状态更新的原子性
+                $connection = Yii::$app->db;
+                $transaction = $connection->beginTransaction();
+                try {
+                    $account_type = $response['resp_data']['account_type'] ?? '';
                     $payWayType = dict::getDict('payWay', 'wxPay');
-                }
-                if ($account_type == 'ALIPAY') {
-                    $payWayType = dict::getDict('payWay', 'alipay');
-                }
-                $order->payWay = $payWayType;
-                $order->save();
-                $cg->payWay = $payWayType;
-                $cg->save();
+                    if ($account_type == 'WECHAT') {
+                        $payWayType = dict::getDict('payWay', 'wxPay');
+                    }
+                    if ($account_type == 'ALIPAY') {
+                        $payWayType = dict::getDict('payWay', 'alipay');
+                    }
+                    $order->payWay = $payWayType;
+                    $order->save();
+                    $cg->payWay = $payWayType;
+                    $cg->save();
 
-                $transactionId = $response['resp_data']['trade_no'] ?? '';
-                $openId = '';
-                $aliUserId = '';
+                    $transactionId = $response['resp_data']['trade_no'] ?? '';
+                    $openId = '';
+                    $aliUserId = '';
 
-                $cg->onlinePay = dict::getDict('onlinePay', 'yes');
-                $cg->codePay = 1;
-                $cg->payOpenId = $openId;
-                $cg->aliUserId = $aliUserId;
-                $cg->save();
+                    $cg->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $cg->codePay = 1;
+                    $cg->payOpenId = $openId;
+                    $cg->aliUserId = $aliUserId;
+                    $cg->save();
 
-                $order->onlinePay = dict::getDict('onlinePay', 'yes');
-                $order->codePay = 1;
-                $order->save();
+                    $order->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $order->codePay = 1;
+                    $order->save();
 
-                $attach = '';
-                payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
-                $transaction->commit();
+                    $attach = '';
+                    payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
+                    $transaction->commit();
+                } catch (\Exception $e) {
+                    $transaction->rollBack();
+                    throw $e;
+                }
 
                 $saleId = $order->id ?? 0;
                 $order = OrderClass::getById($saleId, true);
@@ -922,7 +940,6 @@ class OrderController extends BaseController
                 util::success(['returnStatus' => 'FAILURE', 'returnCode' => $retCode], $msg);
             }
         } catch (\Exception $e) {
-            $transaction->rollBack();
             Yii::error("支付失败原因:" . $e->getMessage());
             util::fail('支付失败');
         }
@@ -1397,7 +1414,7 @@ class OrderController extends BaseController
         //判断花材有效性
         foreach ($itemInfo as $itemData) {
             if (!isset($itemData['mainId']) || $itemData['mainId'] != $this->mainId) {
-                util::fail('只能选择同一家的商品哈');
+                util::fail('只能选一家的商品哈,请清空重选');
             }
         }
 

+ 35 - 17
app-hd/controllers/OrderController.php

@@ -422,7 +422,17 @@ class OrderController extends BaseController
         }
     }
 
-    //微信和支付宝付款码支付 ssh 2021.4.11
+    /**
+     * 微信和支付宝付款码支付
+     * 职责:处理商户扫描客户付款码(被扫支付)的请求,调用拉卡拉支付接口并更新本地订单状态(零售端)
+     * 入参:GET 传参 id (订单ID), authCode (付款码), version (版本号)
+     * 返回:JSON 格式的支付结果(SUCCESS/FAILURE)
+     * 副作用:更新订单支付状态、支付方式,记录支付日志,触发云打印、跑腿呼叫及语音播报
+     * 关键边界:
+     *   1. 验证订单有效性与待付款状态
+     *   2. 校验付款码格式(微信/支付宝)
+     *   3. 优化:将拉卡拉外部网络请求移出数据库事务,避免因网络延迟长时间占用数据库连接和行锁
+     */
     public function actionCodePay()
     {
         ini_set('date.timezone', 'Asia/Shanghai');
@@ -491,8 +501,6 @@ class OrderController extends BaseController
         $totalFee = $order->mainPay ?? 0;
         $capitalType = dict::getDict('capitalType', 'xhOrder', 'id');
 
-        $connection = Yii::$app->db;
-        $transaction = $connection->beginTransaction();
         try {
 
             $merchantPrivateKeyPath = Yii::getAlias("@vendor/lakala") . '/production/api_private_key.pem';
@@ -515,25 +523,35 @@ class OrderController extends BaseController
                 'subject' => $subject,
                 'authCode' => $authCode,
             ];
+
+            // 复杂分支/关键逻辑:在调用外部支付接口前不开启事务,防止网络延迟卡死数据库连接
             $response = $laResource->scanPay($scanParams);
 
             if (isset($response['code']) && $response['code'] == 'BBS00000') {
 
-                $account_type = $response['resp_data']['account_type'] ?? '';
-                $payWayType = dict::getDict('payWay', 'wxPay');
-                if ($account_type == 'WECHAT') {
+                // 复杂分支/关键逻辑:支付成功后,开启数据库事务,确保本地订单状态更新的原子性
+                $connection = Yii::$app->db;
+                $transaction = $connection->beginTransaction();
+                try {
+                    $account_type = $response['resp_data']['account_type'] ?? '';
                     $payWayType = dict::getDict('payWay', 'wxPay');
+                    if ($account_type == 'WECHAT') {
+                        $payWayType = dict::getDict('payWay', 'wxPay');
+                    }
+                    if ($account_type == 'ALIPAY') {
+                        $payWayType = dict::getDict('payWay', 'alipay');
+                    }
+                    $order->payWay = $payWayType;
+                    $transactionId = $response['resp_data']['trade_no'] ?? '';
+                    $order->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $order->save();
+                    $attach = '';
+                    payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
+                    $transaction->commit();
+                } catch (\Exception $e) {
+                    $transaction->rollBack();
+                    throw $e;
                 }
-                if ($account_type == 'ALIPAY') {
-                    $payWayType = dict::getDict('payWay', 'alipay');
-                }
-                $order->payWay = $payWayType;
-                $transactionId = $response['resp_data']['trade_no'] ?? '';
-                $order->onlinePay = dict::getDict('onlinePay', 'yes');
-                $order->save();
-                $attach = '';
-                payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
-                $transaction->commit();
 
                 //解决重复通知
                 $cacheKey = 'hd_shop_order_pay_' . $orderSn;
@@ -578,7 +596,7 @@ class OrderController extends BaseController
             }
 
         } catch (\Exception $e) {
-            $transaction->rollBack();
+            Yii::error("零售端支付失败原因:" . $e->getMessage());
             util::fail('支付失败');
         }
     }

+ 2 - 2
app-hd/controllers/PurchaseController.php

@@ -181,7 +181,7 @@ class PurchaseController extends BaseController
             if (!empty($productInfoList)) {
                 foreach ($productInfoList as $currentInfo) {
                     if (isset($currentInfo['mainId']) == false || $currentInfo['mainId'] != $this->mainId) {
-                        util::fail('只能选择同一家的商品哦');
+                        util::fail('只能选一家的商品,请清空重选。');
                     }
                 }
                 if (count($productInfoList) != count($ids)) {
@@ -386,7 +386,7 @@ class PurchaseController extends BaseController
                 $nowTime = strtotime(date("Y-m-d"));
                 foreach ($productInfoList as $currentInfo) {
                     if (!isset($currentInfo['mainId']) || $currentInfo['mainId'] != $ghsMainId) {
-                        util::fail('只能选择同一家的商品哦');
+                        util::fail('只能选一家的商品,请清空重新选。');
                     }
                     $currentName = $currentInfo['name'] ?? '';
                     if (isset($currentInfo['frontHide']) && $currentInfo['frontHide'] == 1) {

+ 0 - 1
app-hd/controllers/WorkController.php

@@ -77,7 +77,6 @@ class WorkController extends BaseController
     //制作单列表 ssh 20220319
     public function actionList()
     {
-        util::fail('请更新应用');
         $get = Yii::$app->request->get();
         $where = [];
         $where['mainId'] = $this->mainId;

+ 11 - 1
app-mall/controllers/AuthController.php

@@ -127,7 +127,13 @@ class AuthController extends PublicController
         $this->redirect($url);
     }
 
-    //静默获取小程序用户信息
+    /**
+     * 静默获取小程序用户信息
+     * 职责:通过微信小程序 code 换取 openid 和 session_key 并自动登录商城端
+     * 入参:GET 传参 code (微信临时登录凭证)
+     * 返回:登录成功返回 token、用户信息等;失败返回错误提示
+     * 副作用:会将 session_key 写入 Redis 缓存
+     */
     public function actionMiniInfo()
     {
         $code = Yii::$app->request->get('code', '');
@@ -143,6 +149,10 @@ class AuthController extends PublicController
         $url = "https://api.weixin.qq.com/sns/jscode2session?appid={$appId}&secret={$appSecret}&js_code={$code}&grant_type=authorization_code";
         $curl = new curl\Curl();
         $curl->setOption(CURLOPT_SSL_VERIFYPEER, false);
+        // 复杂分支/关键逻辑:设置 cURL 连接和执行超时,并强制使用 IPv4,防止因微信接口卡顿或 DNS 解析慢拖垮 PHP-FPM 进程
+        $curl->setOption(CURLOPT_CONNECTTIMEOUT, 2); // 连接超时限制为 2 秒,避免网络握手长时间卡死
+        $curl->setOption(CURLOPT_TIMEOUT, 3);        // 总执行时间限制为 3 秒,避免请求挂起时间过长
+        $curl->setOption(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // 强制 IPv4 解析,防止 IPv6 解析超时重试
         $result = $curl->get($url);
         $arr = Json::decode($result);
         $sessionKey = isset($arr['session_key']) ? $arr['session_key'] : '';

+ 2 - 2
app-mall/controllers/OrderController.php

@@ -261,7 +261,7 @@ class OrderController extends BaseController
             $nowTime = strtotime(date("Y-m-d"));
             foreach ($productInfoList as $currentInfo) {
                 if (!isset($currentInfo['mainId']) || $currentInfo['mainId'] != $this->mainId) {
-                    util::fail('只能选择同一家的商品哦');
+                    util::fail('只能选一家的商品,请清空重新选呢');
                 }
                 $presell = $currentInfo['presell'] ?? 0;
                 $presellData[] = $presell;
@@ -1319,4 +1319,4 @@ class OrderController extends BaseController
         util::success($detail);
     }
 
-}
+}

+ 1 - 1
biz-ghs/product/classes/ProductClass.php

@@ -1343,7 +1343,7 @@ class ProductClass extends BaseClass
                     $name = $product['name'] ?? '';
                     $pMainId = $product['mainId'] ?? 0;
                     //noticeUtil::push("只能选择同一家的商品哦!{$name} {$pMainId} {$mainId}", '15280215347');
-                    util::fail($name . ' 不是家的商品,请清空重选');
+                    util::fail($name . ' 不是家的商品,请清空重选');
                 }
             }
             if (count($productList) != count($ids)) {

+ 28 - 2
biz/shop/classes/ShopExtClass.php

@@ -206,7 +206,16 @@ class ShopExtClass extends BaseClass
         }
     }
 
-    //零售花店收款声音播放 ssh 20220423
+    /**
+     * 零售花店收款声音播放
+     * 职责:向外部音箱接口发送语音播报请求,提示商户收款成功(零售端)
+     * 入参:ActiveRecord $order (订单模型实例)
+     * 返回:bool (是否成功触发播报)
+     * 副作用:发送外部 HTTP 网络请求
+     * 关键边界:
+     *   1. 仅限门店订单、在线支付且已付款的订单播报
+     *   2. 优化:设置极短的 cURL 连接超时与执行超时限制,并强制使用 IPv4 解析,防止因外部音箱接口延迟拖垮收银支付主流程
+     */
     public static function hdGatheringReport($order)
     {
         if ($order->fromType != dict::getDict("fromType", "shop")) {
@@ -229,6 +238,10 @@ class ShopExtClass extends BaseClass
             $sound = $payWay == dict::getDict('payWay', 'wxPay') ? "微信收款{$actPrice}元,谢谢惠顾,欢迎下次光临" : "支付宝收款{$actPrice}元,谢谢惠顾,欢迎下次光临";
             $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);
         }
     }
@@ -275,7 +288,16 @@ class ShopExtClass extends BaseClass
         }
     }
 
-    //供货商收款声音播放 ssh 20230421
+    /**
+     * 供货商收款声音播放
+     * 职责:向外部音箱接口发送语音播报请求,提示商户收款成功
+     * 入参:ActiveRecord $order (订单模型实例)
+     * 返回:bool (是否成功触发播报)
+     * 副作用:发送外部 HTTP 网络请求
+     * 关键边界:
+     *   1. 仅限门店订单、在线支付且已付款的订单播报
+     *   2. 优化:设置极短的 cURL 连接超时与执行超时限制,并强制使用 IPv4 解析,防止因外部音箱接口延迟拖垮收银支付主流程
+     */
     public static function ghsGatheringReport($order)
     {
         if ($order->fromType != dict::getDict("fromType", "shop")) {
@@ -298,6 +320,10 @@ class ShopExtClass extends BaseClass
             $sound = $payWay == dict::getDict('payWay', 'wxPay') ? "微信收款{$actPrice}元,谢谢惠顾,欢迎下次光临" : "支付宝收款{$actPrice}元,谢谢惠顾,欢迎下次光临";
             $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);
         }
     }